您的位置:首页 > 其它

自定义viewGroup 实现 流式布局

2017-05-18 17:31 477 查看
首先看这篇文章时前 希望你看下http://blog.csdn.net/lmj623565791/article/details/38339817 这篇博客,学习一下简单的自定义viewGroup,如果这篇文章你看懂了,我想你就能直接写这个需求了.

不多说,先虑一下思路:有很多个view,然后一个一个放置到viewGroup中,如果每一个view不能够放下了,就换行。

看下原理图:

红色的是textview的margin 黑色的是viewGroup的padding。



接下来看下整体效果图:



然后看代码:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//测量模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//测量ViewGroup的宽高
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//ViewGroup的padding
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBott
4000
om();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
//当前宽度
int curWidth = 0;
//当前高度
int curHeight = paddingTop + paddingBottom;
//最大宽度
int widthMax = 0;
for(int i = 0;i <  getChildCount();){
View childView = getChildAt(i);
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
if(curWidth == 0 ){
curWidth = paddingLeft + paddingRight;
curHeight += marginLayoutParams.topMargin + marginLayoutParams.bottomMargin + childHeight;
}
if(curWidth + marginLayoutParams.rightMargin + marginLayoutParams.leftMargin + childWidth <= widthSize){
//不换行
curWidth += marginLayoutParams.rightMargin + marginLayoutParams.leftMargin + childWidth;
i++;
}else{
//换行
widthMax = Math.max(widthMax, curWidth);
curWidth = 0;
}
//判断是否是最后一行
if(i == getChildCount()){
widthMax = Math.max(widthMax, curWidth);
curHeight += marginLayoutParams.topMargin + marginLayoutParams.bottomMargin + childHeight;
}
}
setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : widthMax ,
heightMode == MeasureSpec.EXACTLY  ? heightSize : curHeight);
}


关于上面的代码我一点点解释

//测量模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//测量ViewGroup的宽高
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);


也就是获取viewGroup的测量方式和viewGroup的宽高

//初始化curHeight,加上viewGroup的上下padding; 注意: curHeight也就是最终要显示的高度
curHeight += parentBottom + parentTop;


上述代码其实就是直接将初始化viewGroup的高度,因为始终要记住onMeasure只是计算宽度和高度!,然后每换一次行 curHeight就加一下textview的高度和margin.

if(curWidth == 0 ){
curWidth = paddingLeft + paddingRight;
curHeight += marginLayoutParams.topMargin + marginLayoutParams.bottomMargin + childHeight;
}


上述代码和curHeight一样的,就是换一次行,然后curWidth重置为0,然后在加上viewGroup的padding。

if(curWidth + marginLayoutParams.rightMargin + marginLayoutParams.leftMargin + childWidth <= widthSize){
//不换行
curWidth += marginLayoutParams.rightMargin + marginLayoutParams.leftMargin + childWidth;
i++;
}else{
//换行
widthMax = Math.max(widthMax, curWidth);
curWidth = 0;
}


上述代码基本可以说是onmeasure的核心代码,if判断也就是如果curWidth + TextView的宽度+ TextView的margin < viewgroup的宽度,这表明还能放下该textview,此时应该更新curwidth的值。然后判断下一个TextView(也就是i++)。如果else呢就表明大于viewGroup的宽度,此时应该将curwidth置为0,然后curheight需要加上TextView的margin和TextView的高度。(curHeight其实就是每一行的Height和viewGroup的padding).

//判断是否是最后一行
if(i == getChildCount()){
widthMax = Math.max(widthMax, curWidth);
curHeight += marginLayoutParams.topMargin + marginLayoutParams.bottomMargin + childHeight;
}


这个行是非常关键的,就是当最后一个TextView测量完之后,我们需要加上这一行的高度!!!对于为什么,你可以捋一下这个程序流程就知道了。如果不加的话,就不会显示最后一行数据。

好了,接下来看onLayout:

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//获取ViewGroup的padding
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
//当前宽度;
int curWidth = paddingLeft;
//当前高度
int curHeight = paddingTop;
for(int i = 0; i < getChildCount();){
View childView = getChildAt(i);
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
if(curWidth + paddingRight + marginLayoutParams.rightMargin + marginLayoutParams.leftMargin + childWidth <= getWidth()){
//不换行
childView.layout(curWidth + marginLayoutParams.leftMargin,
curHeight + marginLayoutParams.topMargin,
curWidth + marginLayoutParams.leftMargin + childWidth,
curHeight + marginLayoutParams.topMargin + childHeight);
curWidth += marginLayoutParams.leftMargin + childWidth + marginLayoutParams.rightMargin;
i ++;
}else{
//换行
curHeight += marginLayoutParams.topMargin + childHeight + marginLayoutParams.bottomMargin;
curWidth = paddingLeft;
}
}
}


