我是靠谱客的博主 无辜眼神,最近开发中收集的这篇文章主要介绍Android自定义view(一) 知识理解,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言:

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(一) 知识理解所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部