带你走近Android自定义view
Posted 计蒙不吃鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带你走近Android自定义view相关的知识,希望对你有一定的参考价值。
系列文章目录
一篇文章带你走近android自定义view
文章目录
- 系列文章目录
- 前言
- 一、为什么要自定义view
- 二、先看看一个超级简单的自定义view(三个构造函数)
- 三、了解手机的坐标系
- 四、使用Canvas画一个折线图(重写onDraw()方法)
- 五、如何自定义属性,且在view中获取到属性的值(小提,在六中会有案例)
- 六、绘制图案以及加入设置简单的动画(案例讲解很详细)
- 七、自定义view的实现分类以及自定义组合控件的案例
- 八、简单测量以及自定义接口实例来控制动画的更新计算表达式(onMeasure,TypeEvaluator)
- 九 、通过改变变量的值达到动画效果
- 十、当界面更新频繁(SurfaceView)
- 十一、GLSurfaceView(继承自SurfaceView,3D效果)
- 十二 、关于SVG
- 十三 、上一个简单github案例
- 十四 、还没来得及具体写的(关键词)
- 十五 、两道面试相关八股(根据本人面试大厂整理)
前言
从专科到本科,目前本科大四,已经是学习Android的第四个年头了,本打算积累一下冲23考研,但是最近被大佬洗脑后准备冲一冲22的考研,所以后续出文章的几率会很小,但是在前不久答应粉丝整理一个较为详细的Android自定义view教程,恰巧最近报名被华为选入2021年鸿蒙公开课的学生代表之一,在学校为请假条奔波的路上,所以抽出一下午写一篇文章。(有点小感冒,如发现错误请见谅,感谢指正!!!)。
下文为正文内容,所有链接案例注解都比较详细
一、为什么要自定义view
随着各大产品经理的内卷,Android系统内置的View早已无法满足我们的需求,我们需要针对自己的业务来定制我们需要的view,以达到更好的用户体验感,从而增加用户的黏性。
二、先看看一个超级简单的自定义view(三个构造函数)
需求:一个界面两个跑马灯(在xml中实现)
出现的问题:Textview在xml文件中实现跑马灯,如果有两个跑马灯,则会出现抢焦点的现象,只会跑一个。
解决方式:自定义一个Textview,设置其自动获得焦点: isFocused();
原文链接:一个最最最简单的自定义控件(Textview)
public class MyTextView extends TextView
//在用代码创建的时候调用
public MyTextView(Context context)
this(context, null);
//在识别XML的时候会调用此方法创建Textview,底层会用反射去AttribestSet去取属性值
public MyTextView(Context context, @Nullable AttributeSet attrs)
this(context, attrs, 0);
//给第一个构造函数和第二个使用
public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
//解决一个问题,需要Textview天生获取焦点
@Override
public boolean isFocused()
return true;
从以上代码中,本人已将函数的作用写入到备注中。
三、了解手机的坐标系
具体案例文章:Android用Canvas画一个真正能跑的跑马灯
四、使用Canvas画一个折线图(重写onDraw()方法)
此文章案例主要为canvas.drawLine(),drawText()的简单使用。
具体案例文章:Android用Canvas画一个折线图,并加以简单封装
五、如何自定义属性,且在view中获取到属性的值(小提,在六中会有案例)
以颜色为例。
//attrs文件
<attr name="leftcolor" format="reference|color"/>
<attr name="rightcolor" format="reference|color"/>
//java文件 ---TaiJiView为自定义view名称
//获取自定义属性。
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TaiJiView);
//获取颜色
int leftcolor = ta.getColor(R.styleable.TaiJiView_leftcolor, Color.BLACK);
int rightcolor=ta.getColor(R.styleable.TaiJiView_rightcolor, Color.WHITE);
//回收
ta.recycle();
//布局中
app:leftcolor="@color/colorPrimary"
app:rightcolor="#ff0000"
六、绘制图案以及加入设置简单的动画(案例讲解很详细)
canvas.drawCircle ,旋转动画
具体案例文章:Android自定义view之太极图
七、自定义view的实现分类以及自定义组合控件的案例
- 自定义组合控件:将多个控件组合成为一个新的控件。(本案例)
- 继承系统控件:如标题二的案例
- 继承View:如标题六的案例
- 继承ViewGroup:继承自LinearLayout等系统控件,在系统控件的基础功能上进行扩展。
具体案例文章:Android自定义view之模仿登录界面文本输入框(华为云APP)
八、简单测量以及自定义接口实例来控制动画的更新计算表达式(onMeasure,TypeEvaluator)
项目源码贴在链接文章末尾
具体案例文章:Android自定义view之围棋动画
九 、通过改变变量的值达到动画效果
Android自定义view之利用drawArc方法实现动态效果
Android自定义view之围棋动画(化繁为简)
Android自定义view之利用PathEffect实现动态效果
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
小提:把绘制点移动到中间。代码看起来会简洁点
十、当界面更新频繁(SurfaceView)
讲讲Android为自定义view提供的SurfaceView
十一、GLSurfaceView(继承自SurfaceView,3D效果)
如需继续深入还请了解openGL相关内容。
十二 、关于SVG
十三 、上一个简单github案例
Android线条等待动画JMWorkProgress(可添加依赖直接使用)
十四 、还没来得及具体写的(关键词)
贝塞尔曲线,事件分发机制。枚举(可在框架中用于确定动画状态)
十五 、两道面试相关八股(根据本人面试大厂整理)
1.View绘制流程
View的绘制是从 ViewRootImpl
的 performTraversals()
方法开始,从最顶层的 View(ViewGroup)
开始逐层对每个 View
进行绘制操作 。
View 绘制中主要流程分为measure,layout, draw 三个阶段。
measure :根据父 view 传递的 MeasureSpec 进行计算大小, 自定义View的过程中都会在onMeasure中进行宽高的测量,这个方法会从父布局中接收两个参数 widthMeasureSpac
和 heightMeasureSpac
,所以子布局的宽高大小需要受限于父布局。
layout :根据 measure 子 View 所得到的布局大小和布局参数,将子View放在合适的位置上, 结合源码可知 layout()
会将四个位置参数传递给 setOpticalFrame()
或者 setFrame()
,而 setOpticalFrame()
内部会调用 setFrame()
,所以最终通过 setFrame()
确定 View
在 ViewGroup
中的位置。位置确定完毕会调用 onLayout(l,t,r,b)
对子View进行摆放。
draw :把 View 对象绘制到屏幕上。
- Canvas:画布,不管是文字,图形,图片都要通过画布绘制而成
- Paint:画笔,可设置颜色,粗细,大小,阴影等等等等,一般配合画布使用
- Path:路径,用于形成一些不规则图形。
- Matrix:矩阵,可实现对画布的几何变换。
2.View 的事件分发机制
触摸事件的类型
触摸事件对应的是 MotionEvent 类,事件的类型主要有如下三种:
- ACTION_DOWN
- ACTION_MOVE(移动的距离超过一定的阈值会被判定为 ACTION_MOVE 操作)
- ACTION_UP
View 事件分发本质就是对 MotionEvent 事件分发的过程。即当一个 MotionEvent 发生后,系统将这个点击事件传递到一个具体的 View 上。
事件分发流程
事件分发过程由三个方法共同完成:
dispatchTouchEvent: 方法返回值为 true 表示事件被当前视图消费掉;返回为 super.dispatchTouchEvent 表示继续分发该事件,返回为 false 表示交给父类的 onTouchEvent 处理。
onInterceptTouchEvent: 方法返回值为 true 表示拦截这个事件并交由自身的 onTouchEvent 方法进行消费;返回 false 表示不拦截,需要继续传递给子视图。 如果 return super.onInterceptTouchEvent(ev), 事件拦截分两种情况:
-
1.如果该View存在子View且点击到了该子View, 则不拦截, 继续分发 给 子 View 处理, 此时相当于 return false。
-
2.如果该 View 没有子 View 或者有子 View 但是没有点击中子 View(此时 ViewGroup 相当于普通 View), 则交由该 View 的 onTouchEvent 响应,此时相当于 return true。
注意:一般的 LinearLayout、 RelativeLayout、FrameLayout 等 ViewGroup 默认不拦截, 而 ScrollView,ListView 等 ViewGroup 则可能拦截,得看具体情况。
onTouchEvent: 方法返回值为 true 表示当前视图可以处理对应的事件;返回值 为 false 表示当前视图不处理这个事件,它会被传递给父视图的 onTouchEvent 方法进行处理。如果 return super.onTouchEvent(ev),事件处理分为两种情况:
-
1.如果该 View 是 clickable 或者 longclickable 的,则会返回 true, 表示消费 了该事件, 与返回 true 一样;
-
2.如果该 View 不是 clickable 或者 longclickable 的,则会返回 false, 表示不 消费该事件,将会向上传递,与返回 false 一样。
注意:在 Android 系统中,拥有事件传递处理能力的类有以下三种:
Activity:拥有分发和消费两个方法。
ViewGroup:拥有分发、拦截和消费三个方法。
View:拥有分发、消费两个方法。
以上是关于带你走近Android自定义view的主要内容,如果未能解决你的问题,请参考以下文章
Android自定义View(三深入解析控件测量onMeasure)
一起Talk Android吧(第四百六十六回:实现自定义View中的测量功能)
Carson带你学Android:自定义View Canvas类使用教程