Android流式布局

来源:互联网 发布:十金数据黄金外汇直播 编辑:程序博客网 时间:2024/06/05 17:45

概述

大家都知道,java 图形编程有一个布局叫FlowLayout,即流式布局。但Android没有,所以今天教大家写一个Android版的流式布局。

代码

核心代码(FlowLayout.java)如下:
[java] view plain copy
  1. package com.example.flowlayout;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. import android.content.Context;  
  7. import android.util.AttributeSet;  
  8. import android.view.View;  
  9. import android.view.ViewGroup;  
  10. import android.widget.Button;  
  11.   
  12. public class FlowLayout extends ViewGroup {  
  13.     //记录每个View的位置  
  14.     private List<ChildPos> mChildPos = new ArrayList<ChildPos>();  
  15.       
  16.     private class ChildPos {  
  17.         int left, top, right, bottom;  
  18.         public ChildPos(int left, int top, int right, int bottom) {  
  19.             this.left = left;  
  20.             this.top = top;  
  21.             this.right = right;  
  22.             this.bottom = bottom;  
  23.         }  
  24.     }  
  25.       
  26.     public FlowLayout(Context context) {  
  27.         this(context, null);  
  28.     }  
  29.       
  30.     public FlowLayout(Context context, AttributeSet attrs) {  
  31.         this(context, attrs, 0);  
  32.     }  
  33.       
  34.     /** 
  35.      * 最终调用这个构造方法 
  36.      * @param context 上下文 
  37.      * @param attrs xml属性集合 
  38.      * @param defStyle Theme中定义的style 
  39.      */  
  40.     public FlowLayout(Context context, AttributeSet attrs, int defStyle) {  
  41.         super(context, attrs, defStyle);  
  42.     }  
  43.       
  44.     /** 
  45.      * 测量宽度和高度 
  46.      */  
  47.     @Override  
  48.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  49.         //获取流式布局的宽度和模式  
  50.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  51.         int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  52.         //获取流式布局的高度和模式  
  53.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  54.         int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  55.           
  56.         //使用wrap_content的流式布局的最终宽度和高度  
  57.         int width = 0, height = 0;  
  58.         //记录每一行的宽度和高度  
  59.         int lineWidth = 0, lineHeight = 0;  
  60.         //得到内部元素的个数  
  61.         int count = getChildCount();  
  62.         mChildPos.clear();  
  63.         for (int i = 0; i < count; i++) {  
  64.             //获取对应索引的view  
  65.             View child = getChildAt(i);  
  66.             //测量子view的宽和高  
  67.             measureChild(child, widthMeasureSpec, heightMeasureSpec);  
  68.             MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();  
  69.             //子view占据的宽度  
  70.             int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;  
  71.             //子view占据的高度  
  72.             int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;  
  73.             //换行  
  74.             if (lineWidth + childWidth > widthSize - getPaddingLeft() - getPaddingRight()) {  
  75.                 //取最大的行宽为流式布局宽度  
  76.                 width = Math.max(width, lineWidth);  
  77.                 //叠加行高得到流式布局高度  
  78.                 height += lineHeight;  
  79.                 //重置行宽度为第一个View的宽度  
  80.                 lineWidth = childWidth;  
  81.                 //重置行高度为第一个View的高度  
  82.                 lineHeight = childHeight;  
  83.                 //记录位置  
  84.                 mChildPos.add(new ChildPos(  
  85.                         getPaddingLeft() + lp.leftMargin,   
  86.                         getPaddingTop() + height + lp.topMargin,   
  87.                         getPaddingLeft() + childWidth - lp.rightMargin,   
  88.                         getPaddingTop() + height + childHeight - lp.bottomMargin));  
  89.             } else {  //不换行  
  90.                 //记录位置  
  91.                 mChildPos.add(new ChildPos(  
  92.                         getPaddingLeft() + lineWidth + lp.leftMargin,   
  93.                         getPaddingTop() + height + lp.topMargin,   
  94.                         getPaddingLeft() + lineWidth + childWidth - lp.rightMargin,   
  95.                         getPaddingTop() + height + childHeight - lp.bottomMargin));  
  96.                 //叠加子View宽度得到新行宽度  
  97.                 lineWidth += childWidth;  
  98.                 //取当前行子View最大高度作为行高度  
  99.                 lineHeight = Math.max(lineHeight, childHeight);  
  100.             }  
  101.             //最后一个控件  
  102.             if (i == count - 1)  
  103.             {  
  104.                 width = Math.max(lineWidth, width);  
  105.                 height += lineHeight;  
  106.             }  
  107.         }  
  108.           
  109.         setMeasuredDimension(  
  110.                        widthMode == MeasureSpec.EXACTLY ? widthSize : width + getPaddingLeft() + getPaddingRight(),  
  111.                        heightMode == MeasureSpec.EXACTLY ? heightSize : height + getPaddingTop() + getPaddingBottom());  
  112.     }  
  113.       
  114.     /** 
  115.      * 让ViewGroup能够支持margin属性 
  116.      */  
  117.     @Override  
  118.     public LayoutParams generateLayoutParams(AttributeSet attrs) {  
  119.         return new MarginLayoutParams(getContext(), attrs);  
  120.     }  
  121.       
  122.     /** 
  123.      * 设置每个View的位置 
  124.      */  
  125.     @Override  
  126.     protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  127.         int count = getChildCount();  
  128.         for (int i = 0; i < count; i++) {  
  129.             View child = getChildAt(i);  
  130.             ChildPos pos = mChildPos.get(i);  
  131.             //设置View的左边、上边、右边底边位置  
  132.             child.layout(pos.left, pos.top, pos.right, pos.bottom);  
  133.         }  
  134.     }  
  135. }  
