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

Android ScrollView 下拉会有阴影 android下拉框实现_移动开发

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

 

Android ScrollView 下拉会有阴影 android下拉框实现_java_02

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

Android ScrollView 下拉会有阴影 android下拉框实现_控件_03

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

 

 

 

 

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

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

 

1. /** 
2.  * 用于弹出的下拉框 
3.  */  
4. private PopupWindow pWindow = null;  
5.   
6. // **************************************************************************  
7. // 这些是用来当点击一个时,根据他们之间的关系来显示下拉框中的内容  
8. // **************************************************************************  
9. /** 
10.  * 祖父 
11.  */  
12. private DefineSpinnerView gradeParent = null;  
13. /** 
14.  * 父控件 
15.  */  
16. private DefineSpinnerView parents = null;  
17. /** 
18.  * 子控件 
19.  */  
20. private DefineSpinnerView child1 = null;  
21. /** 
22.  * 孙子控件 
23.  */  
24. private DefineSpinnerView child2 = null;  
25.   
26. private Context context = null;  
27. private OptionsAdapter adapter = null; // 下拉框适配器  
28. private List<String> datas = null; // 下拉框数据  
29. private RelativeLayout layout = null; // 父控件  
30. private TextView text = null; // 文本显示  
31. private ImageView image = null; // 下拉箭头  
32. private int p_width = -1; // 下拉框宽度  
33. private ListView list = null; // 下拉表

 

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

 

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.   
11. new ImageView(context);  
12.         image.setBackgroundResource(R.drawable.gerendang_jiantou);  
13.         image.setOnClickListener(lis);  
14. if (LoginAct.MACHINE_PIXELS == IFinalConstant.XHDPI_RESOLUTION) {  
15. 20.0f);  
16. new LayoutParams(19, 17);  
17. 15;  
18. 28;  
19.             map.put(image, params2);  
20. else {  
21. 15.0f);  
22. new LayoutParams(8, 8);  
23. 13;  
24. 16;  
25.             map.put(image, params2);  
26.         }  
27.   
28.         map.put(text, params1);

 

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

 

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,它的定义如下:

 

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方法,定义如下:

 


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,如下:

 

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方法:

 

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.    }

 

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

 

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.     }

 

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

 

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文件:


    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),看全部代码吧:

     


      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文件中能不能生效,各位尽管试试吧。