android通讯录实例(二)
上一篇android通讯录实例(一)地址:http://blog.csdn.net/specialshoot/article/details/50651080
这一此我们把UI上最重要的部分IndexableListView说明,这个项目可以在github上找到。https://github.com/woozzu/IndexableListView
源码下载下来我们发现核心代码有两片,IndexableListView和IndexScroller这两个文件。其中IndexScroller是右侧字母滚动条的代码,IndexableListView是继承自ListView将滚动条整合到ListView的代码。
IndexScroller
IndexScroller.java源代码:
/* * Copyright 2011 woozzu * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.woozzu.android.widget;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.RectF;import android.os.Handler;import android.os.Message;import android.os.SystemClock;import android.view.MotionEvent;import android.widget.Adapter;import android.widget.ListView;import android.widget.SectionIndexer;public class IndexScroller { private float mIndexbarWidth;//索引条宽度 private float mIndexbarMargin; //索引条外边距 private float mPreviewPadding; //索引条内边距 private float mDensity; //颜色密度(用于控制颜色透明度) private float mScaledDensity; //缩放密度 private float mAlphaRate;//透明度 private int mState = STATE_HIDDEN; //索引条状态,默认为隐藏 private int mListViewWidth; //listview宽度 private int mListViewHeight;//listview高度 private int mCurrentSection = -1; //当前索引 private boolean mIsIndexing = false;//是否正在索引 private ListView mListView = null;//listview实例 private SectionIndexer mIndexer = null;//列表适配器 private String[] mSections = null;//存放索引字符的数组 private RectF mIndexbarRect;//索引条背景图形 //索引条状态 private static final int STATE_HIDDEN = 0; //隐藏 private static final int STATE_SHOWING = 1; //正在显示 private static final int STATE_SHOWN = 2; //已显示 private static final int STATE_HIDING = 3; //正在隐藏 public IndexScroller(Context context, ListView lv) { mDensity = context.getResources().getDisplayMetrics().density;//获取密度值 mScaledDensity = context.getResources().getDisplayMetrics().scaledDensity;//获得缩放密度值 mListView = lv;//listview实例 setAdapter(mListView.getAdapter()); //设置适配器 mIndexbarWidth = 20 * mDensity; mIndexbarMargin = 10 * mDensity; mPreviewPadding = 5 * mDensity; } public void draw(Canvas canvas) { if (mState == STATE_HIDDEN) return; // mAlphaRate determines the rate of opacity Paint indexbarPaint = new Paint();//用于显示当前滑动到字母的背景画笔(屏幕中间大字) indexbarPaint.setColor(Color.BLACK); indexbarPaint.setAlpha((int) (64 * mAlphaRate)); indexbarPaint.setAntiAlias(true);//抗锯齿 canvas.drawRoundRect(mIndexbarRect, 5 * mDensity, 5 * mDensity, indexbarPaint); if (mSections != null && mSections.length > 0) { // Preview is shown when mCurrentSection is set if (mCurrentSection >= 0) { Paint previewPaint = new Paint(); //预览图中背景画笔(右侧滚动条) previewPaint.setColor(Color.BLACK); previewPaint.setAlpha(96); previewPaint.setAntiAlias(true); previewPaint.setShadowLayer(3, 0, 0, Color.argb(64, 0, 0, 0)); Paint previewTextPaint = new Paint(); //预览图中文字画笔(滚动条上的文字) previewTextPaint.setColor(Color.WHITE); previewTextPaint.setAntiAlias(true); previewTextPaint.setTextSize(50 * mScaledDensity); float previewTextWidth = previewTextPaint.measureText(mSections[mCurrentSection]);//字符宽度 float previewSize = 2 * mPreviewPadding + previewTextPaint.descent() - previewTextPaint.ascent();//背景宽度/高度 RectF previewRect = new RectF((mListViewWidth - previewSize) / 2 , (mListViewHeight - previewSize) / 2 , (mListViewWidth - previewSize) / 2 + previewSize , (mListViewHeight - previewSize) / 2 + previewSize); canvas.drawRoundRect(previewRect, 5 * mDensity, 5 * mDensity, previewPaint); canvas.drawText(mSections[mCurrentSection], previewRect.left + (previewSize - previewTextWidth) / 2 - 1 , previewRect.top + mPreviewPadding - previewTextPaint.ascent() + 1, previewTextPaint); } Paint indexPaint = new Paint(); indexPaint.setColor(Color.WHITE); indexPaint.setAlpha((int) (255 * mAlphaRate)); indexPaint.setAntiAlias(true); indexPaint.setTextSize(12 * mScaledDensity); float sectionHeight = (mIndexbarRect.height() - 2 * mIndexbarMargin) / mSections.length;//索引条中每个字符空间的高度 float paddingTop = (sectionHeight - (indexPaint.descent() - indexPaint.ascent())) / 2;//索引条中每个字符的上下边距,indexPaint.descent() - indexPaint.ascent()得到的是字符的高度 for (int i = 0; i < mSections.length; i++) { //索引条中各个字符的左右边距,其中indexPaint.measureText(mSections[i])为字符的宽度 float paddingLeft = (mIndexbarWidth - indexPaint.measureText(mSections[i])) / 2; canvas.drawText(mSections[i], mIndexbarRect.left + paddingLeft , mIndexbarRect.top + mIndexbarMargin + sectionHeight * i + paddingTop - indexPaint.ascent(), indexPaint); } } } /** * 索引条的触摸事件 * * @param ev * @return */ public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: // If down event occurs inside index bar region, start indexing // 索引条可见,且按下事件发生在索引条范围内 if (mState != STATE_HIDDEN && contains(ev.getX(), ev.getY())) { setState(STATE_SHOWN); //索引条设置为显示状态 // It demonstrates that the motion event started from index bar mIsIndexing = true; //正在索引 // Determine which section the point is in, and move the list to that section mCurrentSection = getSectionByPoint(ev.getY());//获取当前点击的索引条中字符的位置 mListView.setSelection(mIndexer.getPositionForSection(mCurrentSection));//列表定位到指定位置 return true; } break; case MotionEvent.ACTION_MOVE: if (mIsIndexing) { // If this event moves inside index bar // 在索引条上滑动 if (contains(ev.getX(), ev.getY())) { // Determine which section the point is in, and move the list to that section mCurrentSection = getSectionByPoint(ev.getY());// 获取当前手指触摸点对应的索引条中字符的位置 mListView.setSelection(mIndexer.getPositionForSection(mCurrentSection));// 列表定位到指定位置 } return true; } break; case MotionEvent.ACTION_UP: if (mIsIndexing) { mIsIndexing = false;// 设置mIsIndexing为false,表示正在索引取消 mCurrentSection = -1;// 将当前索引字符位置设置为-1,隐藏预览图 } if (mState == STATE_SHOWN) setState(STATE_HIDING);// 渐退索引条 break; } return false; } /** * 索引条位置重定位 * * @param w * @param h * @param oldw * @param oldh */ public void onSizeChanged(int w, int h, int oldw, int oldh) { mListViewWidth = w; mListViewHeight = h; mIndexbarRect = new RectF(w - mIndexbarMargin - mIndexbarWidth , mIndexbarMargin , w - mIndexbarMargin , h - mIndexbarMargin); } /** * 显示索引条 */ public void show() { if (mState == STATE_HIDDEN) setState(STATE_SHOWING); else if (mState == STATE_HIDING) setState(STATE_HIDING); } /** * 隐藏索引条 */ public void hide() { if (mState == STATE_SHOWN) setState(STATE_HIDING); } /** * 设置监听器 * * @param adapter */ public void setAdapter(Adapter adapter) { if (adapter instanceof SectionIndexer) { mIndexer = (SectionIndexer) adapter; mSections = (String[]) mIndexer.getSections(); } } /** * 设置索引条状态 * * @param state */ private void setState(int state) { if (state < STATE_HIDDEN || state > STATE_HIDING) return; mState = state; switch (mState) { case STATE_HIDDEN: // Cancel any fade effect mHandler.removeMessages(0); break; case STATE_SHOWING: // Start to fade in mAlphaRate = 0; fade(0); break; case STATE_SHOWN: // Cancel any fade effect mHandler.removeMessages(0); break; case STATE_HIDING: // Start to fade out after three seconds mAlphaRate = 1; fade(3000); break; } } /** * 判断该点是否在索引条范围内,包括索引条右边距 * * @param x * @param y * @return */ public boolean contains(float x, float y) { // Determine if the point is in index bar region, which includes the right margin of the bar return (x >= mIndexbarRect.left && y >= mIndexbarRect.top && y <= mIndexbarRect.top + mIndexbarRect.height()); } /** * 根据手指触摸位置返回对应索引字符位置 * * @param y * @return */ private int getSectionByPoint(float y) { if (mSections == null || mSections.length == 0) return 0; if (y < mIndexbarRect.top + mIndexbarMargin) return 0; if (y >= mIndexbarRect.top + mIndexbarRect.height() - mIndexbarMargin) return mSections.length - 1; return (int) ((y - mIndexbarRect.top - mIndexbarMargin) / ((mIndexbarRect.height() - 2 * mIndexbarMargin) / mSections.length)); } private void fade(long delay) { mHandler.removeMessages(0); mHandler.sendEmptyMessageAtTime(0, SystemClock.uptimeMillis() + delay); } private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (mState) { case STATE_SHOWING: // 开始渐进显示索引条 // Fade in effect mAlphaRate += (1 - mAlphaRate) * 0.2; if (mAlphaRate > 0.9) { mAlphaRate = 1; setState(STATE_SHOWN); } mListView.invalidate(); fade(10); break; case STATE_SHOWN: // 取消渐进效果 // If no action, hide automatically setState(STATE_HIDING); break; case STATE_HIDING: // 3秒后开始渐退 // Fade out effect mAlphaRate -= mAlphaRate * 0.2; if (mAlphaRate < 0.1) { mAlphaRate = 0; setState(STATE_HIDDEN); } mListView.invalidate(); fade(10); break; } } };}
整个文件的变量及方法说明如注释所示。 IndexableListView
IndexableListView.java代码:
/* * Copyright 2011 woozzu * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.woozzu.android.widget;import android.content.Context;import android.graphics.Canvas;import android.util.AttributeSet;import android.view.GestureDetector;import android.view.MotionEvent;import android.widget.ListAdapter;import android.widget.ListView;public class IndexableListView extends ListView { private boolean mIsFastScrollEnabled = false;//快速滚动设置 private IndexScroller mScroller = null;//索引条实例 private GestureDetector mGestureDetector = null; //手势监听 public IndexableListView(Context context) { super(context); } public IndexableListView(Context context, AttributeSet attrs) { super(context, attrs); } public IndexableListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean isFastScrollEnabled() { return mIsFastScrollEnabled; } @Override public void setFastScrollEnabled(boolean enabled) { mIsFastScrollEnabled = enabled; if (mIsFastScrollEnabled) { if (mScroller == null) mScroller = new IndexScroller(getContext(), this); //设置快速滚动,初始化索引条 } else { //否则隐藏索引条 if (mScroller != null) { mScroller.hide(); mScroller = null; } } } @Override public void draw(Canvas canvas) { super.draw(canvas); // Overlay index bar if (mScroller != null) mScroller.draw(canvas); //绘制索引条(在索引条不为null时) } /** * 触摸事件 * * @param ev * @return */ @Override public boolean onTouchEvent(MotionEvent ev) { // Intercept ListView's touch event if (mScroller != null && mScroller.onTouchEvent(ev)) { //若1.索引条类不为null 2.索引条可见且触摸事件发生在索引条范围内,则if语句中为true,onTouchEvent不再向下执行 return true; } if (mGestureDetector == null) { //若索引条不可见或触摸事件不是发生在索引条范围内 mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { // If fling happens, index bar shows if (mScroller != null) mScroller.show(); //显示索引条 return super.onFling(e1, e2, velocityX, velocityY); } }); } mGestureDetector.onTouchEvent(ev); return super.onTouchEvent(ev); } /** * 拦截触摸事件 * * @param ev * @return */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mScroller.contains(ev.getX(), ev.getY())) return true; return super.onInterceptTouchEvent(ev); } /** * 设置adapter * * @param adapter */ @Override public void setAdapter(ListAdapter adapter) { super.setAdapter(adapter); if (mScroller != null) mScroller.setAdapter(adapter); } /** * View大小改变时触发 * * @param w * @param h * @param oldw * @param oldh */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (mScroller != null) mScroller.onSizeChanged(w, h, oldw, oldh); }}
IndexableListView解释同样见注释。 onInterceptTouchEvent及onTouchEvent的详细用法可以见http://blog.csdn.net/lvxiangan/article/details/9309927
1、onInterceptTouchEvent()是用于处理事件(类似于预处理,当然也可以不处理)并改变事件的传递方向,也就是决定是否允许Touch事件继续向下(子控件)传递,一但返回True(代表事件在当前的viewGroup中会被处理),则向下传递之路被截断(所有子控件将没有机会参与Touch事件),同时把事件传递给当前的控件的onTouchEvent()处理;返回false,则把事件交给子控件的onInterceptTouchEvent()
2、onTouchEvent()用于处理事件,返回值决定当前控件是否消费(consume)了这个事件,也就是说在当前控件在处理完Touch事件后,是否还允许Touch事件继续向上(父控件)传递,一但返回True,则父控件不用操心自己来处理Touch事件。返回true,则向上传递给父控件(注:可能你会觉得是否消费了有关系吗,反正我已经针对事件编写了处理代码?答案是有区别!比如ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN,如果你没有消费ACTION_DOWN,那么系统会认为ACTION_DOWN没有发生过,所以ACTION_MOVE或者ACTION_UP就不能被捕获。)
对于此控件
- 首先如果捕获到手势,进入IndexableListView的onInterceptTouchEvent,若手势滑动到右边控制条,则进入IndexableListView的onTouchEvent事件中。如果此时索引条可见,则返回true不再向下执行,mScroll.onTouchEvent就可以执行IndexScroller中的动作了
- 如果此时滚动条不可见或触摸事件不是发生在索引条范围内,则将索引条显示出来
以上就是IndexableListView的代码分析,下篇将会讲解获取手机通讯录信息的相关知识。
更多相关文章
- android事件处理
- EditText的属性!
- Android(安卓)调用相册 拍照 实现系统控件缩放 切割图片
- EditText
- Android(安卓)样式和主题,style&theme
- Android软键盘弹出时把布局顶上去,控件乱套解决方法
- Android(安卓)监听EditText文本输入 EditText监听事和输入事件
- Android(安卓)ConstraintLayout 约束布局
- Android热插拔事件处理详解