MainActivity.java的代码如下:
[java] view plain copy
  1. package com.example.flowlayout;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.view.LayoutInflater;  
  6. import android.view.View;  
  7. import android.view.ViewGroup.MarginLayoutParams;  
  8. import android.widget.Button;  
  9. import android.widget.EditText;  
  10. import android.widget.TextView;  
  11.   
  12. public class MainActivity extends Activity {  
  13.     //标签名称  
  14.     private String mTvNames[] = {"动漫""钉宫理惠""灼眼的夏娜""绯弹的亚里亚""零之使魔""夕阳染红的街道"};   
  15.     //流式布局  
  16.     private FlowLayout mFlowLayout;  
  17.     //用于动态添加TextView到流式布局的按钮  
  18.     private Button mBtn;  
  19.     //接收新添加TextView的名称  
  20.     private EditText mEdit;  
  21.       
  22.     @Override  
  23.     protected void onCreate(Bundle savedInstanceState) {  
  24.         super.onCreate(savedInstanceState);  
  25.         setContentView(R.layout.activity_main);  
  26.           
  27.         mFlowLayout = (FlowLayout) findViewById(R.id.flow_layout);  
  28.         init();  
  29.           
  30.         mEdit = (EditText) findViewById(R.id.edit);  
  31.         mBtn = (Button) findViewById(R.id.btn);  
  32.         mBtn.setOnClickListener(new View.OnClickListener() {  
  33.             @Override  
  34.             public void onClick(View v) {  
  35.                 String s = mEdit.getText().toString();  
  36.                 addTextView(s);  
  37.             }  
  38.         });  
  39.     }  
  40.       
  41.     public void init() {  
  42.         //遍历标签名称数组  
  43.         for (String s : mTvNames) {  
  44.             addTextView(s);  
  45.         }  
  46.     }  
  47.       
  48.     public void addTextView(String tvName) {  
  49.         //加载TextView并设置名称,并设置名称  
  50.         TextView tv = (TextView) LayoutInflater.from(this).inflate(R.layout.tv, mFlowLayout, false);  
  51.         tv.setText(tvName);  
  52.         //把TextView加入流式布局  
  53.         mFlowLayout.addView(tv);  
  54.     }  
  55. }  
布局文件(activity_main.xml)如下:
[html] view plain copy
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="match_parent"  
  4.     android:orientation="vertical" >  
  5.   
  6.     <com.example.flowlayout.FlowLayout  
  7.         android:id="@+id/flow_layout"  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="0dp"  
  10.         android:layout_weight="1"  
  11.         android:padding="10dp" >  
  12.     </com.example.flowlayout.FlowLayout>  
  13.   
  14.     <LinearLayout  
  15.         android:layout_width="match_parent"  
  16.         android:layout_height="wrap_content"  
  17.         android:orientation="horizontal" >  
  18.   
  19.         <EditText  
  20.             android:id="@+id/edit"  
  21.             android:layout_width="0dp"  
  22.             android:layout_height="wrap_content"  
  23.             android:layout_weight="1"  
  24.             android:hint="请输入新标签名称" />  
  25.   
  26.         <Button  
  27.             android:id="@+id/btn"  
  28.             android:layout_width="wrap_content"  
  29.             android:layout_height="wrap_content"  
  30.             android:text="添加" />  
  31.     </LinearLayout>  
  32.   
  33. </LinearLayout>  
每个标签的布局(tv.xml)如下:
[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <TextView xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="wrap_content"  
  4.     android:layout_height="wrap_content"  
  5.     android:layout_margin="10dp"  
  6.     android:background="@drawable/bg"  
  7.     android:text="hello"  
  8.     android:textColor="#FFF"  
  9.     android:textSize="20sp" >  
  10.   
  11. </TextView>  
标签背景效果(bg.xml)如下:
[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <shape xmlns:android="http://schemas.android.com/apk/res/android" >  
  3.     <solid android:color="#666666" />  
  4.     <corners android:radius="15dp" />  
  5.     <padding  
  6.         android:bottom="5dp"  
  7.         android:left="10dp"  
  8.         android:right="10dp"  
  9.         android:top="5dp" />  
  10. </shape>  
效果展示:
0 0