1. 程式人生 > >android中自定義下拉框

android中自定義下拉框

android自帶的下拉框好用不?我覺得有時候好用,有時候難有,專案規定這樣的效果,自帶的控制元件實現不了,那麼只有我們自己來老老實實滴寫一個新的了,其實最基本的下拉框就像一些資料填寫時,點選的時候出現在編輯框的下面,然後又很多選項的下拉框,可是我在網上找了一下,沒有這種下拉框額,就自己寫了一個,看效果圖先:

,這個是資料填寫的一部分介面,三個下拉框,選擇故鄉所在地;

點選之後彈出下拉框,選擇下面的選項;

三個下拉框時關聯的,第一個決定了第二資料內容,第二個決定了第三個資料內容,如果三個全部選好之後,再次點選第一個,那麼第二個、第三個都會清空,點選第二個則第三個會清空。

要實現它,也就是一個PopupWindow時主要的介面,下面來看看程式碼:

建立一個DefineSpinnerView.java檔案,繼承至View,然後給出如下屬性:

    /**
     * 用於彈出的下拉框
     */
    private PopupWindow pWindow = null;

    // **************************************************************************
    // 這些是用來當點選一個時,根據他們之間的關係來顯示下拉框中的內容
    // **************************************************************************
    /**
     * 祖父
     */
    private DefineSpinnerView gradeParent = null;
    /**
     * 父控制元件
     */
    private DefineSpinnerView parents = null;
    /**
     * 子控制元件
     */
    private DefineSpinnerView child1 = null;
    /**
     * 孫子控制元件
     */
    private DefineSpinnerView child2 = null;

    private Context context = null;
    private OptionsAdapter adapter = null; // 下拉框介面卡
    private List<String> datas = null; // 下拉框資料
    private RelativeLayout layout = null; // 父控制元件
    private TextView text = null; // 文字顯示
    private ImageView image = null; // 下拉箭頭
    private int p_width = -1; // 下拉框寬度
    private ListView list = null; // 下拉表

在建構函式中,構造出一個TextView和一個ImageView控制元件,並將它們都新增到layout中,程式碼如下:

TextListener lis = new TextListener();
        text = new TextView(context);
        text.setBackgroundResource(R.drawable.edit_normal);
        text.setTextColor(getResources().getColor(R.color.spinner_text));
        text.setGravity(Gravity.CENTER);
        text.setOnClickListener(lis);
        LayoutParams params1 = new LayoutParams(width, hight);
        params1.leftMargin = left;
        params1.topMargin = top;

        image = new ImageView(context);
        image.setBackgroundResource(R.drawable.gerendang_jiantou);
        image.setOnClickListener(lis);
        if (LoginAct.MACHINE_PIXELS == IFinalConstant.XHDPI_RESOLUTION) {
            text.setTextSize(20.0f);
            LayoutParams params2 = new LayoutParams(19, 17);
            params2.topMargin = top + 15;
            params2.leftMargin = left + width - 28;
            map.put(image, params2);
        } else {
            text.setTextSize(15.0f);
            LayoutParams params2 = new LayoutParams(8, 8);
            params2.topMargin = top + 13;
            params2.leftMargin = left + width - 16;
            map.put(image, params2);
        }

        map.put(text, params1);

裡面涉及到一個TextListener內部類,是我們自己定義的一個類,它繼承至OnClickListener介面

    /**
     * @author ZYJ
     *         當點選Text時,根據上一級的內容來設定下一級的內容
     */
    class TextListener implements OnClickListener {
        public void onClick(View v) {
        	hideSoft ();
            if (gradeParent != null && parents != null) {
                DefineSpinnerView.this.setDatas(DefineSpinnerView.this
                        .getGuxiang3(gradeParent.getText(), parents.getText()));
            }
            if (gradeParent == null && parents != null) {
                DefineSpinnerView.this.setDatas(DefineSpinnerView.this
                        .getGuxiang2(parents.getText()));
            }
            cleanText();
            changPopState(text);
        }
    

這個裡面呼叫了一個方法changPopState,它的定義如下:

    /**
     * 顯示或者隱藏下拉框
     *
     * @param v
     */
    private void changPopState(View v) {
        if (pWindow == null) {
            popWindow(v);
            return;
        }
        if (!pWindow.isShowing()) {
            popWindow(v);
        } else {
            if (pWindow != null) {
                pWindow.dismiss();
            }
        }
    }

這個裡面又呼叫了一個popWindow方法,定義如下:

/**
     * 初始化下拉框
     *
     * @param par 父控制元件
     */
    private void popWindow(final View par) {
        if (pWindow == null) {

            // 佈局檔案
            View v = LayoutInflater.from(context).inflate(R.layout.list, null);
            list = (ListView) v.findViewById(R.id.list);
            list.setOnItemClickListener(new OnItemClickListener() {
                public void onItemClick(AdapterView<?> arg0, View arg1,
                                        int arg2, long arg3) {
                    // R.String.butian代表的是“不填”
                    if (datas.get(arg2).toString().equals(context.getString(R.string.butian))) {  
                        text.setText("");
                    } else {
                        text.setText(datas.get(arg2).toString()); // 將當前點選的item中的字串顯示出來
                    }
                    if (pWindow != null) { // 關閉下拉框
                        changPopState(par);
                    }
                }
            });
            adapter = new OptionsAdapter(context, datas); // 根據資料,設定下拉框顯示
            list.setAdapter(adapter);
            list.setDivider(null); // 遮蔽下拉框每個item之間的線條
            /**
             * 兩種不同長度的下拉框,主要是為了適應螢幕的大小
             */
            if (p_width > 0) {
                pWindow = new PopupWindow(v, par.getWidth(), 150);
            } else {
                pWindow = new PopupWindow(v, par.getWidth(), 300);
            }
            pWindow.setFocusable(true);
            pWindow.setBackgroundDrawable(new BitmapDrawable());
            pWindow.setOutsideTouchable(true);
            pWindow.update();
        }
        pWindow.showAsDropDown(text);
    }

然後是一些細節了,提供一個TextView設定上面文字和得到上面文字的方法,設定下拉框資料的方法setDatas,如下:

public void setText(String str) {
        if (text != null) {
            text.setText(str);
        }
    }

    public void setDatas(List<String> datas) {
        this.datas = datas;
        if (adapter != null) {
            adapter.setDatas(datas);
            adapter.notifyDataSetInvalidated();
        }
    }

    public String getText() {
        if (text != null) {
            return text.getText().toString();
        }
        LoginAct.LogW("spinner's textView is null");
        return "";
    }

    private void cleanText() {
        if (child1 != null) {
            child1.text.setText("");
        }
        if (child2 != null) {
            child2.text.setText("");
        }
    }

然後新增幾個關聯控制元件的get方法:

 public void setChild1(DefineSpinnerView child1) {
        this.child1 = child1;
    }

    public void setChild2(DefineSpinnerView child2) {
        this.child2 = child2;
    }

    public void setGradeParent(DefineSpinnerView gradeParent) {
        this.gradeParent = gradeParent;
    }

    public void setParents(DefineSpinnerView parents) {
        this.parents = parents;
    }

    public void setP_width(int p_width) {
        this.p_width = p_width;
    }

接下來提供一個設定子控制元件和運算元控制元件資料的方法:

/**
     * @param s1 父控制元件中的字串
     * @param s2 子控制元件中的字串
     * @return 返回一個List<String>集合
     * @功能 通過父控制元件的字串來設定子控制元件中的內容
     */
    private List<String> getGuxiang3(String s1, String s2) {
        List<String> dd = new ArrayList<String>();
        dd.add(context.getString(R.string.butian));
        Map<String, ArrayList<String>> mapTmp1 = MaterialView.cityMap.get(s1);
        if (mapTmp1 != null) {
            List<String> list = mapTmp1.get(s2);
            if (list != null) {
                for (String str : list) {
                    dd.add(str);
                }
            }
        }
        return dd;
    }

    /**
     * @param s 字串
     * @return
     * @author ZYJ
     * @功能 設定父親輩的下拉框中的內容
     */
    private List<String> getGuxiang2(String s) {
        List<String> dd = new ArrayList<String>();
        dd.add(context.getString(R.string.butian));
        Map<String, ArrayList<String>> mapTmp = MaterialView.cityMap.get(s);
        if (mapTmp != null) {
            for (String str : mapTmp.keySet()) {
                dd.add(str);
            }
        }
        return dd;
    }

最後提供一個隱藏軟鍵盤的方法:

    private void hideSoft() {
		InputMethodManager imm = (InputMethodManager) context
				.getSystemService(Context.INPUT_METHOD_SERVICE);
		imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT,
				InputMethodManager.HIDE_NOT_ALWAYS);
	}

到這裡,自定義控制元件的程式碼基本上寫完了;我們還要來看看下拉框中的xml佈局和介面卡的寫法:

xml檔案:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="horizontal">

    <TextView
            android:id="@+id/info"
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:textSize="15sp"
            android:textColor="@color/spinner_text"
            android:layout_gravity="center"
            android:gravity="center"/>

</LinearLayout>
然後是介面卡類(OptionsAdapter),看全部程式碼吧:
public class OptionsAdapter extends BaseAdapter {

    private Context context = null;
    private List<String> datas = null;

    public OptionsAdapter(Context context, List<String> d) {
        this.context = context;
        this.datas = d;
    }

    public int getCount() {
        return datas.size();
    }

    public Object getItem(int arg0) {
        return datas.get(arg0);
    }

    public long getItemId(int arg0) {
        return arg0;
    }

    /**
     * @author ZYJ
     * @功能 一個簡單TextView顯示
     */
    public View getView(int arg0, View arg1, ViewGroup arg2) {
        View view = LayoutInflater.from(context).inflate(R.layout.childlist,
                null);
        TextView textStr = (TextView) view.findViewById(R.id.info);
        textStr.setText("\t" + getItem(arg0).toString());
        return view;
    }

    public void setDatas(List<String> datas) {
        this.datas = datas;
    }

}
這樣,上面的功能基本上都實現了,我的這個控制元件在我專案中是手動新增上去的,而不是定義在xml檔案中的,所以也不知道定義在xml檔案中能不能生效,各位儘管試試吧。