组件换行:自定义布局的使用

免积分源码下载


暴风影音的搜索页面有一个效果,每一行的组件如果最后一个显示即将超出屏幕宽度,则自动换行。效果图:

本demo的效果图:


实现该效果不能使用已有的组件,必须自定义一个布局。

重点介绍:自定义布局可以继承ViewGroup,要想实现该效果,我们的自定义布局里面必须要继承父类中的onMeasurt()和onLayout()两个方法。

         onMeasure()方法的作用是设置布局的显示范围,超出该范围的部分将被遮挡.核心代码是:setMeasuredDimension(int width,int height).

         onLayout()方法的作用是在父容器内布局子组件,核心功能是根据计算出来的子组件的四个”坐标”将其放在父容器的指定位置,核心代码为:childview.layout(int left,int top,int right,int bottom)

               下面贴出Demo源码,算法解释已经在注释中了。

自定义布局:

HorizantalFallWaterLayout.java

package com.example.baofendeflectdemo.view;

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

/**
 * @Description:仿暴风影音搜索页面效果
 * @company XX(北京)有限公司
 * @author Joe
 * @date 2013-12-18 下午4:38:01
 */
public class HorizantalFallWaterLayout extends ViewGroup {
	private int maxWidth;// 可使用的最大宽度

	public HorizantalFallWaterLayout(Context context) {
		super(context);
	}

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

	public HorizantalFallWaterLayout(Context context, AttributeSet attrs,
			int defStyle) {
		super(context, attrs, defStyle);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		maxWidth = MeasureSpec.getSize(widthMeasureSpec);
		int containorHeight = 0;// 容器的高度,也就是本布局的高度。初始化赋值为0.
		int count = getChildCount();// 获取该布局内子组件的个数
		for (int i = 0; i < count; i++) {
			View view = getChildAt(i);
			/**
			 * measure(int widthMeasureSpec,int
			 * heightMeasureSpec)用于设置子组件显示模式.有三个值:<br/>
			 * MeasureSpec.AT_MOST 该组件可以设置自己的大小,但是最大不能超过其父组件的限定<br/>
			 * MeasureSpec.EXACTLY 无论该组件设置大小是多少,都只能按照父组件限制的大小来显示<br/>
			 * MeasureSpec.UNSPECIFIED 该组件不受父组件的限制,可以设置任意大小
			 */
			view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
			// 把每个子组件的高度相加就是该组件要显示的高度。
			containorHeight += view.getMeasuredHeight();
		}
		setMeasuredDimension(maxWidth, containorHeight);// onMeasure方法的关键代码,该句设置父容器的大小。
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		int childCount = getChildCount();// 获取子组件数
		int row = 1;// 子组件行数,初始化赋值为1
		int left = 0;// 子组件的左边“坐标”
		int right = 0;// 子组件的右边“坐标”
		int top = 0;// 子组件的顶部“坐标”
		int bottom = 0;// 子组件的底部“坐标”
		int p = getPaddingLeft();// 在父组件中设置的padding属性的值,该值显然也会影响到子组件在屏幕的显示位置
		for (int i = 0; i < childCount; i++) {
			View view = getChildAt(i);
			int width = view.getMeasuredWidth();// 测量子组件的宽
			int height = view.getMeasuredHeight();// 测量子组件的高
			left = p + right;// ---------------------------------------------------备注1
			right = left + width;// -----------------------------------------------备注2
			top = p * row + height * (row - 1);// ---------------------------------备注3
			bottom = top + height;// ----------------------------------------------备注4
			if (right > maxWidth) {
				row++;
				left = 0;//每次换行后要将子组件左边“坐标”与右边“坐标”重新初始化
				right = 0;
				left = p + right;
				right = left + width;
				top = p * row + height * (row - 1);
				bottom = top + height;
			}
			view.layout(left, top, right, bottom);// 最后按照计算出来的“坐标”将子组件放在父容器内
		}
	}
}

MainActivity.java什么都没有做

package com.example.baofendeflectdemo;

import android.os.Bundle;
import android.app.Activity;

public class MainActivity extends Activity {

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

}

,下面使用自定义布局作为MainActivity的主布局,显示几个TextView。

activity_main.xml

<com.example.baofendeflectdemo.view.HorizantalFallWaterLayout 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"
    android:padding="5sp" >


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#DBE8F0"
        android:text="生化危机:重返基地" />


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#F8E199"
        android:text="失恋33天" />


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#FAD0D1"
        android:text="宫" />


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#D2E39C"
        android:text="南极大冒险" />


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#DBE8F0"
        android:text="战争之王" />


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#F8E199"
        android:text="最贫穷的哈佛女孩" />


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#FAD0D1"
        android:text="蒙古王" />


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#D2E39C"
        android:text="黑客帝国:重装出击" />


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#DBE8F0"
        android:text="拯救大兵瑞恩" />


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#F8E199"
        android:text="希特勒:帝国的毁灭" />


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#FAD0D1"
        android:text="十二金刚" />


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#D2E39C"
        android:text="我们俩" />


</com.example.baofendeflectdemo.view.HorizantalFallWaterLayout>

 备注1:这里假设容器内的每个组件前面都有一个兄弟组件,则该组件的左边“坐标”其实就是padding值与其上一个兄弟组件的右边“坐标”之和。

 备注2:前面已经计算出该组件的左边坐标,则其右边坐标肯定是其左边坐标与其宽度之和。

 备注3:子组件的顶部“坐标”也有一定规律:第N行的组件,其顶部坐标值是N倍的padding值与(N-1)倍的自身高的和。这个效果中所有子组件的高度是相同的,所以可以这样计算

 备注4:每个组件底部“坐标”是其顶部“坐标”的值与其高度之和。






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值