您的位置:首页 > 移动开发 > Android开发

Android实现快速索引详解

2017-06-20 10:23 375 查看
画字母

        要实现这个效果, 先得把右侧的字母条画出来, 这里我们写个类, 继承自 View, 由于其内部不需要包含其他布局, 所以继承 View 即可, 无需继承 ViewGroup.

[java] view
plain copy

public class QuickIndexBar extends View {  

    private static final String TAG = "TAG";  

    private static final String[] LETTERS = new String[]{  

        "A", "B", "C", "D", "E", "F",  

        "G", "H", "I", "J", "K", "L",  

        "M", "N", "O", "P", "Q", "R",  

        "S", "T", "U", "V", "W", "X",  

        "Y", "Z"};  

    private Paint mPaint;  

      

    public interface OnLetterChangeListener{  

        void OnLetterChange(String letter);  

    }  

    public QuickIndexBar(Context context) {  

        this(context, null);  

    }  

    public QuickIndexBar(Context context, AttributeSet attrs) {  

        this(context, attrs, 0);  

    }  

    public QuickIndexBar(Context context, AttributeSet attrs, int defStyle) {  

        super(context, attrs, defStyle);   

        // 初始化画笔  

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  

        mPaint.setColor(Color.WHITE);  

        mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, getResources().getDisplayMetrics()));  

        mPaint.setTypeface(Typeface.DEFAULT_BOLD);  

    }    

}  

既然要画字母, 就要有画笔, 这里在构造方法里完成画笔的初始化, 创建一个抗锯齿, 颜色为白色, 大小12sp, 粗体的画笔. 有了画笔, 就要开始画了, 画法如图所示。



        这里面要注意的是, 使用 Canvas 画文字的时候, 是从左下角开始的.

[java] view
plain copy

@Override  

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {  

        super.onSizeChanged(w, h, oldw, oldh);  

        mHeight = getMeasuredHeight();  

        mCellHeight = mHeight * 1.0f / LETTERS.length;  

        mCellWidth = getMeasuredWidth();  

    }    

    @Override  

    protected void onDraw(Canvas canvas) {  

        // 绘制字母  

        for (int i = 0; i < LETTERS.length; i++) {  

            String text = LETTERS[i];  

            // 求x坐标  

            int x = (int) (mCellWidth / 2.0f - mPaint.measureText(text) / 2.0f);  

            // 求y坐标  

            // 格子高度的一半 + 文字高度的一半 + 其上边所有格子高度  

            Rect bounds = new Rect();  

            mPaint.getTextBounds(text, 0, text.length(), bounds);  

            int y = (int) (mCellHeight / 2.0f + bounds.height() / 2.0f + mCellHeight * i);  

            canvas.drawText(text, x, y, mPaint);  

        }  

    }  

        这样一来, 字母就画出来了, 如果想要更自由一些的话, 可以使用自定义属性传入字体颜色. 

触摸事件和回调

        界面效果有了, 接下来就是处理触摸事件以及回调了, 处理触摸事件肯定是重写 onTouchEvent 方法了, 回调的话, 定义一个回调接口, 提供 get/set 方法, 在 onTouchEvent 相应的位置调用. onTouchEvent 代码如下:

[java] view
plain copy

@Override  

    public boolean onTouchEvent(MotionEvent event) {  

        switch (MotionEventCompat.getActionMasked(event)) {  

            case MotionEvent.ACTION_DOWN:  

                // 根据y值获取当前触摸到的字母  

                float y = event.getY();  

                int index = (int) (y / mCellHeight);  

                // 如果字母索引发生变化  

                if(index != touchIndex){  

                    if(index >= 0 && index < LETTERS.length){  

                        Log.d(TAG, LETTERS[index]);  

                        if(mLetterChangeListener != null){  

                            // 执行回调  

                            mLetterChangeListener.OnLetterChange(LETTERS[index]);  

                        }  

                    }  

                    touchIndex  = index;  

                }  

                break;  

            case MotionEvent.ACTION_MOVE:  

                // 根据y值获取当前触摸到的字母  

                int i = (int) (event.getY() / mCellHeight);  

                // 如果字母索引发生变化  

                if(i != touchIndex){  

                    if(i >= 0 && i < LETTERS.length){  

                        Log.d(TAG, LETTERS[i]);  

                        if(mLetterChangeListener != null){  

                            mLetterChangeListener.OnLetterChange(LETTERS[i]);  

                        }  

                    }  

                    touchIndex  = i;  

                }  

                break;  

            case MotionEvent.ACTION_UP:  

                // 恢复默认索引值  

                touchIndex = -1;  

                break;  

            default:  

                break;  

        }  

        invalidate();  

        return true;  

    }  

