上一篇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就不能被捕获。)


对于此控件

  1. 首先如果捕获到手势,进入IndexableListView的onInterceptTouchEvent,若手势滑动到右边控制条,则进入IndexableListView的onTouchEvent事件中。如果此时索引条可见,则返回true不再向下执行,mScroll.onTouchEvent就可以执行IndexScroller中的动作了
  2. 如果此时滚动条不可见或触摸事件不是发生在索引条范围内,则将索引条显示出来


以上就是IndexableListView的代码分析,下篇将会讲解获取手机通讯录信息的相关知识。


更多相关文章

  1. android事件处理
  2. EditText的属性!
  3. Android(安卓)调用相册 拍照 实现系统控件缩放 切割图片
  4. EditText
  5. Android(安卓)样式和主题,style&theme
  6. Android软键盘弹出时把布局顶上去,控件乱套解决方法
  7. Android(安卓)监听EditText文本输入 EditText监听事和输入事件
  8. Android(安卓)ConstraintLayout 约束布局
  9. Android热插拔事件处理详解

随机推荐

  1. 搭建一个短视频APP可以用云服务器吗?如何
  2. JS高级ES6的6种继承方式
  3. webpack 配置文件webpack.config.js
  4. Python 函数装饰器应用教程
  5. 意派Epub360丨双12来袭,跟着淘宝学营销,海
  6. JavaScript高级之自定义异常
  7. Spring Security 核心过滤器链讲解
  8. php hello world
  9. Android(安卓)23种设计模式
  10. Android:theme