自定义View实现联系人快速查找

本文介绍了一种在Android应用中实现快速索引滚动的方法,包括绘制A-Z索引、处理触摸事件、拼音转换和数据分组等功能。通过自定义View实现快速定位,并结合ListView展示数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个功能也经常用到比如:微信好友列表, 联系人通讯录, 应用管理, 文件管理等

效果图:
这里写图片描述

实现步骤:
- 绘制 A-Z.的索引,处理Touch事件
- 根据回调回调显示当前索引
- 汉字转换成拼音,将数据进行分组
- 在ListView中使用自定义控件

自定义View代码:

public class QuickIndexView extends View {

    int touchIndex = -1;
    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;
    private int cellWidth;
    private float cellHeight;
    private OnLetterIndexChangeListener mLetterIndexChangeListener;


    public interface OnLetterIndexChangeListener {
        void onLetterIndexChange(String letter);
    }

    public void setOnLetterIndexChangeListener(OnLetterIndexChangeListener mLetterIndexChangeListener) {
        this.mLetterIndexChangeListener = mLetterIndexChangeListener;
    }

    public QuickIndexView(Context context) {
        this(context, null);
    }

    public QuickIndexView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public QuickIndexView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // Paint.ANTI_ALIAS_FLAG 字体抗锯齿
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.GRAY);
        // 粗体
        mPaint.setTypeface(Typeface.DEFAULT_BOLD);
        mPaint.setTextSize(DensityUtils.dp2px(getContext(), 12));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        for (int i = 0; i < LETTERS.length; i++) {
            String letter = LETTERS[i];
            // 计算字体放置x坐标
            int x = (int) (cellWidth / 2.0f - mPaint.measureText(letter) / 2.0f);
            // 获取文本的高度
            Rect rect = new Rect();// 矩形
            mPaint.getTextBounds(letter, 0, letter.length(), rect);
            int letterHeight = rect.height();
            // 每个text单元格增加高度
            int y = (int) (cellHeight / 2.0f + letterHeight / 2.0f + i * cellHeight);
            // 根据按下的字母, 设置画笔颜色
            mPaint.setColor(touchIndex == i ? Color.BLACK : Color.GRAY);
            canvas.drawText(letter, x, y, mPaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int index = 0;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 得到index值,总高度/每个单元格的高度
                index = (int) (event.getY() / cellHeight);
                if (index >= 0 && index < LETTERS.length) {
                    // 判断是否同个一index,是就不再显示
                    if (touchIndex != index) {
                        if (mLetterIndexChangeListener != null) {
                            mLetterIndexChangeListener.onLetterIndexChange(LETTERS[index]);
                        }
                        // 记录当前index
                        touchIndex = index;
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                index = (int) (event.getY() / cellHeight);
                if (index >= 0 && index < LETTERS.length) {
                    if (touchIndex != index) {
                        if (mLetterIndexChangeListener != null) {
                            mLetterIndexChangeListener.onLetterIndexChange(LETTERS[index]);
                        }
                        touchIndex = index;
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                touchIndex = -1;
                break;
        }
        invalidate();
        return true;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 获取单元格的宽
        cellWidth = getMeasuredWidth();
        int mHeight = getMeasuredHeight();
        // 获取单元格的高
        cellHeight = mHeight * 1.0f / LETTERS.length;
    }
}

MainActivity:

public class MainActivity extends AppCompatActivity {

    private ArrayList<Person> datas;
    private TextView tv_center;
    private Animation alpha_show;
    private Animation alpha_die;
    private boolean animationState = true;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        alpha_show = AnimationUtils.loadAnimation(this, R.anim.alpha_show);
        alpha_die = AnimationUtils.loadAnimation(this, R.anim.alpha_die);
        datas = new ArrayList<>();
        QuickIndexView qi_view = (QuickIndexView) findViewById(R.id.qi_view);
        tv_center = (TextView) findViewById(R.id.tv_center);
        final ListView lv_itme = (ListView) findViewById(R.id.lv_itme);
        qi_view.setOnLetterIndexChangeListener(new QuickIndexView.OnLetterIndexChangeListener() {
            @Override
            public void onLetterIndexChange(String letter) {
                showCenterIndex(letter);
                for (int i = 0; i < datas.size(); i++) {
                    Person person = datas.get(i);
                    String index = person.getPinYin().charAt(0)+"";
                    if (TextUtils.equals(index,letter)){
                        lv_itme.setSelection(i);
                        break;
                    }
                }
            }
        });
        datas = fillAndSortData();
        lv_itme.setAdapter(new MainAdapter(datas, getApplicationContext()));

    }
    private Handler mHandler = new Handler();
    private void showCenterIndex(String letter) {
        tv_center.setText(letter);
        if (animationState){
            tv_center.startAnimation(alpha_show);
            animationState = false;
        }

        tv_center.setVisibility(View.VISIBLE);
        mHandler.removeCallbacksAndMessages(null);
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                animationState = true;
                tv_center.startAnimation(alpha_die);
                tv_center.setVisibility(View.GONE);
            }
        },1000);
    }


    private ArrayList<Person> fillAndSortData() {
        ArrayList<Person> Persons = new ArrayList<>(); 
        for (int i = 0; i < Cheeses.NAMES.length; i++) {
            String name = Cheeses.NAMES[i];
            Persons.add(new Person(name));
        }
        Collections.sort(Persons);
        return Persons;
    }
}