首先需要注意的是onLayout中的curHeight初始化是:

int curHeight = paddingTop;//


这根onMeasure不一样,因为还是那句话,onMeasure只是测量,而onLayout就是放置位置了,这时就是从上到下,从左到右了。

然后就是核心代码了:

int childHeight = childView.getMeasuredHeight();
if(curWidth + paddingRight + marginLayoutParams.rightMargin + marginLayoutParams.leftMargin + childWidth <= getWidth()){
//不换行
childView.layout(curWidth + marginLayoutParams.leftMargin,
curHeight + marginLayoutParams.topMargin,
curWidth + marginLayoutParams.leftMargin + childWidth,
curHeight + marginLayoutParams.topMargin + childHeight);
curWidth += marginLayoutParams.leftMargin + childWidth +   marginLayoutParams.rightMargin;
i ++;
}else{
//换行
curHeight += marginLayoutParams.topMargin + childHeight + marginLayoutParams.bottomMargin;
curWidth = paddingLeft;
}


思路:比较某一行是否能够放下该TextView 如果能,就直接放置,然后curwidth更新数据(加上放置的TextView的宽度和margin),最后i++(判断下一个TextView是否能够放下);如果不能放下,这就将curwidth重置为0,然后换行(curheight += TextView的高度 + TextView的上下margin)

然后这个实现了最后贴上整个自定义的ViewGroup:

package com.app.test.testflowproject;

import android.content.Context;
import android.location.GnssMeasurementsEvent;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

/**
* Created by ${liumegnqiang} on 2017/5/18.
*/

public class FlowViewGroup extends ViewGroup {
public FlowViewGroup(Context context) {
super(context);
}

public FlowViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}

public FlowViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//测量模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//测量ViewGroup的宽高
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//ViewGroup的padding
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
//当前宽度
int curWidth = 0;
//当前高度
int curHeight = paddingTop + paddingBottom;
//最大宽度
int widthMax = 0;
for(int i = 0;i < getChildCount();){
View childView = getChildAt(i);
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
if(curWidth == 0 ){ curWidth = paddingLeft + paddingRight; curHeight += marginLayoutParams.topMargin + marginLayoutParams.bottomMargin + childHeight; }
if(curWidth + marginLayoutParams.rightMargin + marginLayoutParams.leftMargin + childWidth <= widthSize){ //不换行 curWidth += marginLayoutParams.rightMargin + marginLayoutParams.leftMargin + childWidth; i++; }else{ //换行 widthMax = Math.max(widthMax, curWidth); curWidth = 0; }
//判断是否是最后一行 if(i == getChildCount()){ widthMax = Math.max(widthMax, curWidth); curHeight += marginLayoutParams.topMargin + marginLayoutParams.bottomMargin + childHeight; }
}
setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : widthMax ,
heightMode == MeasureSpec.EXACTLY ? heightSize : curHeight);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//获取ViewGroup的padding
int<
bf00
/span> paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
//当前宽度;
int curWidth = paddingLeft;
//当前高度
int curHeight = paddingTop;
for(int i = 0; i < getChildCount();){
View childView = getChildAt(i);
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
if(curWidth + paddingRight + marginLayoutParams.rightMargin + marginLayoutParams.leftMargin + childWidth <= getWidth()){
//不换行
childView.layout(curWidth + marginLayoutParams.leftMargin,
curHeight + marginLayoutParams.topMargin,
curWidth + marginLayoutParams.leftMargin + childWidth,
curHeight + marginLayoutParams.topMargin + childHeight);
curWidth += marginLayoutParams.leftMargin + childWidth + marginLayoutParams.rightMargin;
i ++;
}else{
//换行
curHeight += marginLayoutParams.topMargin + childHeight + marginLayoutParams.bottomMargin;
curWidth = paddingLeft;
}
}
}
}



最后附上源码下载地址:

http://download.csdn.net/detail/lmq121210/9845960
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: