Android 打造可下拉的EditText--DropEditText

枕上诗书闲处好,门前风景雨来佳。这篇文章主要讲述Android 打造可下拉的EditText--DropEditText相关的知识,希望能为你提供帮助。
android的默认Spinner只能下拉选择内容,而不能选择,有时候我们想提供给用户更加人性化的UI,既可以通过下拉选择,也可以通过EditText输入,是要定义两个组件吗? 这样并不适合我们的设计要求。
那么, 我们就自己写一个这样的组件吧— — DropEditText。
一、思路
1、DropEditText并不是一个Spinner,也不是一个EditText。
2、这里的解决方式是,组合以个EditText和一个ImageView,点击ImageView弹出一个PopupWindow达到下拉的效果。
 
二、问题
1、组合EditText和ImageView,而又不能让用户看出这是一个组合。
2、PopupWindow弹出的位置怎么控制。
3、如何做到当点击PopupWindow上的选项,向EditText填充对应的内容。


三、效果
再开始代码之前,先看看预想的效果吧,有了目标才好code

Android 打造可下拉的EditText--DropEditText

文章图片


通过效果图可以看出,DropEditText还算比较灵活的,可以设置下拉的模式:跟随上面和包裹内容。


四、解决问题
再回头看看我们的问题。
第一个问题很好解决,相信很多人都可以轻松的解决。做法是用一个LinearLayout包裹EditText和ImageView,同时设置LinearLayout的背景为我们定义的shape,同时取消掉EditText默认的背景。
 
[html]  view plaincopy  
  1. < LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"   
  2.         android:layout_width="match_parent"   
  3.         android:layout_height="wrap_content"   
  4.         android:orientation="horizontal"   
  5.         android:background="@drawable/edit_bg_shape"  >    
  6.            
  7.         < EditText   
  8.                 android:id="@+id/dropview_edit"   
  9.                 android:layout_width="0dip"   
  10.                 android:layout_weight="1"   
  11.                 android:layout_height="wrap_content"   
  12.                 style="?android:attr/textViewStyle"   
  13.                 android:background="@null"   
  14.                 android:height="40dip"  />    
  15.            
  16.         < ImageView   
  17.                 android:id="@+id/dropview_image"   
  18.                 android:layout_width="wrap_content"   
  19.                 android:layout_height="match_parent"   
  20.                 android:layout_gravity="center_vertical"   
  21.                 android:scaleType="fitXY"   
  22.                 android:layout_marginTop="2dip"   
  23.                 android:layout_marginBottom="2dip"   
  24.                 android:paddingRight="2dip"  />    
  25. < /LinearLayout>    
 
 
第二个问题,控制PopupWindow的弹出的位置,很明显我们需要它显示在EditText上下面,sdk给我们提供了一个很好的方法: 
[html]  view plaincopy  
  1. PopupWindow.showAsDropDown(anchor,  xoff,  yoff)   
参数1:以哪个view为目标弹出 
参数2:x偏移量
参数3:y的偏移量
第二个问题就这么轻松的解决了。


第三个问题,我想到了用ListView做,用ListView做可以很方便的取item,但是有一个问题:ListView默认是占满横向的屏幕的,但这也不是问题,我们可以重写ListView的onMeasure方法改变它的测量机制。


五、上代码
先来看看重写的ListView吧,做的工作就是重写onMeasure方法,使它的宽度可变,还提供了一个方法可以设置ListView的宽度:
 
