一篇文章带你走近Android自定义view

Posted 计蒙不吃鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一篇文章带你走近Android自定义view相关的知识,希望对你有一定的参考价值。

系列文章目录

一篇文章带你走近android自定义view



前言

从专科到本科,目前本科大四,已经是学习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效果)

Android自定义view之3D正方体

如需继续深入还请了解openGL相关内容。

十二 、关于SVG

Android利用SVG实现动画效果

十三 、上一个简单github案例

Android线条等待动画JMWorkProgress(可添加依赖直接使用)

十四 、还没来得及具体写的(关键词)

贝塞尔曲线,事件分发机制。枚举(可在框架中用于确定动画状态)

十五 、两道面试相关八股(根据本人面试大厂整理)

1.View绘制流程

View的绘制是从 ViewRootImplperformTraversals()方法开始,从最顶层的 View(ViewGroup)开始逐层对每个 View进行绘制操作 。

View 绘制中主要流程分为measure,layout, draw 三个阶段。

measure :根据父 view 传递的 MeasureSpec 进行计算大小, 自定义View的过程中都会在onMeasure中进行宽高的测量,这个方法会从父布局中接收两个参数 widthMeasureSpacheightMeasureSpac,所以子布局的宽高大小需要受限于父布局。

layout :根据 measure 子 View 所得到的布局大小和布局参数,将子View放在合适的位置上, 结合源码可知 layout()会将四个位置参数传递给 setOpticalFrame()或者 setFrame(),而 setOpticalFrame()内部会调用 setFrame(),所以最终通过 setFrame()确定 ViewViewGroup中的位置。位置确定完毕会调用 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的主要内容,如果未能解决你的问题,请参考以下文章

Carson带你学Android:自定义View Canvas类使用教程

Carson带你学Android:深入解析自定义View工作流程

Android LayoutInflater原理分析,带你一步步深入了解View

Android LayoutInflater原理分析,带你一步步深入了解View

Android LayoutInflater原理分析,带你一步步深入了解View

Android LayoutInflater原理分析,带你一步步深入了解View