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


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


四、解决问题

再回头看看我们的问题。

第一个问题很好解决,相信很多人都可以轻松的解决。做法是用一个LinearLayout包裹EditText和ImageView,同时设置LinearLayout的背景为我们定义的shape,同时取消掉EditText默认的背景。

[html] view
plain
copy

  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
plain
copy

  1. PopupWindow.showAsDropDown(anchor, xoff, yoff)  

参数1:以哪个view为目标弹出

参数2:x偏移量

参数3:y的偏移量

第二个问题就这么轻松的解决了。


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


五、上代码

先来看看重写的ListView吧,做的工作就是重写onMeasure方法,使它的宽度可变,还提供了一个方法可以设置ListView的宽度:

[java] view
plain
copy

  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
plain
copy

  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
plain
copy

  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就是包裹内容的。

接下来就是一个setAdapter的方法了:

[java] view
plain
copy

  1. public void setAdapter(BaseAdapter adapter) {  
  2.     mPopView.setAdapter(adapter);  
  3.           
  4.     mPopup = new PopupWindow(mPopView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);  
  5.     mPopup.setBackgroundDrawable(new ColorDrawable(color.transparent));  
  6.     mPopup.setFocusable(true); // 让popwin获取焦点  
  7. }  

参数就是BasicAdapter,功能就是给ListView设置adapter,在这里还初始化了PopupWindow,没关系它完全可以不在这里初始化,把它放这是因为我并没有想一开始就用ListView做。

下面的代码就没什么好说的了。getText()是获取EditText文本的,在onClick()中显示和隐藏PopupWindow,在onItemClick()中获取ListView的item的内容,并填充到EditText中。

 

ok,到这里DropEditText的实现已经完成,最后再让我们看看效果吧:


最后是项目下载地址:https://github.com/qibin0506/DropEditText

时间: 2024-11-01 22:04:44

Android 打造可下拉的EditText--DropEditText的相关文章

静态测试-Android两个下拉框静态联动

问题描述 Android两个下拉框静态联动 只需要静态联动,是一个选课的年级选择,一级下拉框是1-6年级上下册共12个选择,二级下拉框是第一单元到第十单元.我在做的时候,可以选择,但是选了第二个之后,第一个的数据会变动.本人是初学者菜鸟,求指教 解决方案 页面这样,然后服务器抛出空指针错误 解决方案二: 只能说代码写的不对,查查代码吧 解决方案三: 检查一下都有哪里可以对第一个下拉列表进行修改,考虑怎样会触发这段代码执行,必要情况下走一下断点. 解决方案四: 这种问题最好把你的代码贴出来

按钮-android 如何实现下拉菜单,并查询相应的内容

问题描述 android 如何实现下拉菜单,并查询相应的内容 在android实现 要实现:选择相应的学期,点击查询按钮,即可获得对应的内容. 跪谢各位大神! 解决方案 创建下拉列表Adapter对象,然后再添加单机事件. 解决方案二: 需要源码吗?我现在挺累的,你要是搞懂了我就不回答了,电脑里有练习过的例子源码! 解决方案三: http://www.cnblogs.com/tinyphp/p/3858920.html 用下拉框,至于查询,你客户端获取了下拉选项,然后用web service等提

android gridview自定义下拉刷新,只有头部拉下来了

问题描述 android gridview自定义下拉刷新,只有头部拉下来了 解决方案 什么意思,表达清楚点 解决方案二: 实在不懂什么意思,请表达清楚点 解决方案三: 只有headview拉下来 然后没上去? 还是 没有刷新?

Android中Spinner(下拉框)控件的使用详解_Android

android给我们提供了一个spinner控件,这个控件主要就是一个列表,那么我们就来说说这个控件吧,这个控件在以前的也看见过,但今天还是从新介绍一遍吧. Spinner位于 android.widget包下,每次只显示用户选中的元素,当用户再次点击时,会弹出选择列表供用户选择,而选择列表中的元素同样来自适配器.Spinner是View类得一个子类. 1.效果图 2.创建页面文件(main.xml) <Spinner android:id="@+id/spinner1" and

Android仿美团下拉菜单(商品选购)实例代码_Android

美团电商应用平台大家使用非常频繁,下面小编通过本文给大家介绍电商应用平台中常用的选择类别下拉列表的实现.先给大家展示下效果图: 一.下拉列表的实现 其实实现方法有很多,这时实现的也没有什么技术含量,只是总结下自己在项目中的做法,也提供一个思路. 首先是列表的数据,一般数据都是从后台读过来,这里因为没有后台,所以写死在客户端: private void initMenuData() { menuData = new ArrayList<map<string, string=""

Android中ListView下拉刷新的实现方法实例分析_Android

本文实例讲述了Android中ListView下拉刷新的实现方法.分享给大家供大家参考,具体如下: ListView中的下拉刷新是非常常见的,也是经常使用的,看到有很多同学想要,那我就整理一下,供大家参考.那我就不解释,直接上代码了. 这里需要自己重写一下ListView,重写代码如下: package net.loonggg.listview; import java.util.Date; import android.content.Context; import android.util.

Android实现RecyclerView下拉刷新效果

本文为大家分享了Android实现RecyclerView下拉刷新效果的具体代码,供大家参考,具体内容如下 思路 RealPullRefreshView继承了一个LinearLayout 里面放置了一个刷新头布局,将其margin_top设置为负的刷新头的高度的 再添加一个RecyclerView 触摸事件分发机制,当在特定条件下让RealPullRefreshView拦截触摸事件,否则的话,不拦截,让RecyclerView自己去处理触摸事件 在手指下拉时,定义好不同的状态STATE,在不同状

Android中ListView下拉刷新的实现代码

Android中ListView下拉刷新 实现效果图: ListView中的下拉刷新是非常常见的,也是经常使用的,看到有很多同学想要,那我就整理一下,供大家参考.那我就不解释,直接上代码了. 这里需要自己重写一下ListView,重写代码如下: package net.loonggg.listview; import java.util.Date; import android.content.Context; import android.util.AttributeSet; import a

Android第三方开源下拉框NiceSpinner使用详解

android原生的下拉框Spinner基本上可以满足Android开发对于下拉选项的设计需求,但现在越来越流行的下拉框不满足于Android原生提供的下拉框Spinner所提供的设计样式,而改用自定制或者第三方设计的下拉框Spinner. NiceSpinner是一个第三方开源的下拉框Spinner,其在github上的项目主页是:https://github.com/arcadefire/nice-spinner  NiceSpinner原设计效果如动图所示: 但是通常开发者对于可能还需要对