自定义高亮显示的字母序列控件

最近看到有代码在实现类似通讯录字母索引的功能的时候,使用了ListView来做处理,个人觉得用ListView甚至现在很流行的RecycleView来做并不好,可以来自定义一个索引的控件,达到更好的效果,话不多说,先上效果图:
这里写图片描述

首先:先来说说需求

  1. 把字母[A - Z - #]在控件中画出来(这里以字母来举例)
  2. 当触摸到某个字母时,高亮显示
  3. 给控件设置监听,显示当前触摸到的字母

下面,就开始撸码(其实自定义View基本上就是那几个套路);
1:自定义需要的属性:

   <declare-styleable name="CustomSideBar">
        <!-- 字体大小 高亮的颜色 普通颜色(未选中时候) -->
        <attr name="slideBarTextSize" format="dimension"/>
        <attr name="highLightColor" format="color"/>
        <attr name="normalColor" format="color"/>
    </declare-styleable>

2:在布局文件中使用

<com.justh.dell.customsidebar.CustomSideBar
        android:id="@+id/side_bar"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        app:slideBarTextSize="20dp"
        app:highLightColor="@color/colorAccent"
        app:normalColor="@color/colorPrimary"/>

3:在自定义View中获取属性,并使用

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

    public CustomSideBar(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CustomSideBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.CustomSideBar);

        mSideBarTextSize = array.getDimensionPixelSize(R.styleable.CustomSideBar_slideBarTextSize,dp2px(mSideBarTextSize));
        mHighLightColor = array.getColor(R.styleable.CustomSideBar_highLightColor,mHighLightColor);
        mNormalColor = array.getColor(R.styleable.CustomSideBar_normalColor,mNormalColor);

        array.recycle();

        //初始化画笔
        mHightPaint = getPaint(mHighLightColor);
        mNormalPaint = getPaint(mNormalColor);

    }

    //初始化画笔
    private Paint getPaint(int color){
        Paint paint = new Paint();
        paint.setColor(color);
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setTextSize(mSideBarTextSize);
        return paint;
    }

在onMeasure中去测量空间的宽高

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //计算宽度大小
        //宽度大小等于一个字母的宽度加上左右的padding值 治理取A来做例子
        int width = (int) mNormalPaint.measureText("A") + getPaddingLeft() + getPaddingRight();
        int height = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(width,height);
    }

然后在onDraw方法中间字母滑到对应的位置(这里直接将触摸到的时候的高亮显示字母的代码一并贴上了,懒死算了 哈哈哈)

@Override
    protected void onDraw(Canvas canvas) {
        //首先  由于是竖直排列的   所以先计算出一个字母所占据的高度
        //一个字母所占据的高度 = (高度总和 - paddingTop - paddingBottom) / 元素总和
        mLetterHeight = (getHeight() - getPaddingTop() - getPaddingBottom()) / letters.length;
        for(int i = 0;i<letters.length;i++){
            //计算每一个字母所在的位置的位置的中间值centerY
            int centerY = getPaddingTop() + i * mLetterHeight + mLetterHeight /2;
            Paint.FontMetricsInt fontMetricsInt = mNormalPaint.getFontMetricsInt();
            int dy = (fontMetricsInt.bottom - fontMetricsInt.top)/2 - fontMetricsInt.bottom;
            int baseLine = centerY + dy;
            //计算每一个字母所在的位置的X的起点  (居中情况下)
            int startX = (int) (getWidth()/2 - mNormalPaint.measureText(letters[i])/2);
            if(letters[i].equals(mCurrentLetter)){
                canvas.drawText(letters[i],startX,baseLine,mHightPaint);
            }else{
                canvas.drawText(letters[i],startX,baseLine,mNormalPaint);
            }
        }
    }

最后重写onTouch方法,用来获取当前触摸到的是哪一个字母:

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                //根据当前的坐标 计算当前触摸到的是哪个条目
                int dy = (int) event.getY();
                int currentItem = (dy - getPaddingTop())/mLetterHeight;
                if(currentItem < 0){
                    currentItem = 0;
                }
                if(currentItem >= letters.length){
                    currentItem = letters.length - 1;
                }
                //获取到惦记的是哪个字母
                mCurrentLetter = letters[currentItem];
                if(mSideBarTouchLetterListener != null){
                    mSideBarTouchLetterListener.onTouchLetter(mCurrentLetter,true);
                }
                invalidate();
                break;
            case MotionEvent.ACTION_UP:

                //设置延迟消失
                mHandler.sendEmptyMessageDelayed(1,1000);
                break;
        }
        return true;
    }

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(msg.what == 1){
                if(mSideBarTouchLetterListener != null){
                    mSideBarTouchLetterListener.onTouchLetter(mCurrentLetter,false);
                }
            }
        }
    };

这里,使用了一个Handler 的延迟发送消息来处理延迟1秒消失TextView的效果

最后,给其设置字母的触摸监听回调

private SideBarTouchLetterListener mSideBarTouchLetterListener;

    public void setSideBarTouchLetterListener(SideBarTouchLetterListener listener){
        this.mSideBarTouchLetterListener = listener;
    }
    //设置回调监听
    public interface SideBarTouchLetterListener{
        //flag控制显示与不显示在Activity中的圆形TextView
        void onTouchLetter(String letter,boolean flag);
    }

一直习惯了把注释写到代码里,这样阅读起来也会更方便,相信大家都能理解我的良苦用心,这里就不过多解释了。

最后 只需要在actvity中监听CustomSideBar的回调,并将对应点击的字母设置给TextView显示出来就可以了。

public class MainActivity extends AppCompatActivity implements CustomSideBar.SideBarTouchLetterListener{

    private TextView mTextView;
    private CustomSideBar mSideBar;

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

        initView();
        initListener();
    }

    private void initView(){

        mTextView = (TextView) findViewById(R.id.textView);
        mSideBar = (CustomSideBar) findViewById(R.id.side_bar);

    }

    private void initListener(){
        mSideBar.setSideBarTouchLetterListener(this);
    }

    @Override
    public void onTouchLetter(String letter,boolean flag) {
        if(flag){
            mTextView.setVisibility(View.VISIBLE);
            mTextView.setText(letter);
        }else{
            mTextView.setVisibility(View.GONE);
        }
    }
}

好了,到这里就已经结束了,再来一波最后的效果图,如果大家发现有什么问题,可以回复交流下,谢谢!
这里写图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值