Android的UI界面都是由View和ViewGroup及其派生类组合而成的。
其中,View是所有UI组件的基类,而 ViewGroup是容纳这些组件的容器,ViewGroup是View的子类,.所以它也具有View的特性,但它主要用来充当View的容器,将其中的View视作自己的孩子,对它的子View进行管理,当然它的孩子也可以是ViewGroup类型
View对象是Android平台中用户界面体现的基础单位。像我们常用的TextView、Button、ImageView之类的UI控件都是属于View,这些被称为“widgets(工具)”。
ViewGroup类同样为其被称为“Layouts(布局)”的子类奠定了基础,它们提供了像LinearLayou、RelativeLayout之类的布局架构。
最重要的是要理解安卓View的绘制过程
有关view的绘制流程,大家可以去网上查找资料。
本文中自定义ViewGroup效果如下
有如下优点:
1.在不同的Activity中可以复用该视图,更易维护
2.开发者可以使用自定义属性来定制ViewGroup中子视图的位置
3.布局文件更简明,更容易理解
4.如果需要修改margin,不必重新手动计算每个子视图的margin
步骤
1.创建attrs属性声明文件
<resources>
<declare-styleable name="CustomLayout">
<attr name="horizontal_spacing" format="dimension"/> <!--表示水平间距 -->
<attr name="vertical_spacing" format="dimension"/> <!--表示垂直间距 -->
</declare-styleable>
</resources>
2.指定默认间距
这是为了在未指定上述两个属性时,使用默认的大小。在dimens.xml文件中添加即可。
<resources>
<dimen name="custom_horizontal_spacing">10dp</dimen>
<dimen name="custom_vertical_spacing">10dp</dimen>
</resources>
3.创建自定义LayoutParams
package cn.king.demo.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;
/**
* 自定义的LayoutParams,用来保存每个子视图的x轴和y轴的位置
*/
public class LayoutParams extends ViewGroup.LayoutParams {
int x;
int y;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
4.创建自定义ViewGroup
关键是onMeasure和onLayout
package cn.king.demo.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import cn.king.demo.R;
/**
* 自定义ViewGroup
*/
public class CustomLayout extends ViewGroup {
private int mHorizontalSpacing;
private int mVerticalSpacing;
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomLayout);
try {
//读取水平间隔属性,如未指定则使用默认值
mHorizontalSpacing = a.getDimensionPixelSize(R.styleable.CustomLayout_horizontal_spacing,
getResources().getDimensionPixelSize(R.dimen.custom_horizontal_spacing));
//读取垂直间隔属性,如未指定则使用默认值
mVerticalSpacing = a.getDimensionPixelSize(R.styleable.CustomLayout_vertical_spacing,
getResources().getDimensionPixelSize(R.dimen.custom_vertical_spacing));
} catch (Exception e) {
e.printStackTrace();
} finally {
a.recycle();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//使用宽高计算布局的最终大小以及子视图的x与y轴位置
int width = 0;
int height = getPaddingTop();
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
//令每个子视图测量自身
measureChild(child, widthMeasureSpec, heightMeasureSpec);
cn.king.demo.view.LayoutParams lp = (cn.king.demo.view.LayoutParams) child.getLayoutParams();
width = getPaddingLeft() + mHorizontalSpacing * i;
lp.x = width;
lp.y = height; //令layoutParams保存每个视图的x和y坐标
width += child.getMeasuredWidth();
height += mVerticalSpacing;
}
width += getPaddingRight();
height += getChildAt(getChildCount() - 1).getMeasuredHeight() + getPaddingBottom();
//使用计算所得的宽高设置整个布局的测量尺寸
setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
//设置子view的位置
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
cn.king.demo.view.LayoutParams lp = (cn.king.demo.view.LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight());
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new cn.king.demo.view.LayoutParams(getContext(), attrs);//使用了自定义的layoutParams
}
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new cn.king.demo.view.LayoutParams(p);
}
}
5.创建布局文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<cn.king.demo.view.CustomLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:horizontal_spacing="30dp"
app:vertical_spacing="40dp">
<View
android:layout_width="200dp"
android:layout_height="300dp"
android:background="#0eaa0f" />
<View
android:layout_width="200dp"
android:layout_height="300dp"
android:background="#0068aa" />
<View
android:layout_width="200dp"
android:layout_height="300dp"
android:background="#aa332a" />
</cn.king.demo.view.CustomLayout>
</FrameLayout>
好啦,大功告成!本文主要是让初学者体验下自定义视图的一些方法,让大家对自定义视图有所了解