一、效果:我们看到很多软件的通讯录在右侧都有一个字母索引功能,像微信,小米通讯录,QQ,还有美团选择地区等等。这里我截了一张美团选择城市的图片来看看;
我们今天就来实现图片中右侧模块的索引功能,包括触摸显示以选中的索引字母。这里我的UI界面主要是参照微信的界面来实现,所以各位也可以对照微信来看看效果,什么都不说了,只有效果图最具有说服力!
二、分析:
我们看到这样的效果我们心理都回去琢磨,他是如何实现的;
首先,它肯定是通过自定义 VIEw 来实现的,因为 AndroID 没有提供类似这样的控件、那么接下来就是如何自定义我们的 VIEw ,我们知道自定义 VIEw 最最主要的两个方法就是 onDraw(Canvas canvas)和
onMeasure(int wIDthMeasureSpec,int heightmeasureSpec)方法,当然,如果是自定义 VIEwGroup 的话就必须实现
onLayout(boolean changed,int left,int top,int right,int bottom) 方法,这里我们显然用自定义 VIEw 就能够实现此功能,通过效果图可以看带,当触摸这块区域的时候,会d出一个悬浮类似 Toast 的框来显示已经选中的索引内容,所以这里还需要重写VIEw 的ontouchEvent(MotionEvent event)事件,最后就是悬浮框的实现。那么接下来就开始我们编码。
三、编码实现:
我们就按照 VIEw 的执行顺序来实现
1、实现onMeasure(int wIDthMeasureSpec,int heightmeasureSpec)方法,这个方法的功能是测量出我们的宽和高,具体实现看代码
@OverrIDe protected voID onMeasure(int wIDthMeasureSpec,int heightmeasureSpec) { setMeasuredDimension(measureWIDth(wIDthMeasureSpec),measureHeight(heightmeasureSpec)); }
这里定义了两个方法measureWIDth( int) 和 measureHeight(int) ,通过方法名可以很清楚的知道,其功能分别是测量宽和高,进去看看是如何测量的。
/** * 测量本身的大小,这里只是测量宽度 * @param wIDthMeaSpec 传入父VIEw的测量标准 * @return 测量的宽度 */ private int measureWIDth(int wIDthMeaSpec){ /*定义vIEw的宽度*/ int wIDth ; /*获取当前 VIEw的测量模式*/ int mode = MeasureSpec.getMode(wIDthMeaSpec) ; /* * 获取当前VIEw的测量值,这里得到的只是初步的值, * 我们还需根据测量模式来确定我们期望的大小 * */ int size = MeasureSpec.getSize(wIDthMeaSpec) ; /* * 如果,模式为精确模式 * 当前VIEw的宽度,就是我们 * 的size ; * */ if(mode == MeasureSpec.EXACTLY){ wIDth = size ; }else { /*否则的话我们就需要结合padding的值来确定*/ int desire = size + getpaddingleft() + getpaddingRight() ; if(mode == MeasureSpec.AT_MOST){ wIDth = Math.min(desire,size) ; }else { wIDth = desire ; } } mVIEwWIDth = wIDth ; return wIDth ; }
以上是测量宽度的代码,其测量高度的代码,跟测量宽度的代码大致雷同,就不贴出来了,我会在最后附上源码。
2、实现onDraw(Canvas c)方法,这个方法相信大家都非常熟悉,就是把这些索引的内容绘制到 VIEw 上显示出来,包括选中的时候背景颜色的变化;
@OverrIDe protected voID onDraw(Canvas canvas) { if(mtouched){ canvas.drawcolor(0x30000000); } for (int i = 0 ; i < mIndex.length ; i ++){ mPaint.setcolor(0xff000000); mPaint.setTextSize(mTextSize * 3.0f / 4.0f); mPaint.setTypeface(Typeface.DEFAulT) ; mPaint.getTextBounds(mIndex[i],mIndex[i].length(),mTextBound); float formX = mVIEwWIDth/2.0f - mTextBound.wIDth()/2.0f ; float formY = mTextSize*i + mTextSize/2.0f + mTextBound.height()/2.0f ; canvas.drawText(mIndex[i],formX,formY,mPaint); mPaint.reset(); } }
我来讲一下 onDraw 方法中大致做了什么事,第一,绘制背景颜色,注意不是一上来就绘制,而是等到有手指触摸的时候就绘制背景颜色,第二,就是绘制索引的内容,这里需要根据当前 VIEw 的宽和高来决定绘制内容的大小,和位置。
3、ontouchEvent(MotionEvent event)方法的实现
@OverrIDe public boolean ontouchEvent(MotionEvent event) { float y = event.getY() ; int index = (int) (y / mTextSize); if(index >= 0 && index < mIndex.length){ Log.v("zgy","======index======="+index) ; selectItem(index); } if(event.getAction() == MotionEvent.ACTION_MOVE){ mtouched = true ; }else if (event.getAction() == MotionEvent.ACTION_MOVE){ }else { mfloatVIEw.setVisibility(INVISIBLE); mtouched = false ; } invalIDate(); /*过滤点其他触摸事件*/ return true; }
代码也相对比较简单,首先获取当前触摸的点,根据点的坐标来获取索引的位置,从而拿到索引的位置。
4、到这里其实就已经实现了我们想要的效果,但是这样我们还是无法运用它,这里就需要定义一个回调接口
/*定义一个回调接口*/ public interface OnIndexSelectListener{ /*返回选中的位置,和对应的索引名*/ voID onItemSelect(int position,String value) ; }
回调接口我们放在哪里调用呢,当我们手指按下的时候,这时候其实我们需要确定我们按下的是哪个索引,滑动的时候也是一样,所以,这个没什么好商量的,直接放在ontouchEvent(MotionEvent event)中就可以,
float y = event.getY() ; int index = (int) (y / mTextSize); if(index >= 0 && index < mIndex.length){ Log.v("zgy","======index======="+index) ; selectItem(index); }
selectItem(int)方法中就是执行的回调方法。
5、实现悬浮框显示已经选中的索引内容
这里需要用到 WindowManager 容器,然需要现实的 VIEw 附在这上面的就行,当手指按下的时候,让 VIEw 显示出来,松开不显示就行了
/*设置浮动选中的索引*/ /*获取windowManager*/ mWindowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); /*overly 视图,通过LayoutInflater 获取*/ mfloatVIEw = LayoutInflater.from(getContext()).inflate(R.layout.overlay_indexvIEw,null) ; /*开始让其不可见*/ mfloatVIEw.setVisibility(INVISIBLE); /*转换 高度 和宽度为SP*/ mOverlyWIDth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,70,getResources().getdisplayMetrics()) ; mOverlyHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,getResources().getdisplayMetrics()) ; post(new Runnable() { @OverrIDe public voID run() { WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(mOverlyWIDth,mOverlyHeight,WindowManager.LayoutParams.TYPE_APPliCATION,WindowManager.LayoutParams.FLAG_NOT_touchABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,PixelFormat.TRANSLUCENT) ; mWindowManager.addVIEw(mfloatVIEw,layoutParams); } }) ;
同样的道理,如果需要改变显示的内容,就需要在调用回调的位置,为 VIEw 中的 TextVIEw 设置当前的索引内容。
好了此 VIEw 的代码就这么多,
接下来就把引用他的 Xml 和浮动 VIEw 的 Xml 也贴出来,
引用的布局文件
<moon.wechat.vIEw.IndexVIEw androID:layout_wIDth="25dp" androID:layout_height="match_parent" androID:layout_alignParentRight="true"/>
浮动 VIEw 的布局文件
<?xml version="1.0" enCoding="utf-8"?><TextVIEw xmlns:androID="http://schemas.androID.com/apk/res/androID" androID:ID="@+ID/overly_text" androID:layout_wIDth="70dp" androID:layout_height="70dp" androID:text="A" androID:gravity="center" androID:background="@drawable/bg_overly_text" androID:textSize="40sp" androID:textcolor="#ffffffff" androID:layout_gravity="center"></TextVIEw>
浮动 VIEw 的背景
<?xml version="1.0" enCoding="utf-8"?><@R_212_3419@ xmlns:androID="http://schemas.androID.com/apk/res/androID"> <item> <shape> <solID androID:color="#88000000"/> <corners androID:radius="5dp"/> </shape> </item></@R_212_3419@>
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持编程小技巧!
总结以上是内存溢出为你收集整理的Android自定义View实现通讯录字母索引(仿微信通讯录)全部内容,希望文章能够帮你解决Android自定义View实现通讯录字母索引(仿微信通讯录)所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)