前言:
我们将说点什么:
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()); } }
相关推荐
测量 摆放 绘制 * measure -> layout -> draw * | | | * onMeasure -> onLayout -> ... * onMeasure() (指定自己的宽高, 所有子View的宽高)-> onLayout() (摆放所有子View) -> onDraw() (绘制内容)
View的绘制流程主要包括measure,layout,draw三大流程,measure用来确定view的测量宽/高,layout用来确定view的最终宽/高和四个顶点的位置,而draw则将View绘制到屏幕上 Measure 如果只是一个原始的View,那么通过...
即测量、布局和绘制,其中measure确定View的测量宽高,layout根据测量的宽高确定View在其父View中的四个顶点的位置,而draw则将View绘制到屏幕上,这样通过ViewGroup的递归遍历,一个View树就展现在屏幕上了。...
该项目不再维护。... 还要注意的是,通用(2-D)ScrollView类以及诸如ScalingScrollView的相关类(它是2D通用ScrollView的子类,但还管理缩放和缩放手势)现在是它自己的存储库: ,并与implementation '...
源码shadow-layout,ShadowLayout是一个自定义的ViewGroup,它能让自己包含的view显示出阴影效果。跟CardView相比,ShadowLayout有以下优势:可以绘制圆形阴影,可以设置阴影位置,可以设置阴影的深浅,可以设置...
Android自定义view是什么 在我们的日常开发中,很多时候系统提供的view是无法满足我们的需求的,例如,...继承ViewGroup派生的特殊Layout,主要用于实现自定义布局,也需要自己适配边距等 3.继承特定的View(如Text
如果需要在布局中创造一个层叠的概念,那么使用Android系统中的ViewGroup是不够的,但是可以通过改变ViewGroup的绘制顺序实现 代码下载 继承自FrameLayout FrameLayout已经帮我们实现了子View的measure和layout过程,...
而且,由于你 的程序在设计初期就是已经支持intents的了,当Google的intents出来的时候,你可以很方便的直接他们的intents。还有可能的是 Google可能会借用一些OpenIntents开发的intents。无论如何,如果你的程序...
}View的连线要实现对View和View的连线,只要在View的位置定了之后,就进行画线即可。用Sketch画个演示如下:其中线为一个贝塞尔曲线。代码如下: @Override protected void dispatchDraw(Canvas canvas) { ...
Android系统的视图结构的设计也采用了组合模式,即View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类。 View定义了绘图的基本操作 基本操作由三个函数完成:measure()、layout()、draw(),其内部又分别...
如果您可以编写表示所需形状的路径,则可以使用ViewShaper裁剪路径后的任何View并绘制阴影。 当所需形状比圆形或正方形复杂并且您无法创建XML形状用作View背景时,这是一个很好的解决方案。 实现方式: 创建一个...