在实际项目的自定义View过程
1,拿到需求,判断是否能通过拆分为 简单的View 实现、是否能够隔离事件分发等难题
最终达到的效果:
1)尽量不重写
2)单个View不涉及多的点击事件,不涉及复杂的绘制工作
2,通过步骤1,筛选到只需要绘制简单的View样式
系统的View执行流程为:构造方法 -> onMeasure -> onLayout -> onDraw
由于我们最终要的是效果,因此直接在onDraw中绘制想要的效果,再依次去调整构造方法、onMeasure、onLayout的数据
实际开发中自定义View如果按实现方式大概可以分为三种:自绘、组合、继承
1,自绘View:大部分是继承View,利用onDraw完成绘制,如:圆形进度条;
2,组合View:继承ViewGroup,简单场景直接使用xml已有布局,复杂场景onMeasure/onLayout/onDraw都可能用到,如:日历控件;
3,继承View:继承特定View,使用父控件的特性,并进行一定的扩展,如:自定义WebView;
在自定义View时,我们通常会去重写onDraw()方法来绘制View的显示内容。如果该View还需要使用wrap_content属性,那么还必须重写onMeasure()方法。另外,通过自定义attrs属性,还可以设置新的属性配置值。 在View中通常有以下一些比较重要的回调方法:
onFinishInflate() 回调方法,当应用从XML加载该组件并用它构建界面之后调用的方法
onMeasure() 检测View组件及其子组件的大小
onLayout() 当该组件需要分配其子组件的位置、大小时
onSizeChange() 当该组件的大小被改变时
onDraw() 当组件将要绘 制它的内容时
onKeyDown 当按下某个键盘时
onKeyUp 当松开某个键盘时
onTrackballEvent 当发生轨迹球事件时
onTouchEvent 当发生触屏事件时
onWindowFocusChanged(boolean) 当该组件得到、失去焦点时
onAtrrachedToWindow() 当把该组件放入到某个窗口时
onDetachedFromWindow() 当把该组件从某个窗口上分离时触发的方法
onWindowVisibilityChanged(int): 当包含该组件的窗口的可见性发生改变时触发的方法
自定义控件更新方法
1、invalidate():
invalidate方法用于在UI线程中请求重绘视图。当我们希望在主线程中更新UI时,可以在UI线程中直接调用invalidate()方法。
内部实现:调用了invalidate方法后,为该View添加一个标记位,同时不断向父容器请求刷新,父容器通过计算得出自身需要重绘的区域,直到传递到ViewRootImpl中,最终触发performTraversals方法,开始View树重绘流程(只绘制需要重绘的视图)
2、postInvalidate():
postInvalidate方法用于在非UI线程中请求重绘视图。如果我们希望在非UI线程中更新UI,就需要使用postInvalidate()方法。
内部实现:当调用postInvalidate()方法时,系统会通过Handler将重绘任务添加到主线程的消息队列中。在UI线程的消息循环中,当处理到该消息时,会触发invalidate()方法来进行实际的重绘操作。这样可以确保在非UI线程中请求的重绘操作在主线程中执行,避免多线程并发导致的问题。
3、requestLayout():
requestLayout方法用于请求重新布局视图。当我们希望更新视图的尺寸或位置时,需要调用requestLayout()方法。该方法会触发视图层次结构的重新测量、布局和绘制过程。例如,当修改了视图的宽度、高度、位置或其他布局参数时,应调用requestLayout()方法来请求重新布局。
调用invalidate方法只会执行onDraw方法;调用requestLayout方法只会执行onMeasure方法和onLayout方法,并不会执行onDraw方法。
所以当我们进行View更新时,若仅View的显示内容发生改变且新显示内容不影响View的大小、位置,则只需调用invalidate方法;若View宽高、位置发生改变且显示内容不变,只需调用requestLayout方法;若两者均发生改变,则需调用两者,按照View的绘制流程,推荐先调用requestLayout方法再调用invalidate方法。
invalidate 和 postInvalidate:invalidate方法只能用于UI线程中,在非UI线程中,可直接使用postInvalidate方法,这样就省去使用handler的烦恼。
一、对现有控件进行扩展
这是一个非常重要的自定义View方法,它可以在原生控件的基础上进行拓展,增加新的功能、修改显示的UI等。一般来说我们可以在onDraw()方法中对原生控件行为进行拓展。 以一个TextView为例,使用Canvas对象来进行图像的绘制,然后利用Android的绘图机制,可以绘制出更加复杂丰富的图像。比如可以利用LinearGradient Shader和Matrix来实现一个动态的文字闪动效果,要想实现这一个效果,可以充分利用Android中Paint对象的Shader渲染器。通过设置一个不断变化的LinearGradient,并使用带有该属性的Paint对象来绘制要显示的文字。首先,在onSizeChanged()方法中进行一些对象的初始化工作,并根据View的宽度设置一个LinearGradient渐变渲染器,代码如下:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh){
super.onSizeChanged(w, h, oldw, oldh);
if(mViewWidth == 0) {
mViewWidth = getMeasuredWidth();
if(mViewWidth > 0) {
mPaint = getPaint();
// 创建mLinearGradient渐变渲染器并填充到画笔mPaint
mLinearGradient = new LinearGradient(0, 0, mViewWidth, 0,
new int[] {Color.BLUE, 0xffffffff, Color.BLUE},
null, Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
mGradientMatrix = new Matrix();
}
}
}
其中最关键的就是使用getPaint()方法获取当前绘制TextView的Paint对象,并给这个Paint对象设置原生TextView没有的LinearGradient属性。最后,在onDraw()方法中,通过矩阵的方式来不断平移渐变效果,从而在绘制文字时,产生动态的冷却效果,代码如下所示:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mGradientMatrix != null) {

最低0.47元/天 解锁文章
1029





