Android 自定义控件实现ListView索引

来源:互联网 发布:linux下怎么看java目录 编辑:程序博客网 时间:2024/04/30 02:32

这里写图片描述
实现listview 的索引 中间的预览文本因为截图水平有限没有截出来。。

源代码下载地址:

实现该方法需要重写lisetview 控件。实现步骤如下
1. 新建一个数组 用于存放右侧的a-z 的26个字母 然后写自定义空间IndexLisetview
2.IndexLisetview继承Lisetview 然后重写setFastScrollEnabled 、 draw、onTouchEvent、setAdapter、onSizeChanged 等方法。
3.在IndexScroller类实现绘制索引条和绘制预览文本的方法。并实现索引条的显示和隐藏的方法。
4.因为索引匹配,所以需要一个字符串匹配的算法。

部分代码如下
1.定义IndexLisetview

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)                //创建IndexScroller对象                mScroller = new IndexScroller(getContext(), this);        } else {            if (mScroller != null) {                mScroller.hide();                mScroller = null;            }        }    }    @Override    public void draw(Canvas canvas) {   //用于绘制右侧的索引条        super.draw(canvas);//绘制listview 原来的东西        //绘制右侧的索引条        if (mScroller != null)            mScroller.draw(canvas);    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        //如果mscroller自己处理触摸事件,该方法返回true        if (mScroller != null && mScroller.onTouchEvent(ev))            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 (mScroller != null)                        mScroller.show();                    return super.onFling(e1, e2, velocityX, velocityY);                }            });        }        mGestureDetector.onTouchEvent(ev);        return super.onTouchEvent(ev);    }    @Override    public void setAdapter(ListAdapter adapter) {        super.setAdapter(adapter);        if (mScroller != null)            mScroller.setAdapter(adapter);    }    @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);    }}

2.绘制索引条和预览文本

public class IndexScroller {    private float mIndexbarWidth;//索引條寬度    private float mIndexbarMargin;//索引條距離右邊距離    private float mPreviewPadding;//中心現實的預覽文版距離四周的長度    private float mDensity;//当前的屏幕密度和标准的屏幕密度(160)的商  dp sp会随字体变化而变化      private float mScaledDensity;//当前的屏幕密度和标准的屏幕密度(160)的商(设置字体尺寸)    private float mAlphaRate;//透明度(用于显示和隐藏索引条)0-1之间    private int mState = STATE_HIDDEN;//索引条的状态    private int mListViewWidth;    private int mListViewHeight;    private int mCurrentSection = -1;    private boolean mIsIndexing = false;    private ListView mListView = null;    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;        setAdapter(mListView.getAdapter());          //根據屏幕密度技術索引條的寬度(單位像素)索引條會跟隨屏幕密度變化        mIndexbarWidth = 20 * mDensity;        mIndexbarMargin = 10 * mDensity;        mPreviewPadding = 5 * mDensity;    }      //繪製索引條和預覽文本    public void draw(Canvas canvas) {         //绘制索引条,包括索引条的背景和文版;          //绘制预览文版和背景        if (mState == STATE_HIDDEN)            //如果索引条隐藏不进行绘制            return;        //设置索引背景的绘制属性        Paint indexbarPaint = new Paint();        indexbarPaint.setColor(Color.BLACK);        indexbarPaint.setAlpha((int) (64 * mAlphaRate));        indexbarPaint.setAntiAlias(true);          //绘制索引条(4个交都是圆角举行)        canvas.drawRoundRect(mIndexbarRect, 5 * mDensity, 5 * mDensity, indexbarPaint);        //绘制sections        if (mSections != null && mSections.length > 0) {            //绘制预览文本和背景            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]);                  // 下端descent 正值, 上端ascent 负值                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;            for (int i = 0; i < mSections.length; 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);            }        }    }    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;                mCurrentSection = -1;            }            if (mState == STATE_SHOWN)                setState(STATE_HIDING);            break;        }        return false;    }    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);    }    public void setAdapter(Adapter adapter) {        if (adapter instanceof SectionIndexer) {            mIndexer = (SectionIndexer) adapter;            mSections = (String[]) mIndexer.getSections();        }    }    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;        }    }    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());    }    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:                // Fade out effect                mAlphaRate -= mAlphaRate * 0.2;                if (mAlphaRate < 0.1) {                    mAlphaRate = 0;                    setState(STATE_HIDDEN);                }                mListView.invalidate();                fade(10);                break;            }        }    };}

3.实现A-z字母数组是适配和字符串匹配算法的调用

private class ContentAdapter extends ArrayAdapter<String> implements SectionIndexer {        private String mSections = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";        public ContentAdapter(Context context, int textViewResourceId,                List<String> objects) {            super(context, textViewResourceId, objects);        }        @Override        public int getPositionForSection(int section) {            // 根据Section来获得位置Position            //Y没有对应的items就往前查X直到查到对应的为止            for (int i = section; i >= 0; i--) {                for (int j = 0; j < getCount(); j++) {                    if (i == 0) {                        //查询数字                        for (int k = 0; k <= 9; k++) {                            if (StringMatcher.match(String.valueOf(getItem(j).charAt(0)), String.valueOf(k)))                                return j;                        }                    } else {//查询字母                        if (StringMatcher.match(String.valueOf(getItem(j).charAt(0)), String.valueOf(mSections.charAt(i))))                            return j;                    }                }            }            return 0;        }        @Override        public int getSectionForPosition(int position) {            // 根据Position来获得位置Section            return 0;        }        @Override        public Object[] getSections() {            // 存储数组元素 列表右侧的ABCD            String[] sections = new String[mSections.length()];            //将每个section作为但的数组袁术放到sections中            for (int i = 0; i < mSections.length(); i++)                //从section中获得每一个字符                sections[i] = String.valueOf(mSections.charAt(i));            return sections;        }    }}
0 0
原创粉丝点击