`
包su
  • 浏览: 31172 次
  • 来自: 南京
社区版块
存档分类
最新评论

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());
    }
}



 

 

 

分享到:
评论

相关推荐

    自定义开关View绘制流程

    测量 摆放 绘制 * measure -&gt; layout -&gt; draw * | | | * onMeasure -&gt; onLayout -&gt; ... * onMeasure() (指定自己的宽高, 所有子View的宽高)-&gt; onLayout() (摆放所有子View) -&gt; onDraw() (绘制内容)

    View的绘制流程

    View的绘制流程主要包括measure,layout,draw三大流程,measure用来确定view的测量宽/高,layout用来确定view的最终宽/高和四个顶点的位置,而draw则将View绘制到屏幕上 Measure 如果只是一个原始的View,那么通过...

    深入理解Android中View绘制的三大流程

    即测量、布局和绘制,其中measure确定View的测量宽高,layout根据测量的宽高确定View在其父View中的四个顶点的位置,而draw则将View绘制到屏幕上,这样通过ViewGroup的递归遍历,一个View树就展现在屏幕上了。...

    TileView:TileView是android.view.ViewGroup的子类,它异步显示,平移和缩放基于图块的图像。 插件可用于标记,热点和路径绘制等功能

    该项目不再维护。... 还要注意的是,通用(2-D)ScrollView类以及诸如ScalingScrollView的相关类(它是2D通用ScrollView的子类,但还管理缩放和缩放手势)现在是它自己的存储库: ,并与implementation '...

    一个自定义的ViewGroup显示阴影效果

    源码shadow-layout,ShadowLayout是一个自定义的ViewGroup,它能让自己包含的view显示出阴影效果。跟CardView相比,ShadowLayout有以下优势:可以绘制圆形阴影,可以设置阴影位置,可以设置阴影的深浅,可以设置...

    Android中自定义View的实现方式总结大全

    Android自定义view是什么 在我们的日常开发中,很多时候系统提供的view是无法满足我们的需求的,例如,...继承ViewGroup派生的特殊Layout,主要用于实现自定义布局,也需要自己适配边距等  3.继承特定的View(如Text

    Android实现Z轴布局效果

    如果需要在布局中创造一个层叠的概念,那么使用Android系统中的ViewGroup是不够的,但是可以通过改变ViewGroup的绘制顺序实现 代码下载 继承自FrameLayout FrameLayout已经帮我们实现了子View的measure和layout过程,...

    Android UI组件实例集合

    而且,由于你 的程序在设计初期就是已经支持intents的了,当Google的intents出来的时候,你可以很方便的直接他们的intents。还有可能的是 Google可能会借用一些OpenIntents开发的intents。无论如何,如果你的程序...

    ThinkMap-实现Android端的简易思维导图。可以保存数据。编辑树形图。.zip

    }View的连线要实现对View和View的连线,只要在View的位置定了之后,就进行画线即可。用Sketch画个演示如下:其中线为一个贝塞尔曲线。代码如下: @Override  protected void dispatchDraw(Canvas canvas) { ...

    Android 自定义控件详解及实例代码

    Android系统的视图结构的设计也采用了组合模式,即View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类。 View定义了绘图的基本操作 基本操作由三个函数完成:measure()、layout()、draw(),其内部又分别...

    view_shaper:一个库,可帮助在Android中创建形状的视图和布局

    如果您可以编写表示所需形状的路径,则可以使用ViewShaper裁剪路径后的任何View并绘制阴影。 当所需形状比圆形或正方形复杂并且您无法创建XML形状用作View背景时,这是一个很好的解决方案。 实现方式: 创建一个...

Global site tag (gtag.js) - Google Analytics