已经有一段时间没有给大家更新博客了,貌似自从学校的实训一开始就一直没有心思去学新的东西和写博客,因为这段时间一直都有很多事情要忙,而且笔者马上就要开始实习工作了,可能心理上也是有一定的压力哈哈。现在事情都处理的差不多了,可以重操旧业专心学习了!!!!!
今天给大家带来的是如何关于自定义View的文章,之后笔者会将动画和自定义View的文章陆陆续续整合到一起,方便大家阅读。
目录
自定义View简介
View、ViewGroup区别和联系
实现最简单的自定义View
自定义View
自定义ViewGroup
还有一个小问题
在做Android项目中,视图这块,我们肯定都使用过View,像Button、ImageView、CheckBox、RadioButton、ProgressBar和SeekBar等等这些控件,都是从View继承的。
其实说白了,上面这些我们使用的,以及有些我们没有使用过的控件,其实都是人写出来的(这话说的可能有点2b),我的意思是,无论是以上的这些控件,还是我们将要提到的自定义控件,其实都是我们通过一系列方法将它定义出来,最终显示到我们的视图中。
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public class ImageView extends View
public class Button extends TextView
根据我们最上面说到的,所有的这些的顶级父类都是View,或者说有些控件是在某些通过已经继承了View的控件之上再继承的方式实现自定义View。
我将自定义View分为三种类型:
上面除了View之外还涉及到一个ViewGroup,虽然ViewGroup是自View继承的,但是我们还是将它分开来说。因为几乎所有的视图类(包括控件类和布局类)都是继承View的,所以说View是所以视图控件的根不为过;但是还有一种情况,我们将那些控件类(就像上述的Button、ImageView等)统称为View。这只是我们行业内的一种习惯吧,并不是很规范。因为严格的来说ViewGroup也是继承自View类。
当然ViewGroup类大家没怎么少见,最为普遍的就是布局:六大布局。还有ToolBar、ListView等等。他们区别于View的最显著特征就是,可以在里面添加字View(这里的View也包括ViewGroup哦)。
我们通过代码分别实现自定义View和自定义ViewGroup:
在使用系统的一些控件中,一般情况下我们都是在layout.xml文件中添加标签:
以TextView为例:在使用时候我们可以通过在这里面设置TextView内的一系列属性来实现文字大小、颜色、大小写之类的改变。
是不是很神奇,没关系,今天通过自定义View我们也会学到这种方式。
1.首先需要创建一个自定义的类继承自View。
public class MyView extends View {
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
名字起得很随性:MyView。
注意到我实现了三个构造方法,对于View来说总共是有4个,但是第四个不是很常用,我们只需要看前三个就好。
这三个构造方法每个都比上一个多一个参数,依次解释一下这三个参数的含义:
一般来说只是用到前两个构造方法,而前两个构造方法也是有所不同:
第一个构造方法是适用于通过new创建的自定义View对象,而第二种构造方法适用于在layout.xml中创建。
这里有一个规范的写法:
public class MyView extends View {
public MyView(Context context) {
this(context,null);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
注意到前两个super被改为了this,而且都添加了新的参数。我们让第一个构造方法调用第二个,第二个调用第三个,这样一来无论我们使用哪种方法创建的MyView对象,最后都会进入第三个构造方法中。也就是说所有的逻辑代码全部都写到第三个方法中就可以了。
2.然后需要在values文件目录下创建名为attrs的.xml文件。
3.然后在attrs.xml文件中添加declare-styleable标签,name指定为我们java中自定义的类名。
4.在declare-styleable下添加属性
在里面我们可以自己定义属性的名字,和属性的类型:
可以看到属性有很多的类型:
我们设置一个名为viewBackground的属性,顾名思义是指这个控件的背景,所以我们指定为reference类型。
5.关于设置的部分我们就完成了,下面我们看看如何获取到这些属性的值。
还记得构造方法中的第二个参数AttrsbuteSets吗,我们说是通过他获取到属性值的,
TypedArray attr = context.obtainStyledAttributes(attrs,R.styleable.MyView);
通过上下文context的obtainStyledAttributes方法获取到属性集合。这个方法有四个重载方法,这个重载方法的参数第一个是构造方法中第二个参数,第二个参数就是我们在attrs.xml中添加的declare-styleable的属性名。
那么获取到了这个属性集合,该如何使用呢?
TypedArray中为我们提供了一系列方法去获取属性值。
都是getXXX的格式,有两个参数,第一个参数使我们declare-styleable的attr标签下的name,第二个为默认不设置该属性的情况下,他的属性值是多少。
可以看到这些get方法都是对应着我们attr的format属性值的。
int background = attr.getResourceId(R.styleable.MyView_viewBackground,R.color.colorAccent);
现在我们获取到了背景颜色,如果不设定的话就是这个粉色。接下来把它添加到背景中。
获取属性值的方法是统一的,但是如何使用就看我们自己了,你可以打印一个Log什么都不做-。=
setBackgroundResource(background);
我们通过setBackgroundResource方法添加背景颜色。
好了,这是最最最最简答的自定义控件了,因为我们什么都没干,这是添加了一个背景色的属性。现在我们看看他的样子是什么。
可以看到他是有这个属性值的,我们先不设定,看看是不是粉色:
没毛病,就是粉色,现在我们给他设置成深蓝色:
效果很明显,这样一个改变颜色的自定义view就让我们做出来了(虽然没什么用,但是这是我们伟大的第一次实践,通过这种方法我么我们可以给自定义的view添加各式各样的属性。)
上面说完了自定义View,这个自定义ViewGroup就简单很多了。
悄悄问大家一下,大家有没有在布局文件中实现一个自定义的标题栏:左面一个按钮,右面一个按钮,然后中见是标题,这样写出来的代码不但死板费事,而且拓展性几乎为0。下一次在使用就通过复制粘贴这一大坨代码,然后修改里面的值,相信我,大家以前都这么干过(反正我之前就是一直这样的。),下面我们通过自定义ViewGroup的方式,来实现这个效果:
public class MyToolBar extends LinearLayout {
public MyToolBar(Context context) {
this(context,null);
}
public MyToolBar(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public MyToolBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
前几步骤跟上面的一样,注意在这里我们继承的不是ViewGroup,而是他的实现类之一LinearLayout(这里面有坑,后面会讲到)。
与上面不同的是,我们还需要给MyToolBar创建一个自己的布局文件。
贼丑的一个布局,大家凑乎这看吧。
然后需要在MyToolBar构造方法中调用如下方法:
public MyToolBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
LayoutInflater.from(context).inflate(R.layout.mytoolbar_layout,this,true);
}
我们给布局绑定父类为MyToolBar,说明这就是我们MyToolBar的布局了。
我们还有三个控件,现在也写到里面:
public MyToolBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
LayoutInflater.from(context).inflate(R.layout.mytoolbar_layout,this,true);
leftButton = findViewById(R.id.but_left);
rightButton = findViewById(R.id.but_right);
title = findViewById(R.id.tv_title);
}
现在我们将他添加到布局中,看一看效果:
可以看到这个跟我们mytoolbar_layout布局中一模一样,当然界面很丑,而且颜色也不好看,关于如何添加自定义属性的方法在自定义View中,大家可以自行查看,然后设置属性,最后设置出自己喜欢的标题栏。
有的读者可能会问了,那两个按钮想设置监听事件该怎么办呢。很简单,在MyToolBar中添加两个public方法,用来给两个按钮设置监听器不就好了。(这个不用给代码了吧,大家都会懂)。
有没有读者注意到,我们在上面自定义的View中,设定为了wrap_content,但是还是沾满了全屏呢。这个问题在下一篇文章中会被迎刃而解。
好了,今天的文章就到此结束了,喜欢的朋友希望多多支持。!!