Android学习之自定义view(三)
2015-09-12 22:36
423 查看
自定义view第三篇,废话少说,直接进入正题。
第一步:在attrs.xml文件中定义属性名和值,并在构造函数中获取该值
<resources>
<declare-styleablename="MyCustomView3">
<attrname="horizonal_spacing"format="dimension"/>
<attrname="vertical_spacing"format="dimension"/>
<attrname="layout_vertical_spacing"format="dimension"/>
</declare-styleable>
</resources>
public MyCustomView3(Context context, AttributeSet attrs) {
super(context, attrs);
//获取属性值
TypedArrayta = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView3);
for(inti=0;i<ta.getIndexCount();i++){
int attr = ta.getIndex(i);
switch(attr){
case R.styleable.MyCustomView3_horizonal_spacing
:
horizonal_spacing= ta.getDimensionPixelSize(attr, (int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,16, getResources().getDisplayMetrics()));
break;
case R.styleable.MyCustomView3_vertical_spacing
:
vertical_spacing =ta.getDimensionPixelSize(attr, (int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,16, getResources().getDisplayMetrics()));
break;
}
}
ta.recycle();
}
注意:在构造函数中,horizonal_spacing和vertical_spacing的默认值通过(int)TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,16, getResources().getDisplayMetrics())这个获取,默认为16dp;
也可以在dimension.xml文件中定义一个大小,然后通过getResources().getDimensionPixelSize(R.dimen.horizonal_spacing)获取。
第二步:重写onMeasure方法
在这里,当容器的宽和高被定义为wrap_content的时候:
宽度=(每张卡片的宽度+定义的水平间距) * 3;
高度=(每张卡片的高度+容器定义的垂直间距+视图定义的垂直间距)
* 3;
//记录当为warp_content的时候容器的宽和高
int width = 0;
int height = 0;
for(inti=0;i<getChildCount();i++){
ViewchildView = getChildAt(i);
mParams =(MyLayoutParams)childView.getLayoutParams();
width+=
mParams.width +horizonal_spacing;
//用布局定义的组件高度+定义的垂直间距+容器定义的垂直间距来决定子视图组件的高度
height+=
mParams.height +mParams.layout_vertical_spacing+vertical_spacing;
}
第三步:重写onLayout方法
对子视图进行布局;
@Override
protectedvoid onLayout(boolean
changed, int l,int t,int
r,int b) {
//分别代表卡片的左x坐标,上y坐标,右x坐标,下y坐标
int cl = 0, ct = 0, cr = 0,cb = 0;
for(inti=0;i<getChildCount();i++){
ViewchildView = getChildAt(i);
mParams =(MyLayoutParams)childView.getLayoutParams();
switch(i){
case 0 :
//第一张卡片的左坐标为0,右坐标为视图定义的宽度;上y坐标为子视图自身定义的间距,下y坐标=上y坐标+视图定义的宽度
cr=
mParams.width;
ct+=
mParams.layout_vertical_spacing;
cb=
mParams.height +mParams.layout_vertical_spacing+vertical_spacing;
break;
case 1 :
cl+=
horizonal_spacing;
cr+=
horizonal_spacing;
ct+=
mParams.layout_vertical_spacing+vertical_spacing;
cb+=
mParams.layout_vertical_spacing+vertical_spacing;
break;
case 2 :
cl+=
horizonal_spacing;
cr+=
horizonal_spacing;
ct+=
mParams.layout_vertical_spacing+vertical_spacing;
cb+=
mParams.layout_vertical_spacing+vertical_spacing;
break;
}
childView.layout(cl,ct, cr, cb);
}
}
第四步:比较关键。定义一个内部类,用于保存子视图的布局参数
/**
*
用于获取子视图的布局参数
* @authorwyb
*
*/
privateclass MyLayoutParamsextends
ViewGroup.LayoutParams{
//和布局中子视图定义的垂直间距想对应
publicintlayout_vertical_spacing;
public MyLayoutParams(Contextcontext, AttributeSet attrs) {
super(context, attrs);
//获取垂直间距
TypedArraya = context.obtainStyledAttributes(attrs,
R.styleable.MyCustomView3);
try {
layout_vertical_spacing= a
.getDimensionPixelSize(
R.styleable.MyCustomView3_layout_vertical_spacing,
-1);
} finally {
a.recycle();
}
}
public MyLayoutParams(int arg0,int
arg1) {
super(arg0, arg1);
//TODO Auto-generated constructor stub
}
publicMyLayoutParams(LayoutParams arg0) {
super(arg0);
//TODO Auto-generated constructor stub
}
}
在这个案例中MyLayoutParams类的layout_vertical_spacing就是保存子视图的垂直间距。虽然在容器的构造函数中可以获取这个属性值,用于容器的测量和子视图的布局,但这样显得容器类不是很高类聚,因为这个属性是属于子视图的,所以应该定义在这个内部类中
好,此时如果你就开始使用这个容器的话,在onMeasure方法的
mParams =(MyLayoutParams)childView.getLayoutParams();这一行就会报错
出现这个错误的原因是还没有重写下面这三个方法
@Override
public LayoutParams generateLayoutParams(AttributeSetattrs) {
//TODO Auto-generated method stub
returnnewMyLayoutParams(getContext(),attrs);
}
@Override
protected LayoutParamsgenerateLayoutParams(LayoutParams p) {
//TODO Auto-generated method stub
return new MyLayoutParams(p);
}
@Override
protected LayoutParamsgenerateDefaultLayoutParams() {
//TODO Auto-generated method stub
returnnewMyLayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
}
这三个方法的作用是为了告诉子视图有什么样的布局参数。比如现在MyLayoutParams是继承ViewGroup.LayoutParams,那么子视图想获取leftMargin这些属性的时候是无法获取,可以通过继承MarginLayoutParams来获取;其他属性依次类推,如想获取线性布局的orientation属性,就必须继承LinearLayout.LayoutParams这个类。
当然,也可以不用写这个内部类,然后在重写上面三个方法的时候直接返回某个类的对象即可;在这儿写这个内部类主要是让子视图拥有layout_vertical_spacing这个属性而已。
用法就很简单,直接在子视图中定义,如下:
<View
android:layout_width="70dp"
android:layout_height="100dp"
custom:layout_vertical_spacing="10dp"
android:background="#00FF00"/>
第五步:使用这个自定义的ViewGroup
<com.example.mycustomview3.MyCustomView3
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#000000"
custom:horizonal_spacing="20dp"
custom:vertical_spacing="30dp">
<View
android:layout_width="70dp"
android:layout_height="100dp"
android:background="#FF0000"/>
<View
android:layout_width="70dp"
android:layout_height="100dp"
custom:layout_vertical_spacing="10dp"
android:background="#00FF00"/>
<View
android:layout_width="70dp"
android:layout_height="100dp"
android:background="#0000FF"/>
</com.example.mycustomview3.MyCustomView3>
当为:wrap_content的时候:
当为300dp的时候:
到此,自定义ViewGroup就算完成了。当然,这只是很初步的定义,后面还会进一步学习。
第一步:在attrs.xml文件中定义属性名和值,并在构造函数中获取该值
<resources>
<declare-styleablename="MyCustomView3">
<attrname="horizonal_spacing"format="dimension"/>
<attrname="vertical_spacing"format="dimension"/>
<attrname="layout_vertical_spacing"format="dimension"/>
</declare-styleable>
</resources>
public MyCustomView3(Context context, AttributeSet attrs) {
super(context, attrs);
//获取属性值
TypedArrayta = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView3);
for(inti=0;i<ta.getIndexCount();i++){
int attr = ta.getIndex(i);
switch(attr){
case R.styleable.MyCustomView3_horizonal_spacing
:
horizonal_spacing= ta.getDimensionPixelSize(attr, (int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,16, getResources().getDisplayMetrics()));
break;
case R.styleable.MyCustomView3_vertical_spacing
:
vertical_spacing =ta.getDimensionPixelSize(attr, (int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,16, getResources().getDisplayMetrics()));
break;
}
}
ta.recycle();
}
注意:在构造函数中,horizonal_spacing和vertical_spacing的默认值通过(int)TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,16, getResources().getDisplayMetrics())这个获取,默认为16dp;
也可以在dimension.xml文件中定义一个大小,然后通过getResources().getDimensionPixelSize(R.dimen.horizonal_spacing)获取。
第二步:重写onMeasure方法
在这里,当容器的宽和高被定义为wrap_content的时候:
宽度=(每张卡片的宽度+定义的水平间距) * 3;
高度=(每张卡片的高度+容器定义的垂直间距+视图定义的垂直间距)
* 3;
//记录当为warp_content的时候容器的宽和高
int width = 0;
int height = 0;
for(inti=0;i<getChildCount();i++){
ViewchildView = getChildAt(i);
mParams =(MyLayoutParams)childView.getLayoutParams();
width+=
mParams.width +horizonal_spacing;
//用布局定义的组件高度+定义的垂直间距+容器定义的垂直间距来决定子视图组件的高度
height+=
mParams.height +mParams.layout_vertical_spacing+vertical_spacing;
}
第三步:重写onLayout方法
对子视图进行布局;
@Override
protectedvoid onLayout(boolean
changed, int l,int t,int
r,int b) {
//分别代表卡片的左x坐标,上y坐标,右x坐标,下y坐标
int cl = 0, ct = 0, cr = 0,cb = 0;
for(inti=0;i<getChildCount();i++){
ViewchildView = getChildAt(i);
mParams =(MyLayoutParams)childView.getLayoutParams();
switch(i){
case 0 :
//第一张卡片的左坐标为0,右坐标为视图定义的宽度;上y坐标为子视图自身定义的间距,下y坐标=上y坐标+视图定义的宽度
cr=
mParams.width;
ct+=
mParams.layout_vertical_spacing;
cb=
mParams.height +mParams.layout_vertical_spacing+vertical_spacing;
break;
case 1 :
cl+=
horizonal_spacing;
cr+=
horizonal_spacing;
ct+=
mParams.layout_vertical_spacing+vertical_spacing;
cb+=
mParams.layout_vertical_spacing+vertical_spacing;
break;
case 2 :
cl+=
horizonal_spacing;
cr+=
horizonal_spacing;
ct+=
mParams.layout_vertical_spacing+vertical_spacing;
cb+=
mParams.layout_vertical_spacing+vertical_spacing;
break;
}
childView.layout(cl,ct, cr, cb);
}
}
第四步:比较关键。定义一个内部类,用于保存子视图的布局参数
/**
*
用于获取子视图的布局参数
* @authorwyb
*
*/
privateclass MyLayoutParamsextends
ViewGroup.LayoutParams{
//和布局中子视图定义的垂直间距想对应
publicintlayout_vertical_spacing;
public MyLayoutParams(Contextcontext, AttributeSet attrs) {
super(context, attrs);
//获取垂直间距
TypedArraya = context.obtainStyledAttributes(attrs,
R.styleable.MyCustomView3);
try {
layout_vertical_spacing= a
.getDimensionPixelSize(
R.styleable.MyCustomView3_layout_vertical_spacing,
-1);
} finally {
a.recycle();
}
}
public MyLayoutParams(int arg0,int
arg1) {
super(arg0, arg1);
//TODO Auto-generated constructor stub
}
publicMyLayoutParams(LayoutParams arg0) {
super(arg0);
//TODO Auto-generated constructor stub
}
}
在这个案例中MyLayoutParams类的layout_vertical_spacing就是保存子视图的垂直间距。虽然在容器的构造函数中可以获取这个属性值,用于容器的测量和子视图的布局,但这样显得容器类不是很高类聚,因为这个属性是属于子视图的,所以应该定义在这个内部类中
好,此时如果你就开始使用这个容器的话,在onMeasure方法的
mParams =(MyLayoutParams)childView.getLayoutParams();这一行就会报错
出现这个错误的原因是还没有重写下面这三个方法
@Override
public LayoutParams generateLayoutParams(AttributeSetattrs) {
//TODO Auto-generated method stub
returnnewMyLayoutParams(getContext(),attrs);
}
@Override
protected LayoutParamsgenerateLayoutParams(LayoutParams p) {
//TODO Auto-generated method stub
return new MyLayoutParams(p);
}
@Override
protected LayoutParamsgenerateDefaultLayoutParams() {
//TODO Auto-generated method stub
returnnewMyLayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
}
这三个方法的作用是为了告诉子视图有什么样的布局参数。比如现在MyLayoutParams是继承ViewGroup.LayoutParams,那么子视图想获取leftMargin这些属性的时候是无法获取,可以通过继承MarginLayoutParams来获取;其他属性依次类推,如想获取线性布局的orientation属性,就必须继承LinearLayout.LayoutParams这个类。
当然,也可以不用写这个内部类,然后在重写上面三个方法的时候直接返回某个类的对象即可;在这儿写这个内部类主要是让子视图拥有layout_vertical_spacing这个属性而已。
用法就很简单,直接在子视图中定义,如下:
<View
android:layout_width="70dp"
android:layout_height="100dp"
custom:layout_vertical_spacing="10dp"
android:background="#00FF00"/>
第五步:使用这个自定义的ViewGroup
<com.example.mycustomview3.MyCustomView3
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#000000"
custom:horizonal_spacing="20dp"
custom:vertical_spacing="30dp">
<View
android:layout_width="70dp"
android:layout_height="100dp"
android:background="#FF0000"/>
<View
android:layout_width="70dp"
android:layout_height="100dp"
custom:layout_vertical_spacing="10dp"
android:background="#00FF00"/>
<View
android:layout_width="70dp"
android:layout_height="100dp"
android:background="#0000FF"/>
</com.example.mycustomview3.MyCustomView3>
当为:wrap_content的时候:
当为300dp的时候:
到此,自定义ViewGroup就算完成了。当然,这只是很初步的定义,后面还会进一步学习。
相关文章推荐
- Android 学习第15课,Android 开发的单元测试、及输出错误信息
- Android中获取屏幕相关信息(屏幕大小,状态栏、标题栏高度)
- Android 单线程下载与多线程下载
- Android:LayoutInflater介绍
- Android屏幕适配
- 2. Android系统结构
- Android0909<十四>(Service、Android线程)
- Android中AIDL使用例子
- Android 动画深入分析
- 如何在Android模拟器上安装apk文件
- android获取asset文件存到SD卡
- Glide图片加载器详解(PPT转录)
- android---AlertDialog对话框解析
- Android Studio中使用*.jar,*.aar和*.so文件
- Android开发环境的发展演变
- javaS的tring和androidS的tring区别是什么?
- Android开发之路(二)--浅析MVC开发模式
- 超轻量级的安卓SlidingMenu库
- Android JNI的动态注册
- Android 使用ShardSDK实现社会化分享