概述
前言:
Android开发的小伙伴都知道自定义view是一个让人又爱又恨的东西,爱的原因是因为自定义view可以实现项目的千变万化的需求,恨是因为自定义view有难么一点难以理解,一副雾里看花的感觉,当然这是我自己的个人感受,所以也是为了让自己对自定义view能够有更深入的理解和体会,写下该博客。
1.View是什么?
View正如它的字义理解,就是视图的意思,android中所有的控件都是继承于View这个类的。无论是viewgroup还是简单的widget控件,比如button,所以如果要真正搞明白自定义view,一定要知道view内部涉及到的主要方法和如何加载的过程。
2.View的加载
一个Activity将view加载到屏幕上,代码实现很简单,只需要调用
setContentView(R.layout.activity_main);
这个方法是android的一个封装,里面的流程大致为:
第一步:
LayoutInflater layoutInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
获取一个LayoutInflater 对象,然后调用inflate方法:
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final AttributeSet attrs = Xml.asAttributeSet(parser);
mConstructorArgs[0] = mContext;
View result = root;
try {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
final String name = parser.getName();
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("merge can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, attrs);
} else {
View temp = createViewFromTag(name, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
rInflate(parser, temp, attrs);
if (root != null && attachToRoot) {
root.addView(temp, params);
}
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (IOException e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
}
return result;
}
}
其中的rInflate的方法为:
private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)
throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
parseRequestFocus(parser, parent);
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
} else {
final View view = createViewFromTag(name, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs);
viewGroup.addView(view, params);
}
}
parent.onFinishInflate();
}
以上代码的主要作用是将xml布局文件转化为View类,然后加载在页面显示出来。
同时,我们在获取到LayoutInflate对象后有个重载方法:
inflate(int resource, ViewGroup root, boolean attachToRoot)
有些人对这些参数不同值时代表的意义不是很理解,我觉得其实从参数类的名字的意义更好记忆。
resource:表示加载布局的资源id。
root:表示给该resource添加一个父布局。
attachToRoot:true表示root为该resource的父布局,false表示root不为该resource的父布局。
所以:root为null时,resource不主动添加一层父布局,attachToRoot设置的值也就不起作用。
root不为null时,resource主动添加一层父布局,attachToRoot为true时表示添加成功,false表示添加失败。
加载的过程中会涉及到很多重要的方法:
onMeasure();测量控件大小
onMeasure(int widthMeasureSpec, int heightMeasureSpec);
参数是一个MeasureSpec类型,这个类是由一个高2位表示测量模式,低30位表示测量大小组成的。其中,测量类型mode的类型有三种:
EXACTLY
表示父视图希望子视图的大小应该是由specSize的值来决定的,一般对应的是具体的数值或者match_parent。
AT_MOST
表示子视图最多只能是specSize中指定的大小,一般对应的是warp_content。
UNSPECIFIED
表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。这种情况比较少见,不太会用到。
在onMeasure方法中,最后调用setMeasuredDimension(widthSize, heightSize); 这个方法也是super中调用的方法,是真正起作用的方法。调用完该方法后,我们使用getMeasuredWidth()和getMeasuredHeight()来获取视图测量出的宽高才是有值得。对于继承ViewGroup的控件,onMeasure要用一个循环来测量子view的大小,比如像我们熟知的RelativeLayout和LinearLayout都对该方法进行了重写。
onLayout();控件的位置布局
一般继承ViewGroup的控件需要对onLayout方法进行重写来显示子view的位置逻辑控件。调用完该方法后我们就可以调用getWidth()方法和getHeight()方法来获取视图的宽高了,该宽高是坐标相减计算得到的。
onDraw();控件绘制
对于继承自view的控件,onDraw方法可以用来绘制我们想要的效果的东西。
onFinishInflate();从XML加载完毕后回调
onSizeChange();控件大小发生改变时,这两个方法多在组合控件中使用。
了解完了上面的简单知识点后,下篇文章会以三个具体的例子来说明自定义view的三种方式。
最近搞了个Android技术分享的公众号,欢迎关注投稿。
最后
以上就是无辜眼神为你收集整理的Android自定义view(一) 知识理解的全部内容,希望文章能够帮你解决Android自定义view(一) 知识理解所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复