Adapter:

public class MainAdapter extends BaseAdapter {

    private ArrayList<Person> datas;
    Context context;

    public MainAdapter(ArrayList<Person> datas, Context context) {
        this.datas = datas;
        this.context = context;
    }

    @Override
    public int getCount() {
        return datas.size();
    }

    @Override
    public Object getItem(int position) {
        return datas.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder mHolder = null;
        if (convertView == null) {
           mHolder = new ViewHolder();
        }else{
            mHolder = (ViewHolder) convertView.getTag();
        }

        String str = null;
        String index = datas.get(position).getPinYin().charAt(0) + "";
        if (position == 0) {
            str = index;
        } else {
            //获取上一个inedx判断时否相等
            String preIndex = datas.get(position - 1).getPinYin().charAt(0) + "";
            if (!TextUtils.equals(preIndex, index)) {
                str = index;
            }
        }

        // 根据str是否为空缺定是否显示
        mHolder.lv_index.setVisibility(str == null?View.GONE: View.VISIBLE);
        mHolder.lv_index.setText(index);
        mHolder.lv_name.setText(datas.get(position).getName().trim());
        return mHolder.getView();
    }


    class ViewHolder {

        public TextView lv_index;
        public TextView lv_name;
        public View view;

        public ViewHolder() {
            view = View.inflate(context, R.layout.list_itme, null);
            lv_index = (TextView) view.findViewById(R.id.lv_index);
            lv_name = (TextView) view.findViewById(R.id.lv_name);
            view.setTag(this);
        }
        public View getView() {
            return view;
        }
    }
}

获取拼音:

public class PinYinHelper {

    public static String getPinyin(String text) {
        HanyuPinyinOutputFormat hFormat = new HanyuPinyinOutputFormat();
        // 大写
        hFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE);
        // 没有声调
        hFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
        char[] charArray = text.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < charArray.length; i++) {
            char c = charArray[i];
            // 空格跳过
            if (Character.isWhitespace(c)) {
                continue;
            }
            if (c >= -127 && c < 128) {
                // 不是汉字
                sb.append(c);
            } else {
                String pinYin = "";
                try {
                    pinYin = PinyinHelper.toHanyuPinyinStringArray(c, hFormat)[0];
                    sb.append(pinYin);
                } catch (BadHanyuPinyinOutputFormatCombination e) {
                    e.printStackTrace();
                    sb.append(pinYin);
                }
            }
        }

        return sb.toString();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值