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默认的背景。
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content"
-
android:orientation="horizontal"
-
android:background="@drawable/edit_bg_shape" >
-
-
<EditText
-
android:id="@+id/dropview_edit"
-
android:layout_width="0dip"
-
android:layout_weight="1"
-
android:layout_height="wrap_content"
-
style="?android:attr/textViewStyle"
-
android:background="@null"
-
android:height="40dip" />
-
-
<ImageView
-
android:id="@+id/dropview_image"
-
android:layout_width="wrap_content"
-
android:layout_height="match_parent"
-
android:layout_gravity="center_vertical"
-
android:scaleType="fitXY"
-
android:layout_marginTop="2dip"
-
android:layout_marginBottom="2dip"
-
android:paddingRight="2dip" />
-
</LinearLayout>
第二个问题,控制PopupWindow的弹出的位置,很明显我们需要它显示在EditText上下面,sdk给我们提供了一个很好的方法:
-
PopupWindow.showAsDropDown(anchor, xoff, yoff)
参数1:以哪个view为目标弹出
参数2:x偏移量
参数3:y的偏移量
第二个问题就这么轻松的解决了。
第三个问题,我想到了用ListView做,用ListView做可以很方便的取item,但是有一个问题:ListView默认是占满横向的屏幕的,但这也不是问题,我们可以重写ListView的onMeasure方法改变它的测量机制。
五、上代码
先来看看重写的ListView吧,做的工作就是重写onMeasure方法,使它的宽度可变,还提供了一个方法可以设置ListView的宽度:
-
public class WrapListView extends ListView {
-
private int mWidth = 0;
-
-
public WrapListView(Context context, AttributeSet attrs) {
-
this(context, attrs, 0);
-
}
-
-
public WrapListView(Context context, AttributeSet attrs, int defStyle) {
-
super(context, attrs, defStyle);
-
}
-
-
-
@Override
-
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-
int height = getMeasuredHeight();
-
-
for(int i=0;i<getChildCount();i++) {
-
int childWidth = getChildAt(i).getMeasuredWidth();
-
mWidth = Math.max(mWidth, childWidth);
-
}
-
-
setMeasuredDimension(mWidth, height);
-
}
-
-
-
-
-
-
protected void setListWidth(int width) {
-
mWidth = width;
-
}
-
}
重点在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,让组件用起来更见方便。
-
public class DropEditText extends FrameLayout implements View.OnClickListener, OnItemClickListener {
-
private EditText mEditText;
-
private ImageView mDropImage;
-
private PopupWindow mPopup;
-
private WrapListView mPopView;
-
-
private int mDrawableLeft;
-
private int mDropMode;
-
private String mHit;
-
-
public DropEditText(Context context, AttributeSet attrs) {
-
this(context, attrs, 0);
-
}
-
-
public DropEditText(Context context, AttributeSet attrs, int defStyle) {
-
super(context, attrs, defStyle);
-
LayoutInflater.from(context).inflate(R.layout.edit_layout, this);
-
mPopView = (WrapListView) LayoutInflater.from(context).inflate(R.layout.pop_view, null);
-
-
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.DropEditText, defStyle, 0);
-
mDrawableLeft = ta.getResourceId(R.styleable.DropEditText_drawableRight, R.drawable.ic_launcher);
-
mDropMode = ta.getInt(R.styleable.DropEditText_dropMode, 0);
-
mHit = ta.getString(R.styleable.DropEditText_hint);
-
ta.recycle();
-
}
-
-
@Override
-
protected void onFinishInflate() {
-
super.onFinishInflate();
-
-
mEditText = (EditText) findViewById(R.id.dropview_edit);
-
mDropImage = (ImageView) findViewById(R.id.dropview_image);
-
-
mEditText.setSelectAllOnFocus(true);
-
mDropImage.setImageResource(mDrawableLeft);
-
-
if(!TextUtils.isEmpty(mHit)) {
-
mEditText.setHint(mHit);
-
}
-
-
mDropImage.setOnClickListener(this);
-
mPopView.setOnItemClickListener(this);
-
}
-
-
@Override
-
protected void onLayout(boolean changed, int left, int top, int right,
-
int bottom) {
-
super.onLayout(changed, left, top, right, bottom);
-
-
-
-
if(changed && 0 == mDropMode) {
-
mPopView.setListWidth(getMeasuredWidth());
-
}
-
}
-
-
-
-
-
-
public void setAdapter(BaseAdapter adapter) {
-
mPopView.setAdapter(adapter);
-
-
mPopup = new PopupWindow(mPopView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
-
mPopup.setBackgroundDrawable(new ColorDrawable(color.transparent));
-
mPopup.setFocusable(true);
-
}
-
-
-
-
-
-
public String getText() {
-
return mEditText.getText().toString();
-
}
-
-
@Override
-
public void onClick(View v) {
-
if(v.getId() == R.id.dropview_image) {
-
if(mPopup.isShowing()) {
-
mPopup.dismiss();
-
return;
-
}
-
-
mPopup.showAsDropDown(this, 0, 5);
-
}
-
}
-
-
@Override
-
public void onItemClick(AdapterView<?> parent, View view, int position,
-
long id) {
-
mEditText.setText(mPopView.getAdapter().getItem(position).toString());
-
mPopup.dismiss();
-
}
-
}
在构造方法中我们获取了三个自定义的属性:
mDrawableLeft —— 点击下拉的图标
mDropMode —— 下拉菜单显示的模式,是一个枚举类型,提供了wrap_content和flower_parent两种模式,在演示的图片中可以看到两个DropEditText的下拉列表是不同的,第一个是宽度和上面的View相同,使用了flower_parent类型;第二个是包裹内容,使用了wrap_content类型。
mHit —— 就是EditText的hit。
再看onLayout:
-
@Override
-
protected void onLayout(boolean changed, int left, int top, int right,
-
int bottom) {
-
super.onLayout(changed, left, top, right, bottom);
-
if(changed && 0 == mDropMode) {
-
mPopView.setListWidth(getMeasuredWidth());
-
}
-
}
一个if语句,表示如果模式为flower_parent时才去设置ListView的宽度,为什么呢? 答案就是:WrapListView默认就是包裹内容的,所以在wrap_content模式下根本不需要任何代码,ListView就是包裹内容的。
接下来就是一个setAdapter的方法了:
-
public void setAdapter(BaseAdapter adapter) {
-
mPopView.setAdapter(adapter);
-
-
mPopup = new再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net