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

来源:互联网 发布:淘宝卖食品要什么条件 编辑:程序博客网 时间:2024/06/05 09:04

免积分源码下载


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

本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);}@Overrideprotected 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方法的关键代码,该句设置父容器的大小。}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {int childCount = getChildCount();// 获取子组件数int row = 1;// 子组件行数,初始化赋值为1int 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;// ---------------------------------------------------备注1right = left + width;// -----------------------------------------------备注2top = p * row + height * (row - 1);// ---------------------------------备注3bottom = top + height;// ----------------------------------------------备注4if (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 {@Overridepublic 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 0
原创粉丝点击