先上效果圖:
實現這樣的效果:
一般的思路就是,直接寫布局文件,用LinearLayout 嵌套多層子LinearLayout,然后根據權重layout_weight可以達到上面的效果
還有就是利用gridview了,但是這里的需求就是不能上下滑動,使用gridview的時候還要計算布局的高度,否則內容超出下滑;
開始我是用的第一種,直接在布局文件實現了,但是后來發現代碼太多太惡心哦,所以我繼承viewGroup,重寫兩個關鍵的方法:onLayout(),onMeasure()
我的大致思路:
1.計算當前視圖寬度和高度,然后根據邊距,算出每個布局的item需要分配的多少寬度和高度:
2.支持adapter的方式,動態添加每一項,還可以設置每一項點擊事件
好了,直接上關鍵代碼:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub mMaxChildWidth = 0; mMaxChildHeight = 0; int modeW = 0, modeH = 0; if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) modeW = MeasureSpec.UNSPECIFIED; if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.UNSPECIFIED) modeH = MeasureSpec.UNSPECIFIED; final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( MeasureSpec.getSize(widthMeasureSpec), modeW); final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( MeasureSpec.getSize(heightMeasureSpec), modeH); count = getChildCount(); if (count == 0) { super.onMeasure(childWidthMeasureSpec, childHeightMeasureSpec); return; } for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() == GONE) { continue; } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth()); mMaxChildHeight = Math.max(mMaxChildHeight, child.getMeasuredHeight()); } setMeasuredDimension(resolveSize(mMaxChildWidth, widthMeasureSpec), resolveSize(mMaxChildHeight, heightMeasureSpec)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // TODO Auto-generated method stub int height = b - t;// 布局區域高度 int width = r - l;// 布局區域寬度 int rows = count % colums == 0 ? count / colums : count / colums + 1;// 行數 if (count == 0) return; int gridW = (width - margin * (colums - 1)) / colums;// 格子寬度 int gridH = (height - margin * rows) / rows;// 格子高度 int left = 0; int top = margin; for (int i = 0; i < rows; i++) {// 遍歷行 for (int j = 0; j < colums; j++) {// 遍歷每一行的元素 View child = this.getChildAt(i * colums + j); if (child == null) return; left = j * gridW + j * margin; // 如果當前布局寬度和測量寬度不一樣,就直接用當前布局的寬度重新測量 if (gridW != child.getMeasuredWidth() || gridH != child.getMeasuredHeight()) { child.measure(makeMeasureSpec(gridW, EXACTLY), makeMeasureSpec(gridH, EXACTLY)); } child.layout(left, top, left + gridW, top + gridH); // System.out // .println("--top--" + top + ",bottom=" + (top + gridH)); } top += gridH + margin; } }
要實現adapter也很簡單,自定義一個接口,下面給出完整的代碼
package com.allen.view; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.makeMeasureSpec; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import com.allen.mygridlayout.R; /** * @author allen * @email jaylong1302@163.com * @date 2013-11-26 下午1:19:35 * @company 富媒科技 * @version 1.0 * @description 格子布局(類似4.0中的gridlayout) */ public class MyGridLayout extends ViewGroup { private final String TAG = "MyGridLayout"; int margin = 2;// 每個格子的水平和垂直間隔 int colums = 2; private int mMaxChildWidth = 0; private int mMaxChildHeight = 0; int count = 0; GridAdatper adapter; public MyGridLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); if (attrs != null) { TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MyGridLayout); colums = a.getInteger(R.styleable.MyGridLayout_numColumns, 2); margin = (int) a.getInteger(R.styleable.MyGridLayout_itemMargin, 2); } } public MyGridLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyGridLayout(Context context) { this(context, null); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub mMaxChildWidth = 0; mMaxChildHeight = 0; int modeW = 0, modeH = 0; if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) modeW = MeasureSpec.UNSPECIFIED; if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.UNSPECIFIED) modeH = MeasureSpec.UNSPECIFIED; final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( MeasureSpec.getSize(widthMeasureSpec), modeW); final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( MeasureSpec.getSize(heightMeasureSpec), modeH); count = getChildCount(); if (count == 0) { super.onMeasure(childWidthMeasureSpec, childHeightMeasureSpec); return; } for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() == GONE) { continue; } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth()); mMaxChildHeight = Math.max(mMaxChildHeight, child.getMeasuredHeight()); } setMeasuredDimension(resolveSize(mMaxChildWidth, widthMeasureSpec), resolveSize(mMaxChildHeight, heightMeasureSpec)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // TODO Auto-generated method stub int height = b - t;// 布局區域高度 int width = r - l;// 布局區域寬度 int rows = count % colums == 0 ? count / colums : count / colums + 1;// 行數 if (count == 0) return; int gridW = (width - margin * (colums - 1)) / colums;// 格子寬度 int gridH = (height - margin * rows) / rows;// 格子高度 int left = 0; int top = margin; for (int i = 0; i < rows; i++) {// 遍歷行 for (int j = 0; j < colums; j++) {// 遍歷每一行的元素 View child = this.getChildAt(i * colums + j); if (child == null) return; left = j * gridW + j * margin; // 如果當前布局寬度和測量寬度不一樣,就直接用當前布局的寬度重新測量 if (gridW != child.getMeasuredWidth() || gridH != child.getMeasuredHeight()) { child.measure(makeMeasureSpec(gridW, EXACTLY), makeMeasureSpec(gridH, EXACTLY)); } child.layout(left, top, left + gridW, top + gridH); // System.out // .println("--top--" + top + ",bottom=" + (top + gridH)); } top += gridH + margin; } } public interface GridAdatper { View getView(int index); int getCount(); } /** 設置適配器 */ public void setGridAdapter(GridAdatper adapter) { this.adapter = adapter; // 動態添加視圖 int size = adapter.getCount(); for (int i = 0; i < size; i++) { addView(adapter.getView(i)); } } public interface OnItemClickListener { void onItemClick(View v, int index); } public void setOnItemClickListener(final OnItemClickListener click) { if (this.adapter == null) return; for (int i = 0; i < adapter.getCount(); i++) { final int index = i; View view = getChildAt(i); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub click.onItemClick(v, index); } }); } } }
這里我給出完整的demo下載:click me!!!

