在Android中除了控件之外还有布局控件或者容器控件,比如LinearLayout、FrameLayout等,它们通常不会实现具体的功能,主要负责布局和管理所包含的子控件。Android系统提供的布局控件都有各自的使用规则,需要根据实际的需要选择合适的布局控件。
如果系统提供的布局控件无法满足实际的布局要求,可以通过扩展ViewGroup类来实现自己的布局控件。在扩展ViewGroup类时一般需要覆盖并实现如下三个重要的方法:
onMeasure():计算控件所需要的区域空间。
onLayout():布局子控件。
如果系统提供的布局控件无法满足实际的布局要求,可以通过扩展ViewGroup类来实现自己的布局控件。在扩展ViewGroup类时一般需要覆盖并实现如下三个重要的方法:
onMeasure():计算控件所需要的区域空间。
onLayout():布局子控件。
dispatchDraw():绘制布局控件,需要特别注意:对于自定义控件需要覆盖onDraw方法,而自定义布局控件需要覆盖dispatchDraw方法。
我的需求
开发过程中需要往布局文件中动态添加TextView,LinearLayout默认的布局方式不存在自动换行,所以导致布局效果非常差,为了实现动态添加的TextView能够自动换行显示,这里继承ViewGroup类,重新定义开发了一个布局文件。
直接上代码:
package com.west.aipuli.wheretobuy.unit;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class FixedGridLayout extends ViewGroup {
private int mCellWidth;
private int mCellHeight;
public FixedGridLayout(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public FixedGridLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
// 初始化单元格宽和高
mCellWidth = 60;
mCellHeight = 60;
}
// 设置单元格宽度
public void setCellWidth(int w) {
mCellWidth = w;
requestLayout();
}
// 设置单元格高度
public void setCellHeight(int h) {
mCellHeight = h;
requestLayout();
}
//为布局控件添加边框
@Override
protected void dispatchDraw(Canvas canvas) {
// TODO Auto-generated method stub
// 获取布局控件宽高
int width = getWidth();
int height = getHeight();
// 创建画笔
Paint mPaint = new Paint();
// 设置画笔的各个属性
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(1);
mPaint.setAntiAlias(true);
// 创建矩形框
Rect mRect = new Rect(0, 0, width, height);
// 绘制边框
canvas.drawRect(mRect, mPaint);
// 最后必须调用父类的方法
super.dispatchDraw(canvas);
}
//计算控件所需要的区域空间
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
// 创建测量参数
int cellWidthSpec = MeasureSpec.makeMeasureSpec(mCellWidth,
MeasureSpec.AT_MOST);
int cellHeightSpec = MeasureSpec.makeMeasureSpec(mCellHeight,
MeasureSpec.AT_MOST);
// 记录ViewGroup中Child的总个数
int count = getChildCount();
// 设置子空间Child的宽高
for (int i = 0; i < count; i++) {
View childView = getChildAt(i);
childView.measure(cellWidthSpec, cellHeightSpec);
}
// 设置容器控件所占区域大小
// 注意setMeasuredDimension和resolveSize的用法
setMeasuredDimension(resolveSize(mCellWidth * count, widthMeasureSpec),
resolveSize(mCellHeight * count, heightMeasureSpec));
}
//控制子控件换行
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
int cellWidth = mCellWidth;
int cellHeight = mCellHeight;
int columns = (r - l) / cellWidth;
if (columns < 0) {
columns = 1;
}
int x = 0;
int y = 0;
int i = 0;
int count = getChildCount();
for (int j = 0; j < count; j++) {
final View childView = getChildAt(j);
//获取子控件Child的宽高
int w = childView.getMeasuredWidth();
int h = childView.getMeasuredHeight();
// 计算子控件的顶点坐标
int left = x + ((cellWidth - w) / 2);
int top = y + ((cellHeight - h) / 2);
// 布局子控件
childView.layout(left, top, left + w, top + h);
if (i >= (columns - 1)) {
i = 0;
x = 0;
y += cellHeight;
} else {
i++;
x += cellWidth;
}
}
}
}
在Xml文件中直接使用该控件即可。
<com.west.aipuli.wheretobuy.unit.FixedGridLayout
android:id="@+id/isbuy_list_itemsImage_layout02_layout01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dip"
android:layout_alignLeft="@+id/isbuy_list_itemsImage_itemsTitle"
android:layout_below="@+id/isbuy_list_itemsImage_itemsTitle" >
</com.west.aipuli.wheretobuy.unit.FixedGridLayout>