[java]  view plaincopy  
  1. public  class  WrapListView  extends  ListView  {   
  2.         private  int  mWidth  =  0;    
  3.            
  4.         public  WrapListView(Context  context,  AttributeSet  attrs)  {   
  5.                 this(context,  attrs,  0);    
  6.         }   
  7.    
  8.         public  WrapListView(Context  context,  AttributeSet  attrs,  int  defStyle)  {   
  9.                 super(context,  attrs,  defStyle);    
  10.         }   
  11.            
  12.         //  重写onMeasure方法  解决默认横向占满屏幕问题   
  13.         @Override   
  14.         protected  void  onMeasure(int  widthMeasureSpec,  int  heightMeasureSpec)  {   
  15.                 super.onMeasure(widthMeasureSpec,  heightMeasureSpec);    
  16.                    
  17.                 int  height  =  getMeasuredHeight();    
  18.                 //  measureChildren(widthMeasureSpec,  heightMeasureSpec);    
  19.                 for(int  i=0; i< getChildCount(); i++)  {   
  20.                         int  childWidth  =  getChildAt(i).getMeasuredWidth();    
  21.                         mWidth  =  Math.max(mWidth,  childWidth);    
  22.                 }   
  23.                    
  24.                 setMeasuredDimension(mWidth,  height);    
  25.         }   
  26.            
  27.         /** 
  28.           *  设置宽度,如果不设置,则默认包裹内容 
  29.           *  @param  width  宽度 
  30.           */   
  31.         protected  void  setListWidth(int  width)  {   
  32.                 mWidth  =  width;    
  33.         }   
  34. }   
重点在14~25行,首先我们调用了一下父类的onMeasure方法,为的就是利用ListView默认的测量机制获取总高度。在第17行我们获取了测量后的高度,接下来就是一个for循环,取出每一个item,并获取他的高度。mWidth的值就是子item中最宽的那个item的宽度(当然在下面我们提供的方法中可以手动修改mWidth的值)。 
自定义的setListWidth方法可以手动设置ListView的宽度,按说,我们改变了width的值,应该requestLayout()一下让ListView重走一下测量流程才对,但这里没有提供。答案就是:ListView默认是不显示的,只有在点击了drop图标后才会出现,也就是setListWidth()永远会走在onMeasure前面,可能到这里会有点晕,看完下面的代码后就会清楚了。
 
再来看看DropEditText吧,这个View是组合了EditText和ImageView,内置了一个PopupWindow,让组件用起来更见方便。
 
