Android自定义控件

在实际项目的自定义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 和 postInvalidateinvalidate方法只能用于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) {
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值