回调接口:

[java] view
plain copy

public interface OnLetterChangeListener{  

    void OnLetterChange(String letter);  

}  

// 字母变化监听  

private OnLetterChangeListener mLetterChangeListener;  

public OnLetterChangeListener getLetterChangeListener() {  

    return mLetterChangeListener;  

}  

public void setLetterChangeListener(  

        OnLetterChangeListener mLetterChangeListener) {  

    this.mLetterChangeListener = mLetterChangeListener;  

}  

ListView 的处理
首先我们要获取一个所有名字的集合, 并且对它按照拼音顺序排序.

[java] view
plain copy

private void fillAndSort(ArrayList<Friend> names) {  

        for (int i = 0; i < NAMES.length; i++) {  

            names.add(new Friend(NAMES[i]));  

        }  

        Collections.sort(names);  

    }    

Friend 类如下:  

public class Friend implements Comparable<Friend>{  

    private String name;  

    private String pinyin;  

    public Friend(String name) {  

        super();  

        this.name = name;  

        // 获取拼音  

        pinyin = PinyinUtils.getPinyin(name);  

    }  

    public String getName() {  

        return name;  

    }  

    public void setName(String name) {  

        this.name = name;  

    }  

    public String getPinyin() {  

        return pinyin;  

    }  

    public void setPinyin(String pinyin) {  

        this.pinyin = pinyin;  

    }  

    @Override  

    public int compareTo(Friend another) {  

        return pinyin.compareTo(another.getPinyin());  

    }  

}  

        那么接下来就是 ListView 的 Adapter 了, 这里要注意一点就是, 相同字母开头的名字, 只有第一个显示, 其他的不显示.

[java] view
plain copy

@Override  

    public View getView(int position, View convertView, ViewGroup parent) {  

        View view = convertView;  

        if(convertView == null){  

            view = View.inflate(mContext, R.layout.item_list, null);  

        }  

        ViewHolder mViewHolder = ViewHolder.getHolder(view);  

        Friend friend = names.get(position);  

        // 跟上一个进行比较,如果不同,则显示。  

        String letter = null;  

        String currentLetter = friend.getPinyin().charAt(0) + "";  

        if(position == 0){  

            // 第一个人直接显示  

            letter = currentLetter;  

        }else {  

            // 获取上一个人的拼音  

            String preLetter = names.get(position - 1).getPinyin().charAt(0) + "";  

            if(!TextUtils.equals(preLetter, currentLetter)){  

                letter = currentLetter;  

            }  

        }  

        mViewHolder.mIndex.setVisibility(letter == null ? View.GONE : View.VISIBLE);  

        if(letter != null){  

            mViewHolder.mIndex.setText(letter);  

        }  

        mViewHolder.mName.setText(friend.getName());  

        return view;  

    }  

每个 item 的布局如下:

[html] view
plain copy

<?xml version="1.0" encoding="utf-8"?>  

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  

    android:layout_width="match_parent"  

    android:layout_height="match_parent"  

    android:orientation="vertical" >  

    <TextView  

        android:id="@+id/tv_index"  

        android:layout_width="match_parent"  

        android:layout_height="40dp"  

        android:background="#555555"  

        android:gravity="center_vertical"  

        android:paddingLeft="20dp"  

        android:visibility="gone"  

        android:text="A"  

        android:textColor="#ffffff"  

        android:textSize="20sp" />   

    <TextView  

        android:id="@+id/tv_name"  

        android:layout_width="match_parent"  

        android:layout_height="50dp"  

        android:gravity="center_vertical"  

        android:paddingLeft="20dp"  

        android:text="宋江"  

        android:textColor="#000000"  

        android:textSize="22sp" />  

</LinearLayout>  

        也就是说, 其实每个条目都是有个字母索引, 有个名字, 只是首字母相同的名字, 只有第一个显示索引。最后就是在索引条上滑动的时候移动到 ListView 相应的位置了, 这个就是实现它提供的回调:

[java] view
plain copy

QuickIndexBar mQuickIndexBar = (QuickIndexBar) findViewById(R.id.quickIndex);  

mQuickIndexBar.setLetterChangeListener(new OnLetterChangeListener() {  

    @Override  

    public void OnLetterChange(String letter) {  

        Utils.showToast(getApplicationContext(), letter);  

        // 执行ListView的定位方法  

        for (int i = 0; i < names.size(); i++) {  

            Friend friend = names.get(i);  

            String l =friend.getPinyin().charAt(0) + "";  

            if(TextUtils.equals(letter, l)){  

                // 中断循环,快速定位  

                mListView.setSelection(i);  

                break;  

            }  

        }  

          

    }  

});  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: