流式布局

onMeasure()函数由ViewGroup调用子类的方法,因此值也是从这个ViewGroup中传入的

1、当我们设置自定义控件width或height为fill_parent时,容器在布局时调用子view的measure方法传入的模式是EXACTLY

2、当设置自定义控件为 wrap_content时,容器传进去的是AT_MOST,表示子view的大小最多是多少

3、当设置自定义控件的大小设置为精确值时,容器传入的是EXACTLY

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.imooc.view.FlowLayout
        android:id="@+id/id_flowlayout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="#E5E5F5"
        android:padding="20dp" >
    </com.imooc.view.FlowLayout>

</RelativeLayout>

tv.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="@drawable/tv_bg"
    android:textColor="#5BC4ED"
    android:text="Helloworld" >

</TextView>

MainActivity

package com.imooc.flowlayout;

import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.widget.TextView;

import com.imooc.view.FlowLayout;

public class MainActivity extends Activity {
	private String[] mVals = new String[] { "Hello", "Android", "Weclome Hi ",
			"Button", "TextView", "Hello", "Android", "Weclome",
			"Button ImageView", "TextView", "Helloworld", "Android",
			"Weclome Hello", "Button Text", "TextView" };

	private FlowLayout mFlowLayout;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//加载布局
		setContentView(R.layout.activity_main);
		//获取视图id
		mFlowLayout = (FlowLayout) findViewById(R.id.id_flowlayout);
		//初始化数据
		initData();
	}

	public void initData() {
		LayoutInflater mInflater = LayoutInflater.from(this);
		for (int i = 0; i < mVals.length; i++) {
			TextView tv = (TextView) mInflater.inflate(R.layout.tv,
					mFlowLayout, false);
			tv.setText(mVals[i]);
			mFlowLayout.addView(tv);
		}

	}

}

FlowLayout

package com.imooc.view;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

public class FlowLayout extends ViewGroup {

	/**
	 * 构造方法
	 */
	public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public FlowLayout(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public FlowLayout(Context context) {
		this(context, null);
	}

	/**
	 * ViewGroup指定了其LayoutParams为MarginLayoutParams。 子类可以获取
	 */
	@Override
	public LayoutParams generateLayoutParams(AttributeSet attrs) {
		return new MarginLayoutParams(getContext(), attrs);
	}

	/**
	 * 自定义控件
	 * android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       heightMeasureSpec 是自定义控件的父类传给自定义控件的,里面包含父类约束子类的模式、高宽值
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		/**容器的宽和高*/
		int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
		int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
		int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
		int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

		// wrap_content需要计算高度、宽度--容器的行高、行宽
		int width = 0;
		int height = 0;

		// 记录每一行的宽度与高度
		int lineWidth = 0;
		int lineHeight = 0;

		// 得到内部元素的个数
		int cCount = getChildCount();
		for (int i = 0; i < cCount; i++) {
			View child = getChildAt(i);
			// 测量子View的宽和高,调用viewGroup的测量子view的方法
			measureChild(child, widthMeasureSpec, heightMeasureSpec);
			// 得到子类LayoutParams
			MarginLayoutParams lp = (MarginLayoutParams) child
					.getLayoutParams();

			// 子View占据的宽度
			int childWidth = child.getMeasuredWidth() + lp.leftMargin
					+ lp.rightMargin;
			// 子View占据的高度
			int childHeight = child.getMeasuredHeight() + lp.topMargin
					+ lp.bottomMargin;

			// 当前的行宽+子view的宽度>容器的宽度 --就换行
			if (lineWidth + childWidth > sizeWidth - getPaddingLeft()
					- getPaddingRight()) {
				// 对比得到最大的宽度
				width = Math.max(width, lineWidth);
				// 重置lineWidth--换行之后就是子view的宽度
				lineWidth = childWidth;
				// 记录行高
				height += lineHeight;
				//重置每一行的行高
				lineHeight = childHeight;
			} else
			// 未换行
			{
				// 叠加行宽
				lineWidth += childWidth;
				// 得到当前行最大的高度
				lineHeight = Math.max(lineHeight, childHeight);
			}
			// 最后一个控件
			if (i == cCount - 1) {
				width = Math.max(lineWidth, width);
				height += lineHeight;
			}
		}

		/**
		 * MeasureSpec.EXACTLY说明自定义控件是fill_parent
		 * MeasureSpec.AT_MOST说明是wrap_content
		 */
		setMeasuredDimension(
				//
				modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width
						+ getPaddingLeft() + getPaddingRight(),
				modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height
						+ getPaddingTop() + getPaddingBottom()//
		);

	}

