android通讯录实例(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/specialshoot/article/details/50786358

上一篇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的代码分析,下篇将会讲解获取手机通讯录信息的相关知识。


猜你喜欢

转载自blog.csdn.net/specialshoot/article/details/50786358
今日推荐