快速索引

本文介绍了一种自定义快速索引条的实现方法,包括绘制索引字母表、处理触摸事件以及与ListView联动等功能。该索引条可根据触摸位置显示相应的字母,使用户能够快速定位到列表中的特定项。

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

自定义控件的绘制:

package com.it.quickindex.ui;

import com.it.quickindex.util.Cheeses;
import com.it.quickindex.util.Utils;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * 快速索引条
 * @author poplar
 *
 */
public class QuickIndexBar extends View {

    public 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 paint;

    private float cellHeight; // 单元格高度
    private int cellWidth; // 单元格宽度

    private OnLetterChangeListener mOnLetterChangeListener;

    /**
     * 字母更新监听
     * @author poplar
     *
     */
    public interface OnLetterChangeListener{
        void onLetterChange(String letter);
    }
    /**
     * 设置监听器
     * @param mOnLetterChangeListener
     */
    public void setOnLetterChangeListener(OnLetterChangeListener mOnLetterChangeListener){
        this.mOnLetterChangeListener = mOnLetterChangeListener;
    }

    /**
     * 构造方法
     * @param context
     */
    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);
        //抗锯齿
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.WHITE);
        paint.setTextSize(25);
        //加粗
        paint.setTypeface(Typeface.DEFAULT_BOLD);

    }
    /**
     * 绘制索引字母表
     */
    @Override
    protected void onDraw(Canvas canvas) {

        // 把所有字母画到画板上
        for (int i = 0; i < LETTERS.length; i++) {
            String letter = LETTERS[i];

            // 计算水平坐标(字母控件左侧坐标)
            int x = (int) (cellWidth * 0.5f - paint.measureText(letter) * 0.5f);

            // 计算垂直坐标(左下角为textView的坐标)
            // 将一个文本的指定区域放置到 bounds的矩形对象中,并将值封装给bound
            Rect bounds = new Rect();
            paint.getTextBounds(letter, 0, letter.length(), bounds);
            int y = (int) (cellHeight * 0.5f + bounds.height() * 0.5f + cellHeight * i);

            // 设置画笔颜色, 如果是被触摸的, 设置灰色
            paint.setColor(i == currentIndex ? Color.GRAY : Color.WHITE);

            // 绘制文本
            canvas.drawText(letter, x, y, paint);
        }

    }

    int currentIndex = -1;
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int index;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 根据当前的y值, 计算得到字母的索引
                index = (int) (event.getY() / cellHeight);

                if(index != currentIndex){
                    // 如果发生了变化, 才走到这里边
                    if(index >= 0 && index < LETTERS.length){
                        // 获取到手指按下的字母
                        System.out.println(LETTERS[index]);
                        if(mOnLetterChangeListener != null){
                            mOnLetterChangeListener.onLetterChange(LETTERS[index]);
                        }
                    }
                    currentIndex = index;
                }


                break;
            case MotionEvent.ACTION_MOVE:
                index = (int) (event.getY() / cellHeight);

                if(index != currentIndex) {
                    // 如果发生了变化, 才走到这里边
                    if(index >= 0 && index < LETTERS.length){
                        // 获取到手指按下的字母
                        System.out.println(LETTERS[index]);

                        if(mOnLetterChangeListener != null){
                            mOnLetterChangeListener.onLetterChange(LETTERS[index]);
                        }
                    }
                    currentIndex = index;
                }

                break;
            case MotionEvent.ACTION_UP:
                currentIndex = -1;
                break;
            default:
                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;

    }
}

主页布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <ListView
        android:id="@+id/lv_haohan"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </ListView>

    <com.itheima57.quickindex.ui.QuickIndexBar
        android:id="@+id/quickIndexBar"
        android:layout_width="30dp"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:background="#FF0000" />

    <TextView
        android:id="@+id/tv_center"
        android:visibility="gone"
        android:layout_width="140dp"
        android:layout_height="80dp"
        android:layout_centerInParent="true"
        android:background="@drawable/shape_bg_index"
        android:gravity="center"
        android:text="A"
        android:textColor="#FFFFFF"
        android:textSize="28sp" />

</RelativeLayout>

MainActivity:

package com.it.quickindex;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.View;
import android.view.Window;
import android.widget.ListView;
import android.widget.TextView;

import com.itheima57.quickindex.adapter.HaoHanAdapter;
import com.itheima57.quickindex.domain.HaoHan;
import com.itheima57.quickindex.ui.QuickIndexBar;
import com.itheima57.quickindex.ui.QuickIndexBar.OnLetterChangeListener;
import com.itheima57.quickindex.util.Cheeses;

public class MainActivity extends Activity {

    private TextView tv_center;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        //获取索引弹窗控件
        tv_center = (TextView) findViewById(R.id.tv_center);

        //获取联系人列表ListView控件
        final ListView lv_haohan = (ListView) findViewById(R.id.lv_haohan);

        final List<HaoHan> persons = new ArrayList<HaoHan>();

        // 填充并排序
        fillAndSortData(persons);

        lv_haohan.setAdapter(new HaoHanAdapter(this, persons));

        QuickIndexBar quickIndexBar = (QuickIndexBar) findViewById(R.id.quickIndexBar);

        // 给自定义索引设置监听(选择右侧索引,左侧listView跳转到相应的位置)
        quickIndexBar.setOnLetterChangeListener(new OnLetterChangeListener() {

            @Override
            public void onLetterChange(String letter) {
//              Utils.showToast(getApplicationContext(), "letter: " + letter);

                showCenterLetter(letter);

                // 将ListView调到 , 首次出现 letter 的条目
                for (int i = 0; i < persons.size(); i++) {
                    String l = persons.get(i).getPinyin().charAt(0) + "";
                    if(TextUtils.equals(l, letter)){
                        // 找到了, 跳转到指定位置
                        lv_haohan.setSelection(i);
                        break;
                    }
                }
            }
        });
    }

    private Handler mHandler = new Handler();
    protected void showCenterLetter(String letter) {
        tv_center.setText(letter);
        tv_center.setVisibility(View.VISIBLE);

        // 移除刚刚所有发出的消息(防止之前的消息还没显示结束再显示新消息出现闪屏)
        mHandler.removeCallbacksAndMessages(null);
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                tv_center.setVisibility(View.GONE);
            }
        }, 1500);
    }

    /**
     * 将数据封装进集合并且排序
     * @param persons
     */
    private void fillAndSortData(List<HaoHan> persons) {
        HaoHan haoHan;
        for (int i = 0; i < Cheeses.NAMES.length; i++) {
            String name = Cheeses.NAMES[i];
            haoHan = new HaoHan(name);
            persons.add(haoHan);
        }

        // 排序
        Collections.sort(persons);
    }


}

haohanAdapter:
实现相同索引字母的时候只显示一个标题头。

package com.itheima57.quickindex.adapter;

import java.util.List;

import android.content.Context;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.itheima57.quickindex.R;
import com.itheima57.quickindex.domain.HaoHan;

public class HaoHanAdapter extends BaseAdapter {

    private final List<HaoHan> persons;
    private final Context context;

    public HaoHanAdapter(Context context, List<HaoHan> persons) {
        this.context = context;
        this.persons = persons;
    }

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

    @Override
    public Object getItem(int position) {
        return null;
    }
    @Override
    public long getItemId(int position) {
        return 0;
    }

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

        View view;
        if(convertView == null){
            view = View.inflate(context, R.layout.item_list_haohan, null);
        }else {
            view = convertView;
        }
        // 索引字母
        TextView tv_index = (TextView) view.findViewById(R.id.tv_index);
        // 名称
        TextView tv_name = (TextView) view.findViewById(R.id.tv_name);

        HaoHan haoHan = persons.get(position);

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

        // 分组, 只显示首次出现新的字母的条目索引
        String indexStr = null;

        if(position == 0){
            // 当前是第一个条目, 直接显示
            indexStr = currentLetter;
        }else {
            // 不是第一个条目, 跟前一个比较
            // 前一个拼音首字母
            String previousLetter = persons.get(position - 1).getPinyin().charAt(0) + "";

            if(!TextUtils.equals(previousLetter, currentLetter)){
                // 不相同时, 显示当前的索引栏
                indexStr = currentLetter;
            }
        }

        tv_index.setVisibility(indexStr == null ? View.GONE : View.VISIBLE);
        tv_index.setText(currentLetter);
        tv_name.setText(haoHan.getName());


        return view;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值