view/viewgroup的绘制机制,和,你可以构建自己的layout

前言:

我们将说点什么:

1.view/viewgroup的绘制机制

你要往一个画布上绘制点东西,那么你需要确认几点:要绘制的东西是什么,它有多大,它应该被绘制到画布的哪个位置

也许在android之前,你动手设计过自己的绘制系统,也许,现在,你可以检验一下,你的和android的,是否相似,哪一个更好

2.如何从头开始,构建自己的layout(不要继承于现有的layout.class),利用我们之前的第1点知识

 

************************************************************正文

1.view/viewgroup的绘制机制

如果要你来设计一个绘制系统,你会怎么来做呢

在这个系统中,你要考虑的第一步往往是这些:

  • 我应该提供一个基类,所有的view都继承于该基类
  • 也许我还应提供一些现有的widget,以便使用我的系统的第三方的开发者使用
  • 一副画往往不止由一个部件组成,各个部件需要按照特定的布局,依次陈列于画布上,那么,我需要向上抽象出布局基类
  • 也许我还可以提供一些现有的布局,以便使用我的系统的第三方的开发者使用
  • ....

我们已经思考了上述的需求,我们得到了基类view,一些已经实现的widget(如button, textview),基类viewgroup,一些已经实现的layout(如linearlayout, relativelayout)

嗯,很好的开始,接下来,思考下面的问题:

1.widget怎么绘制呢?嗯,这是一个问题,我能为第三方开发者做些什么吗?

因为每个widget的绘制都有自身的特点,所以,我不能提供“除了制定流程”之外的任何帮助

android提供了钩子:ondraw

 

2.我们脑海中已经抽象了布局基类,那么,我们需要提供布局的流程

你会怎么来放置你的view东西呢?

a.提供你的方式:你是1个东西放1行;还是所有的东西从左往右摆一行,空间不够了摆下一行

b.让我冥想一些抽象的东西:每个东西的大小是怎么样的

c.我自身可能作为一个新的东西,以让别人来布局我:提供自己的大小

上面讲述的,就是android考虑的:

a的实现通过onlayout来完成,在该方法中,你将被layout,如果是非viewgroup,被layout就意味着进行绘制,如果是viewgroup,被layout就意味着:请你来layout你的孩子

b和c的实现通过onmeasure来完成,在该方法中,你将被丈量,如果是非viewgroup,请丈量出自身的大小(举个例子,如果你是textview,请丈量出自身的文字做占用了多少像素),如果是viewgroup,请依次丈量你的孩子(这样,孩子就告诉你,它需要多少空间),并在脑子中浮现出你的布局style,假设一下,你按照你的style来layout完你的所有孩子后,你需要多大的空间

 

让我们再针对于 丈量 来讨论的更多一点

view可以使用之下方式,来表明自己占用多少空间:wrap_content, fill_parent/match_parent, 一个显式的像素值

当要决定view占用了多少空间时,onmeasure就会被调用,而之前你声明的方式,一样会作为参数传递给你

并且,还给了你一些额外的东西:父亲告诉你:当前还剩下多少可分配的空间

这两部分信息被封装成为了measurespec,并做了稍许改变,传递给了你

举个例子

如果你声明的是wrap_content,那么父亲传递给你的measurespec为[at_most,剩余的空间]

如果是match_parent,那么,相应的就是[exactly,剩余的空间]

...

你拿到这个spec的时候,可以完全忽视它,当然,最佳实践是,你参考它们

并在最后丈量出自己:setMeasuredDimension

然后父亲就知道了你的丈量结果:childview.getmeasuredwidth/height

然后父亲就根据你的结果,再根据自身的布局style,丈量出自身的大小

 

让我们再针对于 布局东西 来讨论的更多一点

这里的操作类似于这样:

我已经知道了这个东西的measured dimension,然后我也明白自己的style,

我当然知道,按照我的style,这个东西要被layout到什么位置(相对于我自身的0,0点的坐标)

然后,我调用childview.layout

that's it

2.构建自己的layout

如果你完全清楚我们之前讲的,那么你将很容易构建自己的layout

我提供了,我的style:

将所有孩子放置到一行上,如果该行已经没有空间放置孩子了,那么该孩子放入到下一行

do you get it

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import com.ql.testMyViewGroup.R;

public class MyViewGroup extends ViewGroup {

    public MyViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        TypedArray taMyAttr = context.obtainStyledAttributes(attrs,
                R.styleable.mystyleattr);
        gap = taMyAttr.getInt(R.styleable.mystyleattr_gap, 10);

        taMyAttr.recycle();
    }

    public MyViewGroup(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }

    /**
     * 孩子之间的gap,不考虑孩子的margin(因为myviewgroup不是linearlayout,所以lp也不包含margin)
     */
    private int gap;

    private int maxWidth;

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        maxWidth = MeasureSpec.getSize(widthMeasureSpec);

        /**
         * 假设所有的padding都是相同的
         */
        int padding = getPaddingBottom();

        int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        int width = -gap;
        int height = padding + padding;
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View view = getChildAt(i);
            view.measure(spec, spec);

            width += gap + view.getMeasuredWidth();
            if (i == 0) {
                height += view.getMeasuredHeight();
            }

            if (width > maxWidth - padding - padding) {
                width = -gap;
                height += view.getMeasuredHeight() + gap;
            }
            System.out.println("---dimension:" + view.getWidth() + ","
                    + view.getHeight());
            System.out.println("---measured dimension:"
                    + view.getMeasuredWidth() + "," + view.getMeasuredHeight());
        }
        setMeasuredDimension(maxWidth, height);
        System.out.println("---self dimension:" + getWidth() + ","
                + getHeight());
        System.out.println("---self measured dimension:" + getMeasuredWidth()
                + "," + getMeasuredHeight());
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        System.out.println("on layout");
        // TODO Auto-generated method stub
        int count = getChildCount();
        int padding = getPaddingBottom();

        int cl = padding;
        int ct = padding;
        int height = 0;
        for (int i = 0; i < count; i++) {
            View view = getChildAt(i);
            if (cl + gap + view.getMeasuredWidth() + padding > maxWidth) {
                cl = padding;
                ct += height + gap;
            }
            height = view.getMeasuredHeight();
            view.layout(cl, ct, cl + view.getMeasuredWidth(),
                    ct + view.getMeasuredHeight());
            cl += view.getMeasuredWidth() + gap;

            System.out.println("dimension:" + view.getWidth() + ","
                    + view.getHeight());
            System.out.println("measured dimension:" + view.getMeasuredWidth()
                    + "," + view.getMeasuredHeight());
        }

        System.out.println("self dimension:" + getWidth() + "," + getHeight());
        System.out.println("self measured dimension:" + getMeasuredWidth()
                + "," + getMeasuredHeight());
    }
}



 

猜你喜欢

转载自baosu.iteye.com/blog/1843025