Android自定义类似联系人列表的 快速索引控件

效果图

这里写图片描述

思考

  1. 将字母 A - Z 画出来
  2. 处理字母的触摸事件
  3. 提供使用的回调
  4. 联系人汉字转成拼音
  5. 按拼音排序
  6. 分组(同一个拼音开头的为一组)
  7. 将自定义的view和ListView绑定

实现

1. 绘制A-Z

  • 准备好A-Z 26个字母
  • 在onDraw方法里面计算每个字母的位置并绘制
    这里写图片描述
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"};
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制A-Z
        for (int i = 0; i < LETTERS.length; i++) {
//            int width = (int) (screenWidth * 1.0 / 2 - paint.measureText(LETTERS[i]) * 1.0 / 2);
            Rect rect = new Rect();
            paint.getTextBounds(LETTERS[i], 0, 1, rect);
            int width = (int) (screenWidth * 1.0 / 2 - rect.width() * 1.0 / 2);
            int height = (int) (cellHeight * 1.0 / 2 + rect.height() * 1.0 / 2 + i * cellHeight);
            canvas.drawText(LETTERS[i], width, height, paint);
        }

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        screenWidth = getMeasuredWidth();
        screenHeight = getMeasuredHeight();
        cellHeight = screenHeight * 1.0 / LETTERS.length;
    }

2. 处理onTouchEvent事件


int touchIndex = -1;

@Override
public boolean onTouchEvent(MotionEvent event) {
    int y;
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            y = (int) event.getY();
            for (int i = 0; i < LETTERS.length; i++) {
                if (y > i * cellHeight && y < (i + 1) * cellHeight) {
                    if (touchIndex != i) {
                        Log.e(TAG, LETTERS[i]);
                        Toast.makeText(getContext(), LETTERS[i], 0).show();
                        touchIndex = i;
                    }
                    break;
                }
            }
            break;

        case MotionEvent.ACTION_MOVE:
            y = (int) event.getY();
            for (int i = 0; i < LETTERS.length; i++) {
                if (y > i * cellHeight && y < (i + 1) * cellHeight) {
                    if (touchIndex != i) {
                        Log.e(TAG, LETTERS[i]);
                        Toast.makeText(getContext(), LETTERS[i], 0).show();
                        touchIndex = i;
                    }
                    break;
                }
            }
            break;
        case MotionEvent.ACTION_UP:
            break;

    }
    return true;
}

3. 提供回调方法

private OnIndexChangedListener listener;

public void setOnIndexChangedListener(OnIndexChangedListener listener) {
    this.listener = listener;
}

public interface OnIndexChangedListener {
    void onIndexChanged(String index);
}
//触摸事件的操作通过接口让调用者去实现
@Override
public final boolean onTouchEvent(MotionEvent event) {
    int y;
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            y = (int) event.getY();
            for (int i = 0; i < LETTERS.length; i++) {
                if (y > i * cellHeight && y < (i + 1) * cellHeight) {
                    if (touchIndex != i) {
                        if (listener != null) {
                            listener.onIndexChanged(LETTERS[i]);
                        }
                        touchIndex = i;
                    }
                    break;
                }
            }
            break;

        case MotionEvent.ACTION_MOVE:
            y = (int) event.getY();
            for (int i = 0; i < LETTERS.length; i++) {
                if (y > i * cellHeight && y < (i + 1) * cellHeight) {
                    if (touchIndex != i) {
                        if (listener != null) {
                            listener.onIndexChanged(LETTERS[i]);
                        }
                        touchIndex = i;
                    }
                    break;
                }
            }
            break;
        case MotionEvent.ACTION_UP:
            break;

    }
    return true;
}

4. 汉字转拼音

  • 需要添加 pinyin4j-2.5.0.jar
/**
 * 描述:汉字转拼音的工具类
 * 作者 mjd
 * 日期:2015/12/10 12:50
 */
public class PinYinUtils {

