自定义布局控件,实现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)