参考链接
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();@Overrideprotected 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);}@Overrideprotected 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);}}}@Overridepublic 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发生变化才通知更新UIcurrentTouchIndex = 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;@Overrideprotected 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);}@Overridepublic void onTouch(final String letter, boolean isDown) {if (isDown) {mTv.setText(letter);mTv.setVisibility(View.VISIBLE);} else {mTv.postDelayed(new Runnable() {@Overridepublic 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"><TextViewandroid: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.LetterSideBarandroid: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

红橙Darren视频笔记 自定义sidebar 自定义View ViewGroup套路相关推荐

  1. 红橙Darren视频笔记 面试题 为什么view获取宽高为0 onCreate onResume view.post源码浅析(继承activity api27)

    面试题 下面的输出分别为多少 为什么 <?xml version="1.0" encoding="utf-8"?> <LinearLayout ...

  2. 红橙Darren视频笔记 UML图简介

    整体架构复制自红橙原视频的课堂笔记 因为他这一课没有博客,所以没有转载链接,CSDN没有转载地址是无法作为转载类型的文章发表的,暂时标记为原创 参考链接 https://blog.csdn.net/r ...

  3. 红橙Darren视频笔记 类加载机制(API28) 自己写个热修复 查看源码网站

    第一部分 类加载机制 一个Activity是如何被Android虚拟机找到的? 在之前的文章 红橙Darren视频笔记 自定义View总集篇(https://blog.csdn.net/u011109 ...

  4. 红橙Darren视频笔记 Behavior的工作原理源码分析

    主要coordinatorlayout的代码来自coordinatorlayout-1.0.0-sources.jar 本文从源码介绍 CoordinatorLayout 的 behavior 怎么工 ...

  5. 红橙Darren视频笔记 代理模式 动态代理和静态代理

    红橙Darren视频笔记 代理模式 动态代理和静态代理(Android API 25) 关于代理模式我之前有过相关的介绍: https://blog.csdn.net/u011109881/artic ...

  6. 红橙Darren视频笔记 仿QQ侧滑效果

    这一篇没有什么新的内容 就是改写 红橙Darren视频笔记 仿酷狗侧滑效果 的侧滑的效果 1.去掉淡入淡出效果 2.加上黑色模板效果 效果: 去掉淡入淡出效果很简单 就是注释掉onScrollChan ...

  7. 红橙Darren视频笔记 ViewGroup事件分发分析 基于API27

    本节目标,通过案例,先看程序运行结果,然后跟踪源码,理解为什么会有这样的输出,继而理解view group的分发机制,感觉和证明题很像呢. 考虑以下程序的运行结果: case1: public cla ...

  8. 红橙Darren视频笔记 利用阿里巴巴AndFix进行热修复

    注意 由于AndFix在2017年左右就停止更新了,在最新版本的apk上遇到很多问题,我最终也没有成功进行热修复.本节主要是学习热修复的原理 在上一篇 红橙Darren视频笔记 自己捕获异常并保存到本 ...

  9. 红橙Darren视频笔记 自定义View总集篇

    本节目的 了解 ActivityManagerService Activity ActivityManager Window WindowManager WindowManagerService Se ...

最新文章

  1. Java中迭代列表中数据时几种循环写法的效率比较
  2. lvm 多个硬盘合成一个_「ECS最佳实践」基于多块云盘构建LVM逻辑卷
  3. 字典-字典的增删改查常用操作
  4. 【贪心】最佳浏览路线问题
  5. 同时面了腾讯三个部门,拿下offer!
  6. arcgis 删除图形重复折点_【干货】ArcGIS中画环状图斑、挑子区及消除图斑重复区域...
  7. asterisk积累命令
  8. Flutter进阶—创建有状态控件
  9. Win10下VB6.0开发之串口通信基础(一)控件属性篇
  10. 蓝桥杯2021年第十二届C++省赛第七题-砝码称重
  11. http://jingyan.baidu.com/article/636f38bb3eb78ad6b8461082.html
  12. 信息的载体依附性_信息的一般特征是载体依附性
  13. 【NLP】哈工大车万翔教授 -- 自然语言处理NLPer的核心竞争力是什么?19页ppt
  14. 计算简史:什么是计算机?《禅与计算机程序设计艺术》 / 陈光剑
  15. 洛谷 P2014 选课
  16. Unity制作UI翻页动画
  17. 【Fusion】Conic Quadratic Optimization
  18. 回路、简单回路、简单路径
  19. 考公、事业编、央企国企私企外企、校招社招都在这些地方找到信息(精华)
  20. python axis到底如何理解?

热门文章

  1. jtessboxeditorfx 界面显示不出来_不需要发酵,自制家庭版健康小油条,不会失败的配方...
  2. 课程设计之第二次冲刺----第八天
  3. 关于轻重边及树链剖分该怎么写...
  4. 听 Fabien Potencier 谈Symfony2 之 《What is Symfony2 ?》
  5. uva 10891 - Game of Sum
  6. PAT乙级(1033 旧键盘打字)
  7. python路径在哪里设置_找Python安装目录,设置环境路径以及在命令行运行python脚本实例...
  8. 嘉年华回顾丨胡连亚带你见证金融行业从标准和平台层面推进数据库能力迭代...
  9. 第十届数据技术嘉年华活动彩蛋!
  10. vue3,对比 vue2 有什么优点?