自定义控件---右侧首字母索引列的实现

这个做起来需要注意的是,字母的居中摆放和滑出控件后的特效关闭.

本人写的代码添加完整注释,当然如果哪里有问题,希望可以和大家一起讨论,不胜荣幸.

主要核心:

自定义控件类:

//先写自定义控件 1.继承View类 2.实现三个构造方法(分别有1,2,3个参数)
public class LetterIndexerView extends View {
    private String[] mArrLetters = 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 TextView textView_dialog;
    private int mLineHeight;
    private float mDensity = 0;
    private Paint mPaint;
    public OnLetterClickedListener mListener;//接口回调2

    public LetterIndexerView(Context context) {
        super(context);
        init(null, 0);
    }

    public LetterIndexerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public LetterIndexerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs, defStyleAttr);
    }
    //2.写一个初始化方法,在三个构造中都调用一下,内部对画笔进行初始化,需要用到像素转换时写出像素转换
    private void init(AttributeSet attrs, int defStyleAttr) {
        mDensity = getResources().getDisplayMetrics().density;//像素转换
        mPaint = new Paint();//初始化画笔
        //        paint.setAntiAlias(true);
        mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);//去掉画笔的毛边,抗锯齿.和上面注释的那句一个效果
        mPaint.setColor(Color.GRAY);//将画笔设置为灰色
        mPaint.setTextSize(14 * mDensity);//给画笔设置画文本的文字大小,这里叫做画是因为画笔生成的文字.
    }

    //3.用来识别布局文件中的 match_parent\wrap_content\和固定值\比例等宽高信息,比onDraw先执行
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //这四句话是用来获取设置的宽高的信息,最好都写上这四句
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);//获得宽高的模式AT_MOST\EXACTLY\UNSPECIFIED
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);//获得宽高的尺寸

        int widthPx = (int) (35 * mDensity);
        setMeasuredDimension(widthPx, heightSize);//这里直接设置了固定的宽度,因为此自定义控件不需要使用者自己定义宽度
    }
    //4.在onMeasure后执行,对所需要显示的内容进行绘制
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int viewWidth = getWidth();
        int viewHeight = getHeight();//获取LetterIndexerView控件的宽高
        mLineHeight = viewHeight / mArrLetters.length;//通过控件高度除以显示行数得出每行高度

        for (int i = 0; i < mArrLetters.length; i++) {//循环画出27个数组中元素
            //下面的计算为了让字母在控件居中显示
            int textWwidth = (int) mPaint.measureText(mArrLetters[i]);//获得画笔要画出的字母的宽度
            //viewWidth - textWwidth) / 2算出字母在哪里画会居中显示 mLineHeight * (i + 1)用来算出Y轴坐标
            canvas.drawText(mArrLetters[i], (viewWidth - textWwidth) / 2, mLineHeight * (i + 1), mPaint);//画字母
        }
    }//这个方法写完之后就可以在布局文件中定义一个LetterIndexerView控件了,下面的方法是为了让控件具有一些显示效果的点击监听
    //设置点击事件
    //点击事件分为ACTION_UP(抬起)/ACTION_MOVE(移动)/ACTION_DOWN(按下)
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float y = event.getY();//获得点击的位置的Y轴坐标,用来算显示哪个字母的User信息
        //获取点击位置对应的字母索引
        int position = (int) (y / mLineHeight);
        if (position >= 0 && position < mArrLetters.length) {//判断点击的位置在LetterIndexerView的有效范围内,不判断会出现下标越界异常
            switch (event.getAction()) {
                case MotionEvent.ACTION_UP://当手指抬起时候执行
                    setBackgroundColor(Color.TRANSPARENT);
                    if (textView_dialog != null && textView_dialog.getVisibility() == View.VISIBLE) {
                        textView_dialog.setVisibility(View.INVISIBLE);
                    }//如果textView_dialog在显示的情况并且不为空,那么隐藏它

                    break;

                case MotionEvent.ACTION_MOVE:
                case MotionEvent.ACTION_DOWN://这里的移动和按下效果一样所以用switch穿透写在一起就好
                    setBackgroundColor(Color.parseColor("#959595"));
                    if (textView_dialog != null && textView_dialog.getVisibility() == View.INVISIBLE) {
                        textView_dialog.setVisibility(View.VISIBLE);
                    }//按照条件判断理解就好,不难
                    textView_dialog.setText(mArrLetters[position]);//设置textView_dialog的内容
                    //接口回调
                    if (mListener != null) {
                        mListener.onClick(mArrLetters[position]);//接口回调3,固定这样写法
                    }//这里是接口回调的地方
                    break;
            }
        }else{//当手指划出的时候放此控件特效都消失
            setBackgroundColor(Color.TRANSPARENT);
            textView_dialog.setVisibility(View.INVISIBLE);
        }
        return true;
    }
    //接口回调1
    public interface OnLetterClickedListener {
        void onClick(String letter);
    }
    //为自定义控件设置自定义的监听方法
    public void setOnLetterClickedListener(TextView _textView_dialog, OnLetterClickedListener _listener) {
        this.textView_dialog = _textView_dialog;
        this.mListener = _listener;
    }
}
MainActivity:

 private Context mContext = this;//定义上下文
    private TextView textView_dialog;
    private RecyclerView recyclerView_main;
    private LetterIndexerView letterIndexerView_main;//定义三个布局中的控件
    private MyRecyclerAdapter mAdapter = null;//定义适配器
    private List<ItemBean> mTotalList = new ArrayList<>();//定义数据源集合,这里吧ItemBean写好,需要啥属性就不说了,按自己需要来

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        initView();
        sortData();//将需要在onCreate中写的方法都抽取为单独的方法编程顺序initView-->initData-->sortData
    }

    private void initData() {
        String[] arrUserNames = getResources().getStringArray(R.array.arrUsernames);
        String[] arrIconUrls = getResources().getStringArray(R.array.arrIconUrl);
        for (int i = 0; i < arrUserNames.length; i++) {
            ItemBean itemBean = new ItemBean();
            itemBean.setUserName(arrUserNames[i]);
            itemBean.setIconUrl(arrIconUrls[i]);
            //获取拼音和首字母
            String pinyin = ChineseToPinyinHelper.getInstance().getPinyin(arrUserNames[i]);
            itemBean.setPinyin(pinyin);
            String firstLetter = pinyin.substring(0, 1).toUpperCase();

            if (firstLetter.matches("[A-Z]")) {
                itemBean.setFirstLetter(firstLetter);
            } else {
                itemBean.setFirstLetter("#");
            }
            mTotalList.add(itemBean);
        }
    }

    private void sortData() {
        Collections.sort(mTotalList, new Comparator<ItemBean>() {
            @Override
            public int compare(ItemBean lhs, ItemBean rhs) {
                return lhs.getPinyin().toLowerCase().compareTo(rhs.getPinyin().toLowerCase());
            }
        });
    }
    //先来写初始化控件方法
    private void initView() {
        textView_dialog = (TextView) findViewById(R.id.textView_dialog);
        recyclerView_main = (RecyclerView) findViewById(R.id.recyclerView_main);
        letterIndexerView_main = (LetterIndexerView) findViewById(R.id.letterIndexerView_main);//三个findViewById自不必说

        recyclerView_main.setHasFixedSize(true);//为RecyclerView设置固定尺寸,一般设置上,毕竟没几个不希望固定的,而设置了当内容超出也就不固定了
        recyclerView_main.setItemAnimator(new DefaultItemAnimator());//设置动画效果(默认效果)
        recyclerView_main.addItemDecoration(new DividerItemDecoration(mContext,
                DividerItemDecoration.VERTICAL_LIST));//设置分割线,VERTICAL_LIST--横着的分割线

        mAdapter = new MyRecyclerAdapter(mContext, mTotalList);//初始化适配器,RecyclerView必须设置适配器 ,在这里去写适配器
                                                                // 先把shangxiawen和数据源参数写上,写到这的时候数据源内还是空的得到内容从initData可以看到
        final LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);//定义一个布局管理器,RecyclerView需要用
        recyclerView_main.setLayoutManager(layoutManager);//为RecyclerView设置布局管理器,不设置它的话内容出不来
        recyclerView_main.setAdapter(mAdapter);//RecyclerView设置适配器

        //下面这个监听器是在写完自定义控件的监听方法之后才写的,建议看的时候先看自定义控件的监听方法
        letterIndexerView_main.setOnLetterClickedListener(textView_dialog, new LetterIndexerView.OnLetterClickedListener() {
            @Override
            public void onClick(String letter) {
                //让RecyclerView滚动到指定position
                int position = mAdapter.getPositionForSection(letter.charAt(0));
//                textView_dialog.setText(letter);
                layoutManager.scrollToPositionWithOffset(position, 0);
            }
        });
    }

适配器就不贴出来了,自己写也不麻烦就是普通的适配器


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值