天天看點

Android自定義流式布局-FlowLayout

    很多App在搜尋頁時都有對熱門标簽的一個排列,而Android官方沒有對應的布局控件,是以自己寫了一個流式布局(也稱标簽布局)--FlowLayout。

    為了大家使用的友善,沒有添加自定義屬性,是以需要在代碼中設定下列兩個屬性值:

/**
     * 上下行間距
     */
    public int veticalSpace = 30;
    /**
     * Item間距
     */
    public int horSpace = 30;
           

    其它的隻需關注填充資料方法setAdapter,及設定點選事件setOnItemClick即可。

先上個效果圖:

Android自定義流式布局-FlowLayout

貼出完整代碼,複制即用:

package com.custom.widget;
 
import android.content.Context;
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.util.SparseIntArray;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
 
public class FlowLayout extends AdapterView<ListAdapter> implements
        View.OnClickListener {
    private ListAdapter mAdapter;
 
    /**
     * 上下行間距
     */
    public int veticalSpace = 30;
    /**
     * Item間距
     */
    public int horSpace = 30;
 
    /**
     * 每一行有多少個view
     */
    private SparseIntArray mLineViews = new SparseIntArray();
     
    private AdapterDataSetObserver mDataSetObserver;
     
    /**
     * 一行的高度由最高的item決定
     */
    private int lineHeight;
 
    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    @Override
    public ListAdapter getAdapter() {
        return mAdapter;
    }
        @Override
    public void setAdapter(ListAdapter adapter) {
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }
        mAdapter = adapter;
        if (mAdapter != null) {
            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);
            makeView();
        } else {
            removeAllViewsInLayout();
            invalidate();
        }
    }
    private void makeView() {
        removeAllViewsInLayout();
        int count = mAdapter.getCount();
        for (int i = 0; i < count; i++) {
            View child = mAdapter.getView(i, null, this);
            LayoutParams params = child.getLayoutParams();
            if (params == null)
                params = generateDefaultLayoutParams();
            addViewInLayout(child, i, params);
        }
           
<span style="white-space:pre">	</span>requestLayout();
    }
     
    private class AdapterDataSetObserver extends DataSetObserver {
 
        @Override
        public void onChanged() {
            super.onChanged();
            makeView();
            requestLayout();
        }
    }
     
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
 
        int layoutWidth = 0;
        int layoutHeight = 0;
         
        int lineWidth = getPaddingLeft() + getPaddingRight();
         
        if (mAdapter == null)
            return;
        int count = getChildCount();
        mLineViews.clear();
        int lineCount = 0;
        int line = 0;
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            lineHeight = Math.max(lineHeight, child.getMeasuredHeight());
            int childWidth = child.getMeasuredWidth();
            if (lineWidth + childWidth > widthSize) {
                mLineViews.put(line, lineCount);
                lineWidth -= horSpace;
                layoutWidth = Math.max(layoutWidth, lineWidth);
                line++;
                lineWidth = getPaddingLeft() + getPaddingRight();
                lineCount = 0;
            }
            lineCount++;
            lineWidth += childWidth + horSpace;
 
            if (i == count - 1) {
                mLineViews.put(line, lineCount);
                lineWidth -= horSpace;
                layoutWidth = Math.max(layoutWidth, lineWidth);
                lineCount = 0;
            }
        }
 
        if (modeWidth == MeasureSpec.EXACTLY) {
            layoutWidth = widthSize;
        }
 
        if (modeHeight == MeasureSpec.EXACTLY) {
            layoutHeight = heightSize;
        } else {
            int lines = mLineViews.size();
            layoutHeight = getPaddingTop() + getPaddingBottom() + lines
                    * lineHeight + (lines - 1) * veticalSpace;
        }  
        setMeasuredDimension(layoutWidth, layoutHeight);
 
    }
 
    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        int leftPos = 0;
        int topPos = 0;
 
        int lines = mLineViews.size();
        int lineCount;
         
        int position = 0;
 
        for (int i = 0; i < lines; i++) {
            lineCount = mLineViews.get(i);
            View child;
 
            topPos = getPaddingTop() + i * veticalSpace + i * lineHeight;
                leftPos = getPaddingLeft();
 
            for (int j = 0; j < lineCount; j++) {
                child = getChildAt(position);
            if (child == null)
                return;
            position++;
            child.setOnClickListener(this);
            int childWidth = child.getMeasuredWidth();
            child.layout(leftPos, topPos, leftPos + childWidth, topPos
                    + lineHeight);
 
            leftPos += childWidth + horSpace;
            }
        }
 
    }
     
    @Override
    public void onClick(View v) {
        int pos = indexOfChild(v);
        performItemClick(v, pos, 0);
    }
 
    @Override
    public void setSelection(int position) {
        // TODO Auto-generated method stub
 
    }
 
    @Override
    public View getSelectedView() {
        // TODO Auto-generated method stub
        return null;
    }
     
}
           

繼續閱讀