您的位置:首页 > 其它

Andoird 自定义ViewGroup

2016-06-24 17:19 253 查看

1.自定义ViewGroup

主要关注两点测量和布局:

onMeasure

测量和保存各个视图和子视图的位置信息

onLayout

遍历所有子视图绘制

2.构造函数

1.res/values/attrs.xml

自定义视图的属性

<?xml version="1.0" encoding="utf-8" ?>
<resources>
<declare-styleable name="CascadeLayout">

<attr name="horizontal_spacing" format="dimension" />
<attr name="vertical_spacing" format="dimension" />
</declare-styleable>

</resources>


2.res/values/dimens.xml

自定义视图属性的默认值

<resources>
<dimen name="horizontal_spacing">16dp</dimen>
<dimen name="vertical_spacing">16dp</dimen>
</resources>


java 代码中使用。

private int mHorizontalSpacing;
private int mVerticalSpacing;

public CascadeLayout(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
TypedArray a = context.obtainStyledAttributes(attributeSet, R.styleable.CascadeLayout);
try {
mHorizontalSpacing = a.getDimensionPixelSize(R.styleable.CascadeLayout_horizontal_spacing, getResources().getDimensionPixelSize(R.dimen.horizontal_spacing));
mVerticalSpacing = a.getDimensionPixelSize(R.styleable.CascadeLayout_vertical_spacing, getResources().getDimensionPixelSize(R.dimen.vertical_spacing));
} finally {
a.recycle();
}
}


3. 布局 写法

自定义属性使用

命名空间

a)eclipse写法,android studio也适用

类似系统自带属性命名空间 xmlns:android=”http://schemas.android.com/apk/res/android”

将res/android 换成你自定义view 所属的包名

xmlns:cascade=”http://schemas.android.com/apk/com.lq.viewpractice.viewgroup.CascadeLayout”

b)android studio写法,eclipse 不能用

使用res-auto

xmlns:cascade=”http://schemas.android.com/apk/res-auto”

引用lib 和自定义的view 都智能用res-auto

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:cascade="http://schemas.android.com/apk/com.lq.viewpractice.viewgroup.CascadeLayout"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.lq.viewpractice.ViewGroupTestActivity">

<com.lq.viewpractice.viewgroup.CascadeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
cascade:horizontal_spacing="30dp"
cascade:vertical_spacing="20dp">

<View
android:layout_width="100dp"
android:layout_height="150dp"
android:background="#FF0000" />

<View
android:layout_width="100dp"
android:layout_height="150dp"
android:background="#00FF00" />

<View
android:layout_width="100dp"
android:layout_height="150dp"
android:background="#0000FF" />
</com.lq.viewpractice.viewgroup.CascadeLayout>
</RelativeLayout>


4. 自定义LayoutParams

这个类的用途就是保存每个子视图的x,y轴位置。这里把它定义为静态内部类。

public static class LayoutParams extends ViewGroup.LayoutParams {
int x;
int y;
public int verticalSpacing;

public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
}

public LayoutParams(int w, int h) {
super(w, h);
}

}


除此之外,还需要重写一些方法,checkLayoutParams()、generateDefaultLayoutParams()等,这个方法在不同ViewGroup之间往往是相同的。

@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}

@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
}

@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}

@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p.width, p.height);
}


5. 核心代码 测量 onMeasure

测量子视图的坐标。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = getPaddingLeft();
int height = getPaddingTop();
int verticalSpacing;

final int count = getChildCount();
for (int i = 0; i < count; i++) {
verticalSpacing = mVerticalSpacing;

View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);

LayoutParams lp = (LayoutParams) child.getLayoutParams();
width = getPaddingLeft() + mHorizontalSpacing * i;
//保存每个子视图的x,y轴坐标
lp.x = width;
lp.y = height;

if (lp.verticalSpacing >= 0) {
verticalSpacing += lp.verticalSpacing;
}

width += width;
height += verticalSpacing;

}
width += getPaddingRight();
height += getChildAt(getChildCount() - 1).getMeasuredHeight() + getPaddingBottom();
setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));
}


6.核心代码 布局 onLayout

循环调用子view 布局。

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight());
}
}


7. 参考链接

http://www.w2bc.com/Article/59242

http://blog.csdn.net/manoel/article/details/39062737

8.demo地址

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: