Android 自定义View流程
2015-09-04 22:26
621 查看
在Android中自定义控件我们需要关心的只有3个回调方法:
onMeasure(); 该方法发负责对该类及其子View进行测量宽高
onLayout(); 该方法负责对该类及其子View的位置进行布置
onDraw(); 该方法负责回执该View及其子View
通过一个例子来说明如何自定义View,效果图如下:
这是一个自定义的布局,其子View会自动填充一行一行的想下排列。
首先我们定义一个类集成ViewGroup,在该类的onMeasure方法中进行测量,同时测量
出每个子View 的宽高,根据计算决定每个子View应该在哪一行,然后在onLayout中对
每一个子View的位置进行摆放。
首先我解释一下MeasureSpec, 一个MeasureSpec封装了父布局传递给子布局的布局
要求,每个MeasureSpec代表了一组宽度和高度的要求。一个MeasureSpec由大小和
模式组成。
它有三种模式:
UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;
EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
AT_MOST(至多),子元素至多达到指定大小的值。
它常用的三个函数:
static int getMode(int measureSpec):根据提供的测量值(格式)提取模式(上述三个模式之一)
static int getSize(int measureSpec):根据提供的测量值(格式)提取大小值(这个大小也就是我们通常所说的大小)
static int makeMeasureSpec(int size,int mode):根据提供的大小值和模式创建一个测量值(格式)
这个类的使用呢,通常在view组件的onMeasure方法里面调用.
下面提供我的自定义布局文件代码:
下面是MainActivity代码:
源码地址:http://download.csdn.net/detail/u012943767/9078799/
onMeasure(); 该方法发负责对该类及其子View进行测量宽高
onLayout(); 该方法负责对该类及其子View的位置进行布置
onDraw(); 该方法负责回执该View及其子View
通过一个例子来说明如何自定义View,效果图如下:
这是一个自定义的布局,其子View会自动填充一行一行的想下排列。
首先我们定义一个类集成ViewGroup,在该类的onMeasure方法中进行测量,同时测量
出每个子View 的宽高,根据计算决定每个子View应该在哪一行,然后在onLayout中对
每一个子View的位置进行摆放。
首先我解释一下MeasureSpec, 一个MeasureSpec封装了父布局传递给子布局的布局
要求,每个MeasureSpec代表了一组宽度和高度的要求。一个MeasureSpec由大小和
模式组成。
它有三种模式:
UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;
EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
AT_MOST(至多),子元素至多达到指定大小的值。
它常用的三个函数:
static int getMode(int measureSpec):根据提供的测量值(格式)提取模式(上述三个模式之一)
static int getSize(int measureSpec):根据提供的测量值(格式)提取大小值(这个大小也就是我们通常所说的大小)
static int makeMeasureSpec(int size,int mode):根据提供的大小值和模式创建一个测量值(格式)
这个类的使用呢,通常在view组件的onMeasure方法里面调用.
下面提供我的自定义布局文件代码:
package com.floatlayout; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; /** * Anthor:Jam * * Time: 2015-9-4 下午7:42:23 * * Version: * * Description:自定义瀑布流布局 自定义瀑布流布局的思路是这样的: 1,子View宽高最好只用wrap_content * 2,测量好每个子View的宽高 3,根据每个子View的宽,然后把子View放在一个行对象里面。 4,在onLayout方法内把每个子View放置位置 * */ public class FlowLayout extends ViewGroup { private int useWidth = 0; // 当前一行使用的长度 private int horizontalSpace = 0; // 水平两个子View之间的空位宽度 private int verticalSpace = 0; // 垂直两个子View之间的空位宽度 private Line line; // 表示当前行 public FlowLayout(Context context) { super(context); float density = getResources().getDisplayMetrics().density; horizontalSpace = verticalSpace = (int) (13 / density + 0.5f); } public FlowLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public FlowLayout(Context context, AttributeSet attrs) { super(context, attrs); } /** * 测量每个子View的宽高,并且把子view 分配在每一行 MessureSpec 相当于测量的规则,有3个属性 * android.view.View.MeasureSpec.UNSPECIFIED 不指定 * android.view.View.MeasureSpec.EXACTLY 精确值 * android.view.View.MeasureSpec.AT_MOST 最大是多少 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //当父控件执行onMeasure的时候会调用,所以要清除数据 lines.clear(); line=null; useWidth=0; // 获取父View的宽MODE int parentWidthMode = MeasureSpec.getMode(widthMeasureSpec); // 获取父View的宽 parentWidthSize = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); // 获取父View的高度MODE int parentHeightMode = MeasureSpec.getMode(heightMeasureSpec); // 获取父View的高度 int parentHeightSize = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); // 根据父View的宽高测量规则制定每个子View的测量规则 int childWidthMode = parentWidthMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : parentWidthMode; int childHeightMode = parentHeightMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : parentHeightMode; // 获取子View的测量规则 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( parentWidthSize, childWidthMode); int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( parentHeightSize, childHeightMode); // 遍历子View,测量子View的宽高 line = new Line(); for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); useWidth += child.getMeasuredWidth(); // 如果当前使用宽度小于父View的宽度,表示当前一行能放下这个子View,把它添加进当前行 if (useWidth <= parentWidthSize) { line.addChild(child); // 当前行加空格 useWidth += horizontalSpace; // 否则新添加一行 } else { if (line.getCount() < 1) { line.addChild(child); } // 把当前行添加到list中 lines.add(line); // 创建行的一行 line = new Line(); // 重置 useWidth = 0; } } // 如果是最后一行 if (!lines.contains(line)) { lines.add(line); } int totalHeight = 0; // 父view当前总的高度 for (Line line : lines) { // 把每一行高度添加 totalHeight += line.getHeight(); } // 添加每一行之间的空间,获得父View的总高度 totalHeight += verticalSpace * (lines.size() - 1) + getPaddingBottom() + getPaddingTop(); System.out.println(totalHeight + "..." + lines.size()); // 设置父View的大小 setMeasuredDimension(parentWidthSize + getPaddingLeft() + getPaddingRight(), resolveSize(totalHeight, heightMeasureSpec)); } // 维护每一行的集合 private List<Line> lines = new ArrayList<Line>(); private int parentWidthSize; // 每一行 class Line { // 每一行中的子View List<View> childrens = new ArrayList<View>(); int height = 0; // 行高 int lineWidth = 0; // 行宽 public void addChild(View child) { childrens.add(child); if (child.getMeasuredHeight() > height) { height = child.getMeasuredHeight(); } lineWidth += child.getMeasuredWidth(); } // 获取每一行的高度 public int getHeight() { return this.height; } public int getCount() { return childrens.size(); } // 给当前行每一个子View设置位置 public void layout(int l, int t) { lineWidth += verticalSpace * (childrens.size() - 1); int surplusWidth = parentWidthSize - lineWidth; // 每一行剩余的宽度 int childWidth = 0; // 每一个view应当扩充的宽度 if (surplusWidth > 0) { childWidth = surplusWidth / childrens.size(); } for (int i = 0; i < childrens.size(); i++) { View child = childrens.get(i); child.layout(l, t, l + child.getMeasuredWidth() + childWidth, t + child.getMeasuredHeight()); l += child.getMeasuredWidth() + childWidth; l += horizontalSpace; } } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { l += getPaddingLeft(); t += getPaddingTop(); for (Line line : lines) { line.layout(l, t); t += line.getHeight() + verticalSpace; } } }
下面是MainActivity代码:
package com.floatlayout; import java.util.Random; import android.os.Bundle; import android.app.Activity; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.StateListDrawable; import android.view.Gravity; import android.view.Menu; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; public class MainActivity extends Activity { String[] data = new String[] { "蹦跶", "蹦跶", "溜达", "溜达", "出去走走", "切克闹", "神神侃于此", "侃侃更健康", "娱乐交友群", "美女帅哥", "男女屌丝", "都欢迎", "蹦跶", "蹦跶", "溜达", "溜达", "出去走走", "切克闹", "神神侃于此", "侃侃更健康", "娱乐交友群", "美女帅哥", "男女屌丝", "都欢迎", "蹦跶", "蹦跶", "溜达", "溜达", "出去走走", "切克闹", "神神侃于此", "侃侃更健康", "娱乐交友群", "美女帅哥", "男女屌丝", "都欢迎", "蹦跶", "蹦跶", "溜达", "溜达", "出去走走", "切克闹", "神神侃于此", "侃侃更健康", "娱乐交友群", "美女帅哥", "男女屌丝", "都欢迎", "蹦跶", "蹦跶", "溜达", "溜达", "出去走走", "切克闹", "神神侃于此", "侃侃更健康", "娱乐交友群", "美女帅哥", "男女屌丝", "都欢迎", "蹦跶", "蹦跶", "溜达", "溜达", "出去走走", "切克闹", "神神侃于此", "侃侃更健康", "娱乐交友群", "美女帅哥", "男女屌丝", "都欢迎", "蹦跶", "蹦跶", "溜达", "溜达", "出去走走", "切克闹", "神神侃于此", "侃侃更健康", "娱乐交友群", "美女帅哥", "男女屌丝", "都欢迎", "蹦跶", "蹦跶", "溜达", "溜达", "出去走走", "切克闹", "神神侃于此", "侃侃更健康", "娱乐交友群", "美女帅哥", "男女屌丝", "都欢迎", "蹦跶", "蹦跶", "溜达", "溜达", "出去走走", "切克闹", "神神侃于此", "侃侃更健康", "娱乐交友群", "美女帅哥", "男女屌丝", "都欢迎", "蹦跶", "蹦跶", "溜达", "溜达", "出去走走", "切克闹", "神神侃于此", "侃侃更健康", "娱乐交友群", "美女帅哥", "男女屌丝", "都欢迎", "蹦跶", "蹦跶", "溜达", "溜达", "出去走走", "切克闹", "神神侃于此", "侃侃更健康", "娱乐交友群", "美女帅哥", "男女屌丝", "都欢迎" }; private FlowLayout layout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ScrollView scrollView = new ScrollView(this); layout = new FlowLayout(this); for (int i = 0; i < data.length; i++) { TextView tv = new TextView(this); tv.setLayoutParams(new ViewGroup.LayoutParams(-2, -2)); // -2代表wrap_content tv.setTextColor(Color.WHITE); tv.setTextSize(dip2px(14)); Random random = new Random(); // 创建随机 int red = random.nextInt(200) + 22; int green = random.nextInt(200) + 22; int blue = random.nextInt(200) + 22; int color = Color.rgb(red, green, blue);// 范围 0-255 GradientDrawable createShape = createShape(color); // 默认显示的图片 Drawable pressedDrawable = createShape(0xffcecece); StateListDrawable createSelectorDrawable = createSelectorDrawable( pressedDrawable, createShape);// 创建状态选择器 tv.setBackground(createSelectorDrawable); tv.setPadding((int) dip2px(7), (int) dip2px(4), (int) dip2px(7), (int) dip2px(4)); tv.setGravity(Gravity.CENTER); tv.setText(data[i]); layout.addView(tv); tv.invalidate(); } scrollView.addView(layout); setContentView(scrollView); } /** * 创建圆角矩形 * * @param color * @return */ public GradientDrawable createShape(int color) { GradientDrawable drawable = new GradientDrawable(); drawable.setCornerRadius(dip2px(5));// 设置4个角的弧度 drawable.setColor(color);// 设置颜色 return drawable; } /** * dp转换成px * * @param i * @return */ private float dip2px(int i) { return getResources().getDisplayMetrics().density * i; } /** * 创建选择器 * * @param pressedDrawable * 按下时的颜色 * @param normalDrawable * 正常颜色 * @return */ public StateListDrawable createSelectorDrawable(Drawable pressedDrawable, Drawable normalDrawable) { StateListDrawable stateListDrawable = new StateListDrawable(); stateListDrawable.addState(new int[] { android.R.attr.state_pressed }, pressedDrawable);// 按下显示的图片 stateListDrawable.addState(new int[] {}, normalDrawable);// 抬起显示的图片 return stateListDrawable; } }
源码地址:http://download.csdn.net/detail/u012943767/9078799/
相关文章推荐
- Android内存管理机制详解
- Android学习——android资源访问
- Android学习——Android安装
- Android Studio格式化代码设置和代码风格设置、代码提示键
- HAL层开发框架介绍
- Android学习——Android入门
- android PendingIntent参数详细解析
- android入门——用RadioGroup实现Fragment的切换
- Android ListView EditText批量录入功能
- android aapt
- android Bitmap内存优化(三) 测量 ImageView 大小
- android 自定义属性
- Android中的进程机制(Binder)
- 如何使用Android命令
- Android Api Demos登顶之路(五十六)Service Remote Service
- @+id/android:list"的写法
- Android实战简易教程-第五十三枪(通过实现OnScrollListener接口实现上拉加载更多功能)
- android中的一些小技巧
- 【Android进阶】-Android ORM数据库框架 ActiveAndroid入门
- android的消息处理机制(图+源码分析)——Looper,Handler,Message