	/**
	 * 存储所有的View
	 */
	private List<List<View>> mAllViews = new ArrayList<List<View>>();
	/**
	 * 每一行的高度
	 */
	private List<Integer> mLineHeight = new ArrayList<Integer>();

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		mAllViews.clear();
		mLineHeight.clear();

		// 当前ViewGroup的宽度
		int width = getWidth();

		int lineWidth = 0;
		int lineHeight = 0;

		/*
		 * 每一行的子View集合
		 */
		List<View> lineViews = new ArrayList<View>();

		int cCount = getChildCount();

		for (int i = 0; i < cCount; i++) {
			View child = getChildAt(i);
			//拿到子类的
			MarginLayoutParams lp = (MarginLayoutParams) child
					.getLayoutParams();
			//获取子类的宽度高度
			int childWidth = child.getMeasuredWidth();
			int childHeight = child.getMeasuredHeight();

			// 如果需要换行
			if (childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width
					- getPaddingLeft() - getPaddingRight()) {
				// 记录LineHeight
				mLineHeight.add(lineHeight);
				// 记录当前行的Views
				mAllViews.add(lineViews);

				// 重置我们的行宽和行高
				lineWidth = 0;
				lineHeight = childHeight + lp.topMargin + lp.bottomMargin;
				// 重置我们的View集合
				lineViews = new ArrayList<View>();
			}
			lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
			lineHeight = Math.max(lineHeight, childHeight + lp.topMargin
					+ lp.bottomMargin);
			//每一行添加子view
			lineViews.add(child);

		}// for end
			// 处理最后一行
		mLineHeight.add(lineHeight);
		mAllViews.add(lineViews);

		// 设置子View的位置

		int left = getPaddingLeft();
		int top = getPaddingTop();

		// 行数
		int lineNum = mAllViews.size();

		for (int i = 0; i < lineNum; i++) {
			// 当前行的所有的View
			lineViews = mAllViews.get(i);
			lineHeight = mLineHeight.get(i);

			for (int j = 0; j < lineViews.size(); j++) {
				View child = lineViews.get(j);
				// 判断child的状态
				if (child.getVisibility() == View.GONE) {
					continue;
				}

				MarginLayoutParams lp = (MarginLayoutParams) child
						.getLayoutParams();

				int lc = left + lp.leftMargin;
				int tc = top + lp.topMargin;
				int rc = lc + child.getMeasuredWidth();
				int bc = tc + child.getMeasuredHeight();

				// 为子View进行布局
				child.layout(lc, tc, rc, bc);

				left += child.getMeasuredWidth() + lp.leftMargin
						+ lp.rightMargin;
			}
			left = getPaddingLeft();
			top += lineHeight;
		}

	}

}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 00:02:50

流式布局的相关文章

含有过滤功能的android流式布局

FilterFlowLayout 含有过滤功能的流式布局, 参考FlowLayout 可以去除宽度不在范围(比例或真实值)内的子view 可以设置最大行数 可以添加组件间水平间距 可以添加行间距 系统要求 Android 4.0以上 快速使用 <me.codeboy.android.lib.FilterFlowLayout xmlns:cb="http://schemas.android.com/apk/res-auto" android:id="@+id/filter

Android自定义之流式布局

