您的位置:首页 > 移动开发 > Android开发

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方法里面调用.

下面提供我的自定义布局文件代码:

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