android自带的下拉框好用不?我觉得有时候好用,有时候难有,项目规定这样的效果,自带的控件实现不了,那么只有我们自己来老老实实滴写一个新的了,其实最基本的下拉框就像一些资料填写时,点击的时候出现在编辑框的下面,然后又很多选项的下拉框,可是我在网上找了一下,没有这种下拉框额,就自己写了一个,看效果图先:


android中自定义下拉框_控件

,这个是资料填写的一部分界面,三个下拉框,选择故乡所在地;



android中自定义下拉框_控件_02

点击之后弹出下拉框,选择下面的选项;

android中自定义下拉框_控件_03

三个下拉框时关联的,第一个决定了第二数据内容,第二个决定了第三个数据内容,如果三个全部选好之后,再次点击第一个,那么第二个、第三个都会清空,点击第二个则第三个会清空。





要实现它,也就是一个PopupWindow时主要的界面,下面来看看代码:

创建一个DefineSpinnerView.java文件,继承至View,然后给出如下属性:



[java]  ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​


  1. /**
  2. * 用于弹出的下拉框
  3. */
  4. private PopupWindow pWindow = null;

  5. // **************************************************************************
  6. // 这些是用来当点击一个时,根据他们之间的关系来显示下拉框中的内容
  7. // **************************************************************************
  8. /**
  9. * 祖父
  10. */
  11. private DefineSpinnerView gradeParent = null;
  12. /**
  13. * 父控件
  14. */
  15. private DefineSpinnerView parents = null;
  16. /**
  17. * 子控件
  18. */
  19. private DefineSpinnerView child1 = null;
  20. /**
  21. * 孙子控件
  22. */
  23. private DefineSpinnerView child2 = null;

  24. private Context context = null;
  25. private OptionsAdapter adapter = null; // 下拉框适配器
  26. private List<String> datas = null; // 下拉框数据
  27. private RelativeLayout layout = null; // 父控件
  28. private TextView text = null; // 文本显示
  29. private ImageView image = null; // 下拉箭头
  30. private int p_width = -1; // 下拉框宽度
  31. private ListView list = null; // 下拉表


在构造函数中,构造出一个TextView和一个ImageView控件,并将它们都添加到layout中,代码如下:


[java]  ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​


  1. TextListener lis = new TextListener();
  2. new TextView(context);
  3. text.setBackgroundResource(R.drawable.edit_normal);
  4. text.setTextColor(getResources().getColor(R.color.spinner_text));
  5. text.setGravity(Gravity.CENTER);
  6. text.setOnClickListener(lis);
  7. new LayoutParams(width, hight);
  8. params1.leftMargin = left;
  9. params1.topMargin = top;

  10. new ImageView(context);
  11. image.setBackgroundResource(R.drawable.gerendang_jiantou);
  12. image.setOnClickListener(lis);
  13. if (LoginAct.MACHINE_PIXELS == IFinalConstant.XHDPI_RESOLUTION) {
  14. 20.0f);
  15. new LayoutParams(19, 17);
  16. 15;
  17. 28;
  18. map.put(image, params2);
  19. else {
  20. 15.0f);
  21. new LayoutParams(8, 8);
  22. 13;
  23. 16;
  24. map.put(image, params2);
  25. }

  26. map.put(text, params1);



里面涉及到一个TextListener内部类,是我们自己定义的一个类,它继承至OnClickListener接口


[java]  ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​


  1. /**
  2. * @author ZYJ
  3. *         当点击Text时,根据上一级的内容来设置下一级的内容
  4. */
  5. class TextListener implements OnClickListener {
  6. public void onClick(View v) {
  7. hideSoft ();
  8. if (gradeParent != null && parents != null) {
  9. this.setDatas(DefineSpinnerView.this
  10. .getGuxiang3(gradeParent.getText(), parents.getText()));
  11. }
  12. if (gradeParent == null && parents != null) {
  13. this.setDatas(DefineSpinnerView.this
  14. .getGuxiang2(parents.getText()));
  15. }
  16. cleanText();
  17. changPopState(text);
  18. }



这个里面调用了一个方法changPopState,它的定义如下:


[java]    ​​view plain​​​   ​​copy​​​   ​​print​​​   ​​?​​    


1. /**
2. * 显示或者隐藏下拉框
3. *
4. * @param v
5. */
6. private void changPopState(View v) {
7. if (pWindow == null) {
8. popWindow(v);
9. return;
10. }
11. if (!pWindow.isShowing()) {
12. popWindow(v);
13. else {
14. if (pWindow != null) {
15. pWindow.dismiss();
16. }
17. }
18. }


这个里面又调用了一个popWindow方法,定义如下:


[java] ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​​


1. /**
2. * 初始化下拉框
3. *
4. * @param par 父控件
5. */
6. private void popWindow(final View par) {
7. if (pWindow == null) {
8.
9. // 布局文件
10. null);
11. list = (ListView) v.findViewById(R.id.list);
12. new OnItemClickListener() {
13. public void onItemClick(AdapterView<?> arg0, View arg1,
14. int arg2, long arg3) {
15. // R.String.butian代表的是“不填”
16. if (datas.get(arg2).toString().equals(context.getString(R.string.butian))) {
17. "");
18. else {
19. // 将当前点击的item中的字符串显示出来
20. }
21. if (pWindow != null) { // 关闭下拉框
22. changPopState(par);
23. }
24. }
25. });
26. new OptionsAdapter(context, datas); // 根据数据,设置下拉框显示
27. list.setAdapter(adapter);
28. null); // 屏蔽下拉框每个item之间的线条
29. /**
30. * 两种不同长度的下拉框,主要是为了适应屏幕的大小
31. */
32. if (p_width > 0) {
33. new PopupWindow(v, par.getWidth(), 150);
34. else {
35. new PopupWindow(v, par.getWidth(), 300);
36. }
37. true);
38. new BitmapDrawable());
39. true);
40. pWindow.update();
41. }
42. pWindow.showAsDropDown(text);
43. }


