红橙Darren视频笔记 自定义sidebar 自定义View ViewGroup套路

参考链接
https://www.jianshu.com/p/1dc41a770f64

1效果

在这里插入图片描述

2 目的

学习onMeasure onDraw onTouchEvent等自定义view方法的使用

3 自定义view

public class LetterSideBar extends View {
    private static final String TAG = "LetterSideBar";
    String mLetters[] = 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 mTextPaint, mFocusTextPaint;
    private int currentTouchIndex = -1;

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

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

    public LetterSideBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mTextPaint = initPaint(Color.BLUE);
        mFocusTextPaint = initPaint(Color.RED);
    }

    private Paint initPaint(int color) {
        //省略自定义属性
        Paint paint = new Paint();
        paint.setColor(color);
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setTextSize(sp2px(20));
        return paint;
    }

    private float sp2px(int sp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
    }

    private Rect bounds = new Rect();

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int maxLetterWidth = 0;
        int height = MeasureSpec.getSize(heightMeasureSpec);
        for (String letter : mLetters) {//此处有改动 因为每个字母的宽度不等 所以我觉得不能以字母A的宽度作为控件宽度 而应该取最大的字母的宽度
            mTextPaint.getTextBounds(letter, 0, 1, bounds);
            maxLetterWidth = Math.max(maxLetterWidth, bounds.width());
        }

        setMeasuredDimension(maxLetterWidth + getPaddingEnd() + getPaddingStart(), height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int itemHeight = (getMeasuredHeight() - getPaddingTop() - getPaddingBottom()) / mLetters.length;
        Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
        int baseLine = (itemHeight - fontMetricsInt.bottom - fontMetricsInt.top) / 2;

        int startX;//每个字母开始绘制的x点
        int startY;//;//每个字母开始绘制的y起点
        for (int i = 0; i < mLetters.length; i++) {
            mTextPaint.getTextBounds(mLetters[i], 0, 1, bounds);
            int currentTextWidth = bounds.width();
            startX = (getMeasuredWidth() - currentTextWidth) / 2;
            startY = getPaddingTop() + i * itemHeight + baseLine;
            if (currentTouchIndex == i) {
                canvas.drawText(mLetters[i], startX, startY, mFocusTextPaint);
            } else {
                canvas.drawText(mLetters[i], startX, startY, mTextPaint);
            }

        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                //计算落在那个字母
                int itemHeight = (getMeasuredHeight() - getPaddingTop() - getPaddingBottom()) / mLetters.length;
                int index = (int) ((event.getY() - getPaddingTop()) / itemHeight);
                if (index < 0 || index > 26) {
                    return true;
                }
                if (currentTouchIndex != index) {//优化 只有index发生变化才通知更新UI
                    currentTouchIndex = index;
                    mLetterSideBarTouchListener.onTouch(mLetters[index], true);
                    invalidate();
                }

                Log.d(TAG, "onTouchEvent: " + mLetters[index]);
                break;
            case MotionEvent.ACTION_UP:
                //TODO 优化 松开的时候 高亮字母变色
                mLetterSideBarTouchListener.onTouch("", false);
                break;
        }
        return true;//view 消费事件
    }

    private LetterSideBarTouchListener mLetterSideBarTouchListener;

    void setLetterSideBarListener(LetterSideBarTouchListener listener) {
        mLetterSideBarTouchListener = listener;
    }

    interface LetterSideBarTouchListener {
        void onTouch(String letter, boolean isDown);
    }
}

4 mainActivity

public class MainActivity extends AppCompatActivity implements LetterSideBar.LetterSideBarTouchListener {
    private TextView mTv;
    private LetterSideBar mLetterSideBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTv = findViewById(R.id.tv);
        mLetterSideBar = findViewById(R.id.letterSideBar);
        mLetterSideBar.setLetterSideBarListener(this);
    }

    @Override
    public void onTouch(final String letter, boolean isDown) {
        if (isDown) {
            mTv.setText(letter);
            mTv.setVisibility(View.VISIBLE);
        } else {
            mTv.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mTv.setText(letter);
                    mTv.setVisibility(View.GONE);
                }
            }, 200);
        }

    }
}

5 xml

<?xml version="1.0" encoding="utf-8"?>
<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">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textSize="50sp"
        android:visibility="gone" />

    <com.example.chj.lettersidebar.LetterSideBar
        android:id="@+id/letterSideBar"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentEnd="true"
        android:padding="15dp" />

</RelativeLayout>

6 自定义View ViewGroup套路总结

6.1自定义View的套路:
6.1.1 自定义属性,获取自定义属性(达到配置的效果)
6.1.2 onMeasure()方法用于测量计算自己的宽高,如果继承系统的Button TextView一般不需要重写
6.1.3 onDraw() 绘制自己
6.1.4 onTouch() 处理交互
6.2 自定义ViewGroup的套路:
6.2.1 自定义属性,获取自定义属性(同上)
6.2.2 onMeasure() 方法,for循环测量子View,根据子View的宽高来计算自己的宽高(多了测量子view的部分)
6.2.3 onDraw() 一般不需要,默认情况下是不会调用,如果你要绘制需要实现dispatchDraw()方法,参见
https://blog.csdn.net/u011109881/article/details/110729417
6.2.4 onLayout() 用来摆放子View,必须实现的方法,注意排除是GONE的case
6.2.5 在很多情况下不会继承自ViewGroup ,往往是继承 系统已经提供好的ViewGroup 如 ViewPager ScrollView RelativeLayout

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值