流式布局,好处就是父类布局可以自动的判断子孩子是不是需要换行,什么时候需要换行,可以做到网页版的标签的效果.今天就是简单的做了自定义的流式布局. 具体效果: 原理: 其实很简单,Measure  Layout.只需要这两个步骤就可以搞定了.完全的手动去Measure  Layout. 我们看一下代码. 解释就在代码里面做注释了,因为使用为知笔记写的博客,格式不符合代码格式.大家可以看具体的源码.最后又源码下载地址. 1.Measure  测量 @Override protected void o

GUI布局:边界布局、流式布局、网格布局、卡片布局

边界布局 package guiTest; //JFrame默认的是边界布局BorderLayout import java.awt.BorderLayout; import javax.swing.JButton; import javax.swing.JFrame; public class BorderLayoutDemo { public static void main(String[] args) { JFrame f = new JFrame("边界布局BorderLayout&q

c# winform panel 流式布局 panel块可自动排列

代码下载地址  http://download.csdn.net/detail/simadi/7677053 c# winform panel 流式布局 panel块可自动排列,布布扣,bubuko.com

css3流式布局

css3布局方式: 不推荐使用float,有时候使用浮动的时候,对于可适应的流氏布局,无法胜任. 推荐使用css3的display:webkit-box. 使用的html代码 <div class="warp"> <div class="one"> </div> <div class="two"></div> <div class="three"><

静态布局、自适应布局、流式布局、响应式布局、弹性布局简析

近期学习,有很多感想,有时候看似相近的概念,其实意义却不相同.所以学习要针对不同的名词有明确的区分意识. 抽空时间,打算学习下display:flex;本以为就是一个小小的知识点,正式去研究的时候,才发现display:flex;有很多内容,能实现很多效果.比如三栏布局(左右两栏固定,中间栏自适应),圣杯布局. 后来想着经常听到流式布局,自适应布局,响应式布局,他们有什么区别呢,就去搜了许多内容查看,才发现每种布局都有优缺点和不同使用场景. 静态布局:给页面元素设置固定的宽度和高度,单位用px,

移动web中的流式布局和viewport知识介绍

1   流式布局 其实  流式布局  就是百分比布局,通过盒子的宽度设置成百分比来根据屏幕的宽度来进行伸缩,不受固定像素的限制,内容向两侧填充. 这样的布局方式  就是移动web开发使用的常用布局方式 2    Viewport 我们猜想下pc页面在移动设备上显示情况. 放不下,缩放? 我们测试下pc页面在移动设备上显示. 默认的缩放的显示的 认识viewport 在移动端用来承载网页的这个区域,就是我们的视觉窗口,viewport(视口),这个区域可是设置高度宽度,可是按比例放大缩小,而且能设

JAVA 流式布局管理器

//流式布局管理器 import java.awt.*; import javax.swing.*; public class Jiemian2 extends JFrame{ //定义组件 JButton[] an = {null,null,null,null,null,null,null,null}; public static void main(String[] args){ //运行本类的构造方法 Jiemian2 jiemian = new Jiemian2(); } public

android流式布局热门标签的实现

在日常的app使用中,我们会在android 的app中看见热门标签等自动换行的流式布局,今天就为大家分享一种android流式布局的实现. 先看最终效果 自定义流式布局的实现 package com.sunny.flowlayout.view; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.util.AttributeSet; import an

自定义流式布局

1.概述 何为FlowLayout,就是控件根据ViewGroup的宽,自动的往右添加,如果当前行剩余空间不足,则自动添加到下一行.有点所有的控件都往左飘的感觉,第一行满了,往第二行飘~所以也叫流式布局.Android并没有提供流式布局,但是某些场合中,流式布局还是非常适合使用的,比如关键字标签,搜索热词列表等,比如下图: 这些都特别适合使用FlowLayout 2.简单的分析 1.对于FlowLayout,需要指定的LayoutParams,我们目前只需要能够识别margin即可,即使用Mar