然后是一些细节了,提供一个TextView设置上面文字和得到上面文字的方法,设置下拉框数据的方法setDatas,如下:


[java] ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​​


1. public void setText(String str) {
2. if (text != null) {
3. text.setText(str);
4. }
5. }
6.
7. public void setDatas(List<String> datas) {
8. this.datas = datas;
9. if (adapter != null) {
10. adapter.setDatas(datas);
11. adapter.notifyDataSetInvalidated();
12. }
13. }
14.
15. public String getText() {
16. if (text != null) {
17. return text.getText().toString();
18. }
19. "spinner's textView is null");
20. return "";
21. }
22.
23. private void cleanText() {
24. if (child1 != null) {
25. "");
26. }
27. if (child2 != null) {
28. "");
29. }
30. }


然后添加几个关联控件的get方法:


[java] ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​​


1. public void setChild1(DefineSpinnerView child1) {
2. this.child1 = child1;
3. }
4.
5. public void setChild2(DefineSpinnerView child2) {
6. this.child2 = child2;
7. }
8.
9. public void setGradeParent(DefineSpinnerView gradeParent) {
10. this.gradeParent = gradeParent;
11. }
12.
13. public void setParents(DefineSpinnerView parents) {
14. this.parents = parents;
15. }
16.
17. public void setP_width(int p_width) {
18. this.p_width = p_width;
19. }


接下来提供一个设置子控件和算子控件数据的方法:


[java] ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​​


1. /**
2. * @param s1 父控件中的字符串
3. * @param s2 子控件中的字符串
4. * @return 返回一个List<String>集合
5. * @功能 通过父控件的字符串来设置子控件中的内容
6. */
7. private List<String> getGuxiang3(String s1, String s2) {
8. new ArrayList<String>();
9. dd.add(context.getString(R.string.butian));
10. Map<String, ArrayList<String>> mapTmp1 = MaterialView.cityMap.get(s1);
11. if (mapTmp1 != null) {
12. List<String> list = mapTmp1.get(s2);
13. if (list != null) {
14. for (String str : list) {
15. dd.add(str);
16. }
17. }
18. }
19. return dd;
20. }
21.
22. /**
23. * @param s 字符串
24. * @return
25. * @author ZYJ
26. * @功能 设置父亲辈的下拉框中的内容
27. */
28. private List<String> getGuxiang2(String s) {
29. new ArrayList<String>();
30. dd.add(context.getString(R.string.butian));
31. Map<String, ArrayList<String>> mapTmp = MaterialView.cityMap.get(s);
32. if (mapTmp != null) {
33. for (String str : mapTmp.keySet()) {
34. dd.add(str);
35. }
36. }
37. return dd;
38. }


最后提供一个隐藏软键盘的方法:


[java] ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​​


1. private void hideSoft() {
2. putMethodManager imm = (InputMethodManager) context
3. .getSystemService(Context.INPUT_METHOD_SERVICE);
4. m.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT,
5. InputMethodManager.HIDE_NOT_ALWAYS);


到这里,自定义控件的代码基本上写完了;我们还要来看看下拉框中的xml布局和适配器的写法:
xml文件:

[html] ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​​


1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2. xmlns:tools="http://schemas.android.com/tools"
3. android:layout_width="match_parent"
4. android:layout_height="match_parent"
5. android:orientation="horizontal">
6.
7. <TextView
8. android:id="@+id/info"
9. android:layout_width="wrap_content"
10. android:layout_height="30dp"
11. android:textSize="15sp"
12. android:textColor="@color/spinner_text"
13. android:layout_gravity="center"
14. android:gravity="center"/>
15.
16. </LinearLayout>



然后是适配器类(OptionsAdapter),看全部代码吧:


[java] ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​​


1. public class OptionsAdapter extends BaseAdapter {
2.
3. private Context context = null;
4. private List<String> datas = null;
5.
6. public OptionsAdapter(Context context, List<String> d) {
7. this.context = context;
8. this.datas = d;
9. }
10.
11. public int getCount() {
12. return datas.size();
13. }
14.
15. public Object getItem(int arg0) {
16. return datas.get(arg0);
17. }
18.
19. public long getItemId(int arg0) {
20. return arg0;
21. }
22.
23. /**
24. * @author ZYJ
25. * @功能 一个简单TextView显示
26. */
27. public View getView(int arg0, View arg1, ViewGroup arg2) {
28. View view = LayoutInflater.from(context).inflate(R.layout.childlist,
29. null);
30. TextView textStr = (TextView) view.findViewById(R.id.info);
31. "\t" + getItem(arg0).toString());
32. return view;
33. }
34.
35. public void setDatas(List<String> datas) {
36. this.datas = datas;
37. }
38.
39. }


这样,上面的功能基本上都实现了,我的这个控件在我项目中是手动添加上去的,而不是定义在xml文件中的,所以也不知道定义在xml文件中能不能生效,各位尽管试试吧。