[java]  view plaincopy  
  1. public  class  DropEditText  extends  FrameLayout  implements  View.OnClickListener,  OnItemClickListener  {   
  2.         private  EditText  mEditText;     //  输入框   
  3.         private  ImageView  mDropImage;   //  右边的图片按钮   
  4.         private  PopupWindow  mPopup;   //  点击图片弹出popupwindow   
  5.         private  WrapListView  mPopView;   //  popupwindow的布局   
  6.            
  7.         private  int  mDrawableLeft;    
  8.         private  int  mDropMode;   //  flower_parent  or  wrap_content   
  9.         private  String  mHit;    
  10.            
  11.         public  DropEditText(Context  context,  AttributeSet  attrs)  {   
  12.                 this(context,  attrs,  0);    
  13.         }   
  14.            
  15.         public  DropEditText(Context  context,  AttributeSet  attrs,  int  defStyle)  {   
  16.                 super(context,  attrs,  defStyle);    
  17.                 LayoutInflater.from(context).inflate(R.layout.edit_layout,  this);    
  18.                 mPopView  =  (WrapListView)  LayoutInflater.from(context).inflate(R.layout.pop_view,  null);    
  19.                    
  20.                 TypedArray  ta  =  context.obtainStyledAttributes(attrs,  R.styleable.DropEditText,  defStyle,  0);    
  21.                 mDrawableLeft  =  ta.getResourceId(R.styleable.DropEditText_drawableRight,  R.drawable.ic_launcher);    
  22.                 mDropMode  =  ta.getInt(R.styleable.DropEditText_dropMode,  0);    
  23.                 mHit  =  ta.getString(R.styleable.DropEditText_hint);    
  24.                 ta.recycle();    
  25.         }   
  26.            
  27.         @Override   
  28.         protected  void  onFinishInflate()  {   
  29.                 super.onFinishInflate();    
  30.                    
  31.                 mEditText  =  (EditText)  findViewById(R.id.dropview_edit);    
  32.                 mDropImage  =  (ImageView)  findViewById(R.id.dropview_image);    
  33.                    
  34.                 mEditText.setSelectAllOnFocus(true);    
  35.                 mDropImage.setImageResource(mDrawableLeft);    
  36.            
  37.                 if(!TextUtils.isEmpty(mHit))  {   
  38.                         mEditText.setHint(mHit);    
  39.                 }   
  40.                    
  41.                 mDropImage.setOnClickListener(this);    
  42.                 mPopView.setOnItemClickListener(this);    
  43.         }   
  44.            
  45.         @Override   
  46.         protected  void  onLayout(boolean  changed,  int  left,  int  top,  int  right,   
  47.                         int  bottom)  {   
  48.                 super.onLayout(changed,  left,  top,  right,  bottom);    
  49.                 //  如果布局发生改   
  50.                 //  并且dropMode是flower_parent   
  51.                 //  则设置ListView的宽度   
  52.                 if(changed  & &   0  ==  mDropMode)  {   
  53.                         mPopView.setListWidth(getMeasuredWidth());    
  54.                 }   
  55.         }   
  56.            
  57.         /** 
  58.           *  设置Adapter 
  59.           *  @param  adapter  ListView的Adapter 
  60.           */   
  61.         public  void  setAdapter(BaseAdapter  adapter)  {   
  62.                 mPopView.setAdapter(adapter);    
  63.                    
  64.                 mPopup  =  new  PopupWindow(mPopView,  LinearLayout.LayoutParams.WRAP_CONTENT,  LinearLayout.LayoutParams.WRAP_CONTENT);    
  65.                 mPopup.setBackgroundDrawable(new  ColorDrawable(color.transparent));    
  66.                 mPopup.setFocusable(true);   //  让popwin获取焦点   
  67.         }   
  68.            
  69.         /** 
  70.           *  获取输入框内的内容 
  71.           *  @return  String  content 
  72.           */   
  73.         public  String  getText()  {   
  74.                 return  mEditText.getText().toString();    
  75.         }   
  76.            
  77.         @Override   
  78.         public  void  onClick(View  v)  {   
  79.                 if(v.getId()  ==  R.id.dropview_image)  {   
  80.                         if(mPopup.isShowing())  {   
  81.                                 mPopup.dismiss();    
  82.                                 return;    
  83.                         }   
  84.                            
  85.                         mPopup.showAsDropDown(this,  0,  5);    
  86.                 }   
  87.         }   
  88.    
  89.         @Override   
  90.         public  void  onItemClick(AdapterView< ?>   parent,  View  view,  int  position,   
  91.                         long  id)  {   
  92.                 mEditText.setText(mPopView.getAdapter().getItem(position).toString());    
  93.                 mPopup.dismiss();    
  94.         }   
  95. }   

 
在构造方法中我们获取了三个自定义的属性:
mDrawableLeft  — —   点击下拉的图标
mDropMode  — —   下拉菜单显示的模式,是一个枚举类型,提供了wrap_content和flower_parent两种模式,在演示的图片中可以看到两个DropEditText的下拉列表是不同的,第一个是宽度和上面的View相同,使用了flower_parent类型;第二个是包裹内容,使用了wrap_content类型。
mHit  — —   就是EditText的hit。
再看onLayout:
[java]  view plaincopy  
  1. @Override   
  2. protected  void  onLayout(boolean  changed,  int  left,  int  top,  int  right,   
  3.                 int  bottom)  {   
  4.         super.onLayout(changed,  left,  top,  right,  bottom);    
  5.         if(changed  & &   0  ==  mDropMode)  {   
  6.                 mPopView.setListWidth(getMeasuredWidth());    
  7.         }   
  8. }   

一个if语句,表示如果模式为flower_parent时才去设置ListView的宽度,为什么呢?  答案就是:WrapListView默认就是包裹内容的,所以在wrap_content模式下根本不需要任何代码,ListView就是包裹内容的。
【Android 打造可下拉的EditText--DropEditText】接下来就是一个setAdapter的方法了:
[java]  view plaincopy  
  1. public  void  setAdapter(BaseAdapter  adapter)  {   
  2.         mPopView.setAdapter(adapter);    
  3.                    
  4.         mPopup  =  new再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net

    推荐阅读