自定义View的一些方式有必要了解一下(来自网友的总结):
通常情况下,Android实现自定义控件无非三种方式。
Ⅰ、继承现有控件,对其控件的功能进行拓展。
Ⅱ、将现有控件进行组合,实现功能更加强大控件。
Ⅲ、重写View实现全新的控件
首先,我们要明白在什么样的情况下,需要重写View来实现一种全新的控件,一般当我们遇到了原生控件无法满足我们现有的需求的时候,我们此时就可以考虑创建一个全新的View来实现我们所需要的功能。创建一个全新View实现自定义控件,无非分成这么几步:
Ⅰ、在OnMeasure()方法中,测量自定义控件的大小,使自定义控件能够自适应布局各种各样的需求。
Ⅱ、在OnDraw()方法中,利用哼哈二将(Canvas与Paint)来绘制要显示的内容。
Ⅲ、在OnLayout()方法中来确定控件显示位置。
Ⅳ、在OnTouchEvent()方法处理控件的触摸事件。
说明:其实前两种我们用的比较多。其中第二种应该是最多的,然后遇到原生控件缺失的效果,我们一般就是重新某个方法,比如scroll滚动,有时候需要重新。比如多个fragment做切换,如何避免每次重新加载。 然后就是自定义View,重新相关的绘制,监听,测量等 :小白的我们太懒,又不好好学习,总是用第三方,导致这个知识点严重缺失,所以有必要补补!
官方文档走起:https://developer.android.google.cn/reference/android/view/View?hl=zh-cn
View
public class View
extends Object implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource
java.lang.Object
↳ android.view.View
Known direct subclasses
AnalogClock, ImageView, KeyboardView, MediaRouteButton, ProgressBar, Space, SurfaceView, TextView, TextureView, ViewGroup, ViewStub
Known indirect subclasses
AbsListView, AbsSeekBar, AbsSpinner, AbsoluteLayout, ActionMenuView, AdapterView, AdapterViewAnimator, AdapterViewFlipper, AppWidgetHostView, AutoCompleteTextView, Button, CalendarView, and 52 others.
翻译:View继承自Object,哇哦!Java万物都是继承Object....实现了Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource. 同时,我们知道直接继承View的有AnalogClock, ImageView, TextView, ViewGroup等。 间距继承自View的还有AbsListView, AbsoluteLayout, Reycleview等。
顺便看下View继承的那几个接口,多了解一点(后面我们可以尝试去重写这些接口,看看是否有点击事件、刷新事件等的回调, 具体比较细的解释可以慢慢深入):
继续View的下一段介绍:
This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling.
View is the base class for widgets, which are used to create interactive UI components (buttons, text fields, etc.).
The ViewGroup subclass is the base class for layouts, which are invisible containers that hold other Views (or other ViewGroups) and define their layout properties.
Developer Guides
For information about using this class to develop your application's user interface, read the User Interface developer guide.
翻译:这些类呈现了用户界面的基本构成。视图占据了屏幕的矩形区域,负责绘制和事件的处理。ViewGroup继承自该基类,可以容纳其他视图并定义其布局属性。
开发指导:更多信息可以看相关的用户界面开发的指导 用户界面 | Android Developers
接着下一段:
Using Views
All of the views in a window are arranged in a single tree. You can add views either from code or by specifying a tree of views in one or more XML layout files. There are many specialized subclasses of views that act as controls or are capable of displaying text, images, or other content.
Once you have created a tree of views, there are typically a few types of common operations you may wish to perform:
Set properties: for example setting the text of a TextView. The available properties and the methods that set them will vary among the different subclasses of views. Note that properties that are known at build time can be set in the XML layout files.
Set focus: The framework will handle moving focus in response to user input. To force focus to a specific view, call requestFocus().
Set up listeners: Views allow clients to set listeners that will be notified when something interesting happens to the view. For example, all views will let you set a listener to be notified when the view gains or loses focus. You can register such a listener using setOnFocusChangeListener(android.view.View.OnFocusChangeListener). Other view subclasses offer more specialized listeners. For example, a Button exposes a listener to notify clients when the button is clicked.
Set visibility: You can hide or show views using setVisibility(int).
Note: The Android framework is responsible for measuring, laying out and drawing views. You should not call methods that perform these actions on views yourself unless you are actually implementing a ViewGroup.
翻译:大概就是说视图的基本使用,你可以设置属性,设置焦点,设置可见性。通过xml设置,或者代码设置都可以。
然后到了我们比较关键的自定义View:
Implementing a Custom View
To implement a custom view, you will usually begin by providing overrides for some of the standard methods that the framework calls on all views. You do not need to override all of these methods. In fact, you can start by just overriding onDraw(android.graphics.Canvas).
翻译:继承并自定义一个View,通常都是提供标准方法的重载开始。你也没有必要重载所有的这些方法。事实上,你可以仅仅重载onDraw(android.graphics.Canvas) 方法即可。
所以我们自定义View可以从这里开始实践。下一篇我们就准备从这个地方开始~~
我们接着看下官网后面的内容,先大体过一下...可能不一定都看完...
后面就是逐步结束绘制相关的概念,什么ids, layout, touch等
然后还有一些公共方法
总之,方法很多,属性很多,一抹多,够看一阵子了!
构造函数了解下就短暂结束这篇初识篇,以便正式开始我们的绘制,测量,事件处理等...
View
added in API level 1
public View (Context context)
Simple constructor to use when creating a view from code.
Parameters
context Context: The Context the view is running in, through which it can access the current theme, resources, etc.
View
added in API level 1
public View (Context context,
AttributeSet attrs)
Constructor that is called when inflating a view from XML. This is called when a view is being constructed from an XML file, supplying attributes that were specified in the XML file. This version uses a default style of 0, so the only attribute values applied are those in the Context's Theme and the given AttributeSet.
The method onFinishInflate() will be called after all children have been added.
Parameters
context Context: The Context the view is running in, through which it can access the current theme, resources, etc.
attrs AttributeSet: The attributes of the XML tag that is inflating the view.
This value may be null.
View
added in API level 1
public View (Context context,
AttributeSet attrs,
int defStyleAttr)
Perform inflation from XML and apply a class-specific base style from a theme attribute. This constructor of View allows subclasses to use their own base style when they are inflating. For example, a Button class's constructor would call this version of the super class constructor and supply R.attr.buttonStyle for defStyleAttr; this allows the theme's button style to modify all of the base view attributes (in particular its background) as well as the Button class's attributes.
Parameters
context Context: The Context the view is running in, through which it can access the current theme, resources, etc.
attrs AttributeSet: The attributes of the XML tag that is inflating the view.
This value may be null.
defStyleAttr int: An attribute in the current theme that contains a reference to a style resource that supplies default values for the view. Can be 0 to not look for defaults.
View
added in API level 21
public View (Context context,
AttributeSet attrs,
int defStyleAttr,
int defStyleRes)
Perform inflation from XML and apply a class-specific base style from a theme attribute or style resource. This constructor of View allows subclasses to use their own base style when they are inflating.
When determining the final value of a particular attribute, there are four inputs that come into play:
Any attribute values in the given AttributeSet.
The style resource specified in the AttributeSet (named "style").
The default style specified by defStyleAttr.
The default style specified by defStyleRes.
The base values in this theme.
Each of these inputs is considered in-order, with the first listed taking precedence over the following ones. In other words, if in the AttributeSet you have supplied
解释:(总共四个构造函数,第四个构造函数是Android5.0才有的东西。所以如果是旧版的暂时还不支持。用前三种就行。不过大部分调用的比较多的就是第二个。)
A第一个构造函数:Simple constructor to use when creating a view from code.代码创建是会走
B第二个构造函数:大概就是说通过xml定义控件会走该构造方法,另外所有的子控件加载完会走onFinishInflate。style将默认是0.
C第三个构造函数:(**See also: **View(Context, AttributeSet) 所以就是第二个构造函数,该构造函数通过第二个构造函数调用。 不过我们可以自定义一些风格,属性,然后通过获取这些自定义属性在该构造函数里面做一些事情。
D第四个构造函数: 通过第三个函数调用,传入我们自定义的样式资源。
总结:总得来说就是定义了一些规则和方便我们处理自定义控件的方式,然后方便我们进行自定义属性和样式的设置。以前我们都是迷惑,需要实现这么多构造函数么?每个构造函数参数又怎么写了?随着我们自定义View的深入学习和实践相信越来越清晰了。
一般写法是:
public class MyTextView extends View {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MyTextView(Context context) {
this(context, null);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MyTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr,0);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
///< TODO
}
@Override
protected void onDraw(Canvas canvas) {
}
什么情况下会调用第一个,第二个,第三个,第四个呢?- 我们可以自定义的View,然后每个构造函数打印一下生命周期看看会发生什么:
比如View04.java
package me.heyclock.hl.customcopy;
import android.content.Context;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
public class View04 extends View {
private static final String TAG = View03.class.getName();
public View04(Context context) {
super(context);
Log.e(TAG, "View04 0000");
}
public View04(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
Log.e(TAG, "View04 1111");
}
public View04(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
Log.e(TAG, "View04 2222");
}
public View04(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
Log.e(TAG, "View04 3333");
}
}
然后分别代码创建,以及xml布局:
View04 view04 = new View04(this);
styles.xml如下:
attrs.xml如下:
然后打印发现只有:
也就是如网友所说:
在代码中直接new一个Custom View实例的时候,会调用第一个构造函数.这个没有任何争议.
在xml布局文件中调用Custom View的时候,会调用第二个构造函数.这个也没有争议.
在xml布局文件中调用Custom View,并且Custom View标签中还有自定义属性时,这里调用的还是第二个构造函数.
也就是说,系统默认只会调用Custom View的前两个构造函数,至于第三个构造函数的调用,通常是我们自己在构造函数中主动调用的(例如,在第二个构造函数中调用第三个构造函数).
所以一般简单可以只有第二个构造函数,然后我们再第二个构造函数里面做一些属性的获取就行。我们还是按照常规性做法,都重新,然后层级调用就行。
最后做个总结就是:
混个眼熟 + 构造函数调用第一个第二个(第二个调用第三个,第三个调用第四个) + 自定义属性/样式 = 基本绘制眼熟
有个短暂的了解,我们就先这样。后面我们先熟悉下相关属性的获取的操作(还能继续看看构造函数),方便控件的扩展...