AZ侧栏选择器(SidebarView)

SidebarView


前言

最近公司项目用到像通讯那样的A-Z的选择器,网上有很多的案例自己也想实现一下,以此记录一下。

在这里插入图片描述
下面看代码的实现:

  1. 先定义属性

 <declare-styleable name="SidebarView">
        <attr name="sidebar_TextColor" format="color" />
        <attr name="sidebar_TextSelectColor" format="color" />
        <attr name="sidebar_TextSize" format="dimension" />
        <attr name="sidebar_TextPadding" format="dimension" />
        <attr name="sidebar_Data" format="reference" /> // string-arry 资源id 在res 创建strings 
    </declare-styleable>
  
    <string-array name="az">
        <item>A</item>
        <item>B</item>
        <item>C</item>
        <item>D</item>
        <item>E</item>
        <item>F</item>
        <item>G</item>
        <item>H</item>
        <item>I</item>
        <item>J</item>
        <item>K</item>
        <item>L</item>
        <item>M</item>
        <item>N</item>
        <item>O</item>
        <item>P</item>
        <item>Q</item>
        <item>R</item>
        <item>S</item>
        <item>T</item>
        <item>U</item>
        <item>V</item>
        <item>W</item>
        <item>X</item>
        <item>Y</item>
        <item>Z</item>
    </string-array>

  1. 自定义View 继承View 重写onMeasure() onDraw() onTouchEvent() 代码简单 直接上代码
package com.ui.lirary;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

public class SidebarView extends View {

    private final Paint paint;
    private int textColor = Color.BLACK;
    private int textSelectColor = Color.BLUE;
    private int textHeight;
    private int textWidth;
    private final int textPadding;
    private final String[] data;
    private int selectIndex = -1;
    private OnSelectCallback onSelectCallback;
    private TextView toastView;

    public void setOnSelectCallback(OnSelectCallback onSelectCallback) {
        this.onSelectCallback = onSelectCallback;
    }

    public void setToastView(TextView toastView) {
        this.toastView = toastView;
    }

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

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

    public SidebarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        float textSize = context.getResources().getDimension(R.dimen.sidebar_text_size);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SidebarView);
        textColor = array.getColor(R.styleable.SidebarView_sidebar_TextColor, textColor);
        textSelectColor = array.getColor(R.styleable.SidebarView_sidebar_TextSelectColor, textSelectColor);
        textSize = array.getDimension(R.styleable.SidebarView_sidebar_TextSize, textSize);
        textPadding = array.getDimensionPixelOffset(R.styleable.SidebarView_sidebar_TextPadding, 0);
        int resourcesId = array.getResourceId(R.styleable.SidebarView_sidebar_Data, R.array.az);
        array.recycle();
        data = context.getResources().getStringArray(resourcesId);
        paint = new Paint();
        paint.setTextSize(textSize);
        paint.setColor(textColor);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthModel = MeasureSpec.getMode(widthMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int heightModel = MeasureSpec.getMode(heightMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        measureText();
        int w = textWidth + getPaddingStart() + getPaddingEnd();
        int h = textHeight + getPaddingTop() + getPaddingBottom() + textPadding * (data.length < 1 ? 0 : data.length - 1);
        if (widthModel == MeasureSpec.EXACTLY && heightModel == MeasureSpec.EXACTLY) {
            setMeasuredDimension(width, height);
        } else if (widthModel == MeasureSpec.EXACTLY && heightModel == MeasureSpec.AT_MOST) {
            setMeasuredDimension(width, h);
        } else if (widthModel == MeasureSpec.AT_MOST && heightModel == MeasureSpec.EXACTLY) {
            setMeasuredDimension(w, height);
        } else {
            setMeasuredDimension(w, h);
        }
    }

    private void measureText() {
        for (String datum : data) {
            Rect rect = new Rect();
            paint.getTextBounds(datum, 0, datum.length(), rect);
            int w = rect.width();
            if (w > textWidth) {
                textWidth = w;
            }
            textHeight += rect.height();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int itemHeight = textHeight / data.length;
        float h = itemHeight / 2.0f + getPaddingTop();
        for (int i = 0; i < data.length; i++) {
            String text = data[i];
            if (i == selectIndex) {
                paint.setColor(textSelectColor);
            } else {
                paint.setColor(textColor);
            }
            float itemWidth = paint.measureText(text);
            float x = (getWidth() - getPaddingStart() - getPaddingEnd() - itemWidth) / 2 + getPaddingStart();
            Paint.FontMetrics fontMetrics = paint.getFontMetrics();
            //dy的计算公式是固定的 (bottom 绝对值 +  top 绝对值)/2 减去 bottom   由于 top 是负数  所以直接减法可以了
            float dy = ((fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom);
            //基线 等于 item  y轴中间点 + dy
            float baseLine = h + dy;
            canvas.drawText(text, x, baseLine, paint);
            h += itemHeight + textPadding;

        }
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        final int action = event.getAction();
        final float y = event.getY();
        final int oldChoose = selectIndex;
        // 点击y坐标所占总高度的比例乘数组的长度就等于当前选中的索引.
        final int c = (int) (y / getHeight() * data.length);
        if (action == MotionEvent.ACTION_UP) {
            if (toastView != null) {
                toastView.setVisibility(GONE);
            }
            invalidate();
        } else {
            if (toastView != null) {
                toastView.setVisibility(VISIBLE);
            }
            if (oldChoose != c) {
                if (c >= 0 && c < data.length) {
                    selectIndex = c;
                    if (onSelectCallback != null) {
                        onSelectCallback.onCallback(data[selectIndex]);
                    }
                    invalidate();
                }
            }
        }
        return true;
    }


    public interface OnSelectCallback {
        void onCallback(String s);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值