View绘制系列(3)-自定义View简介

自定义View简介

经过前面两篇文章的介绍,相信大多数同学已经清楚的认识了View,那么我们来看下自定义View这个主题,在小册简介中,我们已经描述了自定义View的目的,同学们还记得吗?用于解决使用系统控件实现不了或实现比较复杂的UI效果。

View简介部分,我们看到不管是XXXLayout还是XXXView,这些系统组件都直接或间接继承自View,那么自定义View的方式自然也有区分,根据继承父类的不同,我们大致可以将自定义View分为三类:

  • 继承自View

  • 继承自ViewGroup

  • 继承自已有控件(ImageViewTextViewLinearLayout等诸如此类系统控件)

有同学们要问了,你前两节讲的是View,这里又提到了ViewGroup,是不是又要讲下ViewGroup的生命周期?当然不需要啦。ViewGroup作为View的子类,它的特点是什么呢?管理其内部的多个子View,其生命周期与View生命周期基本相同,同样要经过构造,绑定,布局,绘制的过程,只不过在布局过程中的测量部分,其需要驱动内部子View进行自身测量,在布局过程的layout部分,其需要驱动子View layout,在绘制过程中其既要完成自身绘制,又要驱动内部子View绘制。从这里可以看出View树的创建过程实际上是一个对布局的深度优先遍历过程,因为整个Activity布局的根布局肯定是ViewGroup的直接或间接子类。

那么自定义View究竟怎么做呢?那么多函数是否都需要重写?答案是不需要,通常情况下我们只需要重写构造,布局及绘制过程即可,一个基本的自定义View代码如下图所示(以继承自View为例):

public class MyCustomView extends View {
   /******   构造过程   ****/
   public MyCustomView(Context context) {
       super(context);
  }

   public MyCustomView(Context context, @Nullable AttributeSet attrs) {
       super(context, attrs);
  }

   public MyCustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
       super(context, attrs, defStyleAttr);
  }

   public MyCustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
       super(context, attrs, defStyleAttr, defStyleRes);
  }

   /******   布局过程中的测量部分   ****/
   @Override
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  }

   /******   布局过程中的布局部分   ****/
   @Override
   protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
       super.onLayout(changed, left, top, right, bottom);
  }

   /******   测量过程   ****/
   @Override
   protected void onDraw(Canvas canvas) {
       super.onDraw(canvas);
  }

}

完成了自定义View的声明,我们怎么使用它呢?有两种方式:

  • 直接使用new关键词调用构造函数创建,随后添加到View树中

  • 直接在xml文件中声明引用

使用new关键词创建并添加到View树的示例代码如下:

public class CustomViewActivity extends AppCompatActivity {

   private MyCustomView mCustomView = null;
   
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       mCustomView = new MyCustomView(this);
       setContentView(mCustomView);
  }
}

使用xml直接声明引用的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<Androidx.coordinatorlayout.widget.CoordinatorLayout 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=".CustomViewActivity">
   
   <com.example.myapplication.MyCustomView
       android:id="@+id/custom_view"
       android:layout_width="match_parent"
       android:layout_height="match_parent"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

对应的Java代码如下:

public class CustomViewActivity extends AppCompatActivity {

   private MyCustomView mCustomView = null;
   
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       setContentView(R.layout.activity_custom_view);

       mCustomView = findViewById(R.id.custom_view);

  }
}

总体来说和系统控件的使用方式是一样的,需要注意的是在xml文件中引用时,一定要使用完整包路径并保证路径和实际一致,在代码重构中,移动了自定义View的包路径后,记得修改xml中引用的路径,否则会爆ClassNotFoundException,这是因为View树的创建过程中,是通过反射进行View对象初始化的。有些好奇的同学要问了,为啥系统组件不用写包路径,自己写的View就需要包路径,这不是赤果果的歧视吗?当然不是,其实对于系统组件而言,其在View树创建时系统默认添加了包路径,所以就不需要自己指定了

哈。

END!

往期推荐

View绘制系列(1)-View简介

OpenCV SDK下载及Android Java环境搭建

玩转花式Loading

原文始发于微信公众号(小海编码日记):View绘制系列(3)-自定义View简介

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/67970.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!