    public static String getPinYin(String hanZi) {
        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
        format.setCaseType(HanyuPinyinCaseType.UPPERCASE);
        format.setToneType(HanyuPinyinToneType.WITH_TONE_NUMBER);
        StringBuilder sb = new StringBuilder();

        char[] charArray = hanZi.toCharArray();
        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 s = "";
                try {
                    // 通过char得到拼音集合. 单 -> dan, shan
                    s = PinyinHelper.toHanyuPinyinStringArray(c, format)[0];
                    sb.append(s);
                } catch (BadHanyuPinyinOutputFormatCombination e) {
                    e.printStackTrace();
                    sb.append(s);
                }
            }
        }

        return sb.toString();
    }


}

5. 汉字按拼音排序

//让联系人自身具备比较性(按照拼音)

/**
 * 描述:
 * 作者 mjd
 * 日期:2015/12/10 13:01
 */
public class Person implements Comparable<Person> {

    private String name;
    private String pinyin;

    public Person(String name) {
        this.name = name;
        this.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(@NonNull Person another) {
        return this.pinyin.compareTo(another.getPinyin());
    }


}

//主页面(Cheeses.NAMES 是准备好的一百零八位好汉的名字)
/**
 * 描述:
 * 作者 mjd
 * 日期:2015/12/9 21:43
 */

public class MainActivity extends AppCompatActivity {

    private FastIndexBar fastIndexBar;
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fastIndexBar = (FastIndexBar) findViewById(R.id.fast_index_bar);
        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        fastIndexBar.setOnIndexChangedListener(new FastIndexBar.OnIndexChangedListener() {
            @Override
            public void onIndexChanged(String index) {
                Toast.makeText(MainActivity.this, index, 0).show();
            }
        });

        ContactAdapter adapter = new ContactAdapter(getContacts());
        recyclerView.setAdapter(adapter);
        RecyclerView.LayoutManager manager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(manager);

    }

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

}
//联系人的适配器
/**
 * 描述:
 * 作者 mjd
 * 日期:2015/12/10 13:08
 */
public class ContactAdapter extends RecyclerView.Adapter<MyViewHolder> {

    private List<Person> personList;

    public ContactAdapter(List<Person> personList) {
        this.personList = personList;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = View.inflate(parent.getContext(), R.layout.item_contact, null);
        view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        Person person = personList.get(position);
        holder.tvIndex.setText(String.valueOf(person.getPinyin().charAt(0)));
        holder.tvName.setText(person.getName());
    }

    @Override
    public int getItemCount() {
        return personList.size();
    }


}

class MyViewHolder extends RecyclerView.ViewHolder {

    TextView tvIndex;
    TextView tvName;

    public MyViewHolder(View itemView) {
        super(itemView);
        tvIndex = (TextView) itemView.findViewById(R.id.tv_index);
        tvName = (TextView) itemView.findViewById(R.id.tv_name);
    }
}

6. 汉字按拼音分组

//在适配器的onBindViewHolder方法中判断
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
    Person person = personList.get(position);
    String currentIndex = String.valueOf(person.getPinyin().charAt(0));
    String str = null;
    if (position == 0) {
        str = currentIndex;
    } else {
        String preIndex = String.valueOf(personList.get(position - 1).getPinyin().charAt(0));
        if (!currentIndex.equals(preIndex)) {
            str = currentIndex;
        }
    }
    holder.tvIndex.setVisibility(str == null ? View.GONE : View.VISIBLE);
    holder.tvIndex.setText(currentIndex);
    holder.tvName.setText(person.getName());
}

7. 将快速索引控件与RecyclerView绑定

fastIndexBar.setOnIndexChangedListener(new FastIndexBar.OnIndexChangedListener() {
    @Override
    public void onIndexChanged(String index) {
        //显示居中的字母提醒
        showIndex(index);
        //找到personList中第一个以index为拼音首字母的对象,得到索引
        for (int i = 0; i < personList.size(); i++) {
            String currentIndex = String.valueOf(personList.get(i).getPinyin().charAt(0));
            if (currentIndex.equals(index)) {//匹配成功
                recyclerView.scrollToPosition(i);
                break;
            }
        }
    }
});

private Handler handler = new Handler();

private void showIndex(String index) {
    tvCenter.setVisibility(View.VISIBLE);
    tvCenter.setText(index);
    handler.removeCallbacksAndMessages(null);
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            tvCenter.setVisibility(View.GONE);
        }
    }, 2000);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值