自定义布局控件,实现lineaerlayout自动换行
一、原理分析 在Android中除了控件之外还有布局控件或者容器控件,比如LinearLayout、FrameLayout等,它们通常不会实现具体的功能,主要负责布局和管理所包含的子控件。Android系统提供的布局控件都有各自的使用规则,需要根据实际的需要选择合适的布局控件。 如果系统提供的布局控件无法满足实际的布局要求,可以通过扩展ViewGro
在Android中除了控件之外还有布局控件或者容器控件,比如LinearLayout、FrameLayout等,它们通常不会实现具体的功能,主要负责布局和管理所包含的子控件。Android系统提供的布局控件都有各自的使用规则,需要根据实际的需要选择合适的布局控件。
如果系统提供的布局控件无法满足实际的布局要求,可以通过扩展ViewGroup类来实现自己的布局控件。在扩展ViewGroup类时一般需要覆盖并实现如下三个重要的方法:
onMeasure():计算控件所需要的区域空间。
onLayout():布局子控件。
dispatchDraw():绘制布局控件,需要特别注意:对于自定义控件需要覆盖onDraw方法,而自定义布局控件需要覆盖dispatchDraw方法。
二、示例分析
下面通过一个Demo来说明,如何通过扩展ViewGroup类实现自定义布局控件。对于Android中的线性布局控件LinearLayout,只能横向或者纵向排列子控件,而且横向布局时不能自动换行。实际上,通过扩展ViewGroup类,通过一些简单的算法就能够实现支持自动换行的布局控件。
onMeasure、onLayout、dispatchDraw三个方法在Demo中的作用总结如下:
onMeasure:计算控件及其子控件所占区域
onLayout:控制子控件的换行
dispatchDraw:为布局控件添加边框
JAVA代码如下:
001 |
package com.devdiv.test.view_test_viewgroup; |
002 |
003 |
import android.content.Context; |
004 |
import android.content.res.TypedArray; |
005 |
import android.graphics.Canvas; |
006 |
import android.graphics.Color; |
007 |
import android.graphics.Paint; |
008 |
import android.graphics.Rect; |
009 |
import android.util.AttributeSet; |
010 |
import android.view.View; |
011 |
import android.view.ViewGroup; |
012 |
import android.view.View.MeasureSpec; |
013 |
014 |
public class FixedGridLayout extends ViewGroup { |
015 |
|
016 |
private int mCellWidth; |
017 |
private int mCellHeight; |
018 |
019 |
public FixedGridLayout(Context context) { |
020 |
super(context); |
021 |
// TODO Auto-generated constructor stub |
022 |
} |
023 |
024 |
public FixedGridLayout(Context context, AttributeSet attrs) { |
025 |
super(context, attrs); |
026 |
// TODO Auto-generated constructor stub |
027 |
|
028 |
//初始化单元格宽和高 |
029 |
mCellWidth = 60; |
030 |
mCellHeight = 60; |
031 |
|
032 |
} |
033 |
|
034 |
//设置单元格宽度 |
035 |
public void setCellWidth(int w) { |
036 |
mCellWidth = w; |
037 |
|
038 |
//Call this when something has changed which has invalidated the layout of this view. |
039 |
//This will schedule a layout pass of the view tree |
040 |
requestLayout(); |
041 |
} |
042 |
|
043 |
//设置单元格高度 |
044 |
public void setCellHeight(int h) { |
045 |
mCellHeight = h; |
046 |
requestLayout(); |
047 |
} |
048 |
|
049 |
|
050 |
@Override |
051 |
protected void dispatchDraw(Canvas canvas) { |
052 |
// TODO Auto-generated method stub |
053 |
|
054 |
//获取布局控件宽高 |
055 |
int width = getWidth(); |
056 |
int height = getHeight(); |
057 |
//创建画笔 |
058 |
Paint mPaint = new Paint(); |
059 |
//设置画笔的各个属性 |
060 |
mPaint.setColor(Color.BLUE); |
061 |
mPaint.setStyle(Paint.Style.STROKE); |
062 |
mPaint.setStrokeWidth(10); |
063 |
mPaint.setAntiAlias(true); |
064 |
|
065 |
//创建矩形框 |
066 |
Rect mRect = new Rect(0, 0, width, height); |
067 |
//绘制边框 |
068 |
canvas.drawRect(mRect, mPaint); |
069 |
|
070 |
//最后必须调用父类的方法 |
071 |
super.dispatchDraw(canvas); |
072 |
} |
073 |
074 |
075 |
@Override |
076 |
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
077 |
// TODO Auto-generated method stub |
078 |
|
079 |
//创建测量参数 |
080 |
int cellWidthSpec = MeasureSpec.makeMeasureSpec(mCellWidth, MeasureSpec.AT_MOST); |
081 |
int cellHeightSpec = MeasureSpec.makeMeasureSpec(mCellHeight, MeasureSpec.AT_MOST); |
082 |
|
083 |
//记录ViewGroup中Child的总个数 |
084 |
int count = getChildCount(); |
085 |
|
086 |
//设置子空间Child的宽高 |
087 |
for (int i = 0; i < count; i++) { |
088 |
View childView = getChildAt(i); |
089 |
/* |
090 |
* This is called to find out how big a view should be. |
091 |
* The parent supplies constraint information in the width and height parameters. |
092 |
* The actual mesurement work of a view is performed in onMeasure(int, int), |
093 |
* called by this method. |
094 |
* Therefore, only onMeasure(int, int) can and must be overriden by subclasses. |
095 |
*/ |
096 |
childView.measure(cellWidthSpec, cellHeightSpec); |
097 |
} |
098 |
|
099 |
//设置容器控件所占区域大小 |
100 |
//注意setMeasuredDimension和resolveSize的用法 |
101 |
setMeasuredDimension(resolveSize(mCellWidth * count, widthMeasureSpec), resolveSize(mCellHeight * count, heightMeasureSpec)); |
102 |
//setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); |
103 |
|
104 |
//不需要调用父类的方法 |
105 |
//super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
106 |
} |
107 |
108 |
109 |
@Override |
110 |
protected void onLayout(boolean changed, int l, int t, int r, int b) { |
111 |
// TODO Auto-generated method stub |
112 |
|
113 |
int cellWidth = mCellWidth; |
114 |
int cellHeight = mCellHeight; |
115 |
int columns = (r - l) / cellWidth; |
116 |
|
117 |
if(columns < 0) { |
118 |
columns = 1; |
119 |
} |
120 |
|
121 |
int x = 0; |
122 |
int y = 0; |
123 |
int i = 0; |
124 |
int count = getChildCount(); |
125 |
|
126 |
for (int j = 0; j < count; j++) { |
127 |
final View childView = getChildAt(j); |
128 |
//获取子控件Child的宽高 |
129 |
int w = childView.getMeasuredWidth(); |
130 |
int h = childView.getMeasuredHeight(); |
131 |
//计算子控件的顶点坐标 |
132 |
int left = x + ((cellWidth - w)/2); |
133 |
int top = y + ((cellHeight - h)/2); |
134 |
//int left = x; |
135 |
//int top = y; |
136 |
//布局子控件 |
137 |
childView.layout(left, top, left + w, top + h); |
138 |
|
139 |
if(i >= (columns - 1)) { |
140 |
i = 0; |
141 |
x = 0; |
142 |
y += cellHeight; |
143 |
} else { |
144 |
i++; |
145 |
x += cellWidth; |
146 |
|
147 |
} |
148 |
|
149 |
} |
150 |
151 |
} |
152 |
153 |
} |
完成了FixedGridLayout类之后,就可以在xml布局文件中使用它了,对于自定义布局控件也是一样,需要使用完整的类名。FixedGridLayout控件能够自动排列子控件,所以不需要设置太多的属性,只要按顺序添加子控件即可,下面是完整的xml代码:
01 |
<?xml version="1.0" encoding="utf-8"?> |
02 |
<com.devdiv.test.view_test_viewgroup.FixedGridLayout |
03 |
xmlns:android="http://schemas.android.com/apk/res/android" |
04 |
android:id="@+id/mlayout" |
05 |
android:layout_width="fill_parent" |
06 |
android:layout_height="fill_parent" |
07 |
> |
08 |
|
09 |
<ImageView |
10 |
android:layout_width="wrap_content" |
11 |
android:layout_height="wrap_content" |
12 |
android:src="@drawable/bugdroid" /> |
13 |
|
14 |
<ImageView |
15 |
android:layout_width="wrap_content" |
16 |
android:layout_height="wrap_content" |
17 |
android:src="@drawable/bugdroid" /> |
18 |
|
19 |
<ImageView |
20 |
android:layout_width="wrap_content" |
21 |
android:layout_height="wrap_content" |
22 |
android:src="@drawable/bugdroid" /> |
23 |
|
24 |
<ImageView |
25 |
android:layout_width="wrap_content" |
26 |
android:layout_height="wrap_content" |
27 |
android:src="@drawable/bugdroid" /> |
28 |
|
29 |
<ImageView |
30 |
android:layout_width="wrap_content" |
31 |
android:layout_height="wrap_content" |
32 |
android:src="@drawable/bugdroid" /> |
33 |
|
34 |
<ImageView |
35 |
android:layout_width="wrap_content" |
36 |
android:layout_height="wrap_content" |
37 |
android:src="@drawable/bugdroid" /> |
38 |
39 |
40 |
</com.devdiv.test.view_test_viewgroup.FixedGridLayout> |
其中,构造函数中完成了mCellWidth和mCellHeight的初始化,代码如下:
1 |
public FixedGridLayout(Context context, AttributeSet attrs) { |
2 |
super(context, attrs); |
3 |
// TODO Auto-generated constructor stub |
4 |
|
5 |
//初始化单元格宽和高 |
6 |
mCellWidth = 60; |
7 |
mCellHeight = 60; |
8 |
|
9 |
} |
也可以对mCellWidth和mCellHeight进行不同的设置,代码如下:
1 |
FixedGridLayout mFixedGridLayout = (FixedGridLayout) findViewById(R.id.mlayout); |
2 |
mFixedGridLayout.setCellWidth(120); |
3 |
mFixedGridLayout.setCellHeight(120); |
三、运行效果
图5-4 Demo运行效果图(mCellWidth=mCellHeight=60)
图5-5 Demo运行效果图(mCellWidth=mCellHeight=120)
更多推荐



所有评论(0)