赞
踩
最上层是view类,直接子类是Textview.Imageview,viewgroup;
Textview的直接子类是:Button和EditTEXT ;
Imageview,
ViewGroup的直接子类就是常见的布局,可以理解为容器.
1.imageView 2. progressbar 3.TextView 4.viewGroup
常见的自定义控件有四种:
首先是Android系统中的视图结构是也采用了组合模式,也就是说View是作为所有控件的基类,viewGroup相当于是view的派生类.
所谓的派生类在这里就是指继承view形成的一个类,viewgroup是一个相当于是一个容器,来存放其他的控件的.ViewGroup继承view作为视图容器类.
在View中定义了三个方法,来完成一个控件的展示过程,那就是,先测量控件的宽和高measure()方法,对后面控件的展示过程做到一个初始化参数的地步.后面就是布局,因为View是所有的控件,并且派生出一个ViewGroup的容器类,里面会有子类,我们需要指定子类控件的位置,所以View中还有一个layout()的方法,宽度和高度都有了,还具有了在父控件中位置,我们就可以将控件绘制出来.所以具有一个draw()方法.三个方法中个包含一个子方法,我们一一详解
在View中measure()方法的作用,主要是用来计算视图的大小,也就是计算视图的宽和高,定义的类型是final类型,所以在自定义控件的时候我们最好不要重写measure()方法;关键点就是在measure()中会调用自己的子方法 onMeasure();onMeasure()方法中会调用;setMeasuredDimension(with,heignt)方法
视图大小的将在这里最终确定,也就是说measure只是对onMeasure的一个包装,子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并通过setMeasuredDimension(width, height)保存计算结果。
在view中提供了layout()方法,主要是设置视图在屏幕中的显示位置,和measure()方法相似,定义的类型也是final,要求子类不去修改.
在layout中有两个关于位置的方法,第一个方法就是setFrame()
layout()中的方法一:
protected boolean setFrame(int left, int top, int right, int bottom)
layout()中的方法二
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {};
第一个方法是用来保存子视图在父视图中的位置,保存的其实是两个点,分别是左上点和右下点.onLayout()方法主要是给派生类viewGroup来实用的,用于指定子空间在viewgroup中的位置.
前面的measure()是计算视图宽和高,最后保存在onMeasure()中的setMeasureDimension(with,height)和位置,是对于绘制的参数的初始化,只有通过绘制,才能展示出来.
draw操作利用前两部得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。子类也不应该修改该方法,因为其内部定义了绘图的基本操作:
(1)绘制背景;
(2)如果要视图显示渐变框,这里会做一些准备工作;
(3)绘制视图本身,即调用onDraw()函数。在view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的显示(比如TextView在这里实现了绘制文字的过程)。而对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的,其包含了多个子view,而子View已经实现了自己的绘制方法,因此只需要告诉子view绘制自己就可以了,也就是下面的dispatchDraw()方法;
(4)绘制子视图,即dispatchDraw()函数。在view中这是个空函数,具体的视图不需要实现该方法,它是专门为容器类准备的,也就是容器类必须实现该方法;
(5)如果需要(应用程序调用了setVerticalFadingEdge或者setHorizontalFadingEdge),开始绘制渐变框;
(6)绘制滚动条;
从上面可以看出自定义View需要最少覆写onMeasure()和onDraw()两个方法。
viewGroup内部可以用来存放多个View控件,并且根据自身的测量模式,来测量View子控件,并且决定View子控件的位置。子控件的测量根据父控件的测量数据来进行测量的,具体如下:
我们重写了onMeasure(),在方法里面,我们首先先获取ViewGroup中的子View的个数,然后遍历它所有的子View,得到每一个子View,调用measureChild()放来,来对子View进行测量。子View的测量是根据ViewGroup所提供的测量模式来进行来,所以在measureChild()方法中,把ViewGroup的widthMeasureSpec 和 heightMeasureSpec和子View一起传进去了,
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childCount = getChildCount();
for(int i = 0 ; i < childCount ; i ++){
View children = getChildAt(i);
measureChild(children,widthMeasureSpec,heightMeasureSpec);
}
}
测量孩子的方法的具体实现:
measureChild()源码方法里面很好理解,它首先得到子View的LayoutParams,然后根据ViewGroup传递进来的宽高属性值和自身的LayoutParams 的宽高属性值及自身padding属性值分别调用getChildMeasureSpec()方法获取到子View的测量。由该方法我们也知道ViewGroup中在测量子View的大小时,测量结果分别是由父节点的测量模式和子View本身的LayoutParams及padding所决定的。
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
etChildMeasureSpec()方法源码:
public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); int size = Math.max(0, specSize - padding); int resultSize = 0; int resultMode = 0; switch (specMode) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
自定义的viewgroup需要覆写的方法是
必须的方法是onMeasure()方法来测量自己和测量子控件的宽和高,第二方法是onLayout()确定子空间在ViewGroup()中的位置,至于ondraw(),viewgroup不需要覆写,自己是一个容器,是不需要绘制,只需要告诉子空间绘制自己就行,dispatchDraw()方法就是告诉自己的子控件去绘制自己的方法.
http://www.cnblogs.com/0616–ataozhijia/p/4003380.html
首先自定义控件的实现是继承一个控件,重写里面的三个方法,算是自定义控件的创建就完成了!
实际中有四个方法,
在资源文件目录下d的values中新建一个attrs.xml;
也就是;res -> values -> attrs.xml
declare声明的意思
1) 添加自定义属性到xml文件中
<?xml version="1.0" encoding="utf-8"?>
<resources>
//第一name是声明对哪一个自定义控件的声明的自定义属性
<declare-styleable name="RatioLayout" >
// name自定义属性的名称 format:是在定义属性的类型,是double还是float还是true等类型
<attr name="ratio" format="float"/>
</declare-styleable>
</resources>
2) 在xml的中,指定属性的值
3) 在view中获取xml中的值
命名空间:xmlns:**itheima**="http://schemas.android.com/apk/res-auto"
itheima;可以自己随意定义,不要和标准相重合就行.
<cast.itheima.com.mygoogleapp94.view.RatioLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
itheima:ratio="2.45">
<ImageView
android:id="@+id/subjectpagerdetail_pullto_icon"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/ic_launcher" />
</cast.itheima.com.mygoogleapp94.view.RatioLayout>
第一种: 使用自定义控件中的属性参数 (AttributeSet attrs)
参数说明:第一个参数是:命名控件,在使用布局中使用自定义属性的时定义过
attrs.getAttributeFloatValue(参数三个);
float scale = attrs.getAttributeFloatValue(NAMESPACE,"ratio",-1);
参数说明:第一个参数是:命名空间 private static final String NAMESPACE= “http://schemas.android.com/apk/res-auto/“;
参数二:是自定义属性的名称:”ratio”
参数三:自定义属性类型的默认值:
获取自定义属性在布局中的复制方法二:
使用context方法来获取:
//第二种方法就是使用context的方法
//当View被创建的时候,可以通过AttributeSet读取所有的定义在xml中的属性,在构造函数中通过obtainStyledAttributes读取attrs,
// 该方法会返回一个TypeArray数组。通过TypeArray可以读取到已经定义在XML中的方法。
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RatioLayout);
参数说明:参数一:类型 AttributeSet 表示的是自定义属性的集合,也就是在自定义属性中定义的<attr name ="">中的集合,
参数二:是对于哪一个自定义控件,我们自定义属性首先会声明是对哪一个自定义的控件进行申明的自定义属性.
结果:是包含TypedArray类中Container for an array of values that were retrieved with
//获取ration
ratio = typedArray.getFloat(R.styleable.RatioLayout_ratio, -1);
//回收
typedArray.recycle();
4) 将获取的值应用到view中
第一是:命名控件的生成
xmlns:itheima=”http://schemas.android.com/apk/res-auto”
首先是: xmls 后面的只是一个标识,随意些,不要和系统命名重名就行,相当于xmlns:android=”http://schemas.android.com/apk/res/android”中的android
在eclipse中
res-auto是自定义控件的全类名,在androidStudio中:xmlns:itheima=”http://schemas.android.com/apk/res-auto”
ctrl+ h 表示的是层次结构
第一种就是 使用 attrs(AttributeSet)
第二种就是 使用 TypedArray来获取
获取自定义属性的方法(区别讲的很透彻)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。