1. 程式人生 > >android 自定義view實現流式佈局

android 自定義view實現流式佈局

今天搞了一個流式佈局:如圖


網上也有部落格講這方面的,只是每個人實現思路不一樣,這是在網上看到一篇文章講這個,我看了下,說下這個怎麼實現原理,網上好多是直接繼承了ViewGroup,那樣的話就有個換行和計運算元view的大小.子view的排放位置,但是這個就省略了那麼多複雜的過程,因為我繼承的是RelativeLayout,這樣就不用實現onLayout()方法去計算每個子view排放的位置,而且我這個view是用佈局檔案實現的,畫個圖理解下


現在就有個問題了,因為是繼承了RelativeLayout,那麼它的位置是相對的,這個時候就要用到view的id了,那我們是給每個tag view設定id麼,其實這個可以取巧的,因為我們是新增很多tagview,所以可以用標表示layout id,這樣處理就方便多了,

現在貼程式碼:很多地方都註釋了,如果想處理點選事件的話,看懂了,就很簡單

activty_main.xml

<RelativeLayout 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"
    >
    <com.zhimore.customflowlayout.view.FlowLayout
        xmlns:zhimore="http://schemas.android.com/apk/res-auto"
        android:id="@+id/flowlayout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="3dp"
        zhimore:isDeletable="false"
        zhimore:tagBackgroundColor="#ffff0000"
        zhimore:tagTextSize="6sp"
        zhimore:tagTextColor="#333333"
        android:background="#ffffff"
        zhimore:deletableTextSize="8sp"
        zhimore:deletableTextColor="#ff9acd32"
        />
</RelativeLayout>

FlowLayout.java
package com.zhimore.customflowlayout.view;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.zhimore.customflowlayout.R;
import com.zhimore.customflowlayout.bean.TagView;

public class FlowLayout extends RelativeLayout {
	private static final int DEFAULT_TAG_LAYOUT_COLOR = Color.parseColor("#aa66cc");//標籤預設背景
	 private static final int DEFAULT_TAG_TEXT_COLOR = Color.parseColor("#1a1a1a");//標籤文字的文字顏色
	 private static final int DEFAULT_DELETABLE_TEXT_COLOR = Color.parseColor("#1a1a1a");
	 private static final int INNER_VIEW_PADDING = 25;
	 private static final int DEFAULT_TEXT_SIZE = 14;
	 private static final int WIDTH = ViewGroup.LayoutParams.WRAP_CONTENT;
	 private static final int TAG_LAYOUT_LEFT_MERGIN = 20;
	 private int mTagBackgroundColor;//
	 private int mTagTextColor ;
	 private float mTagTextSize;
	 private boolean mIsDeletable;
	 private int mDeletableTextColor;
	 private float mDeletableTextSize;
	 private LayoutInflater mInflater;
	 private Display mDisplay;
	 private boolean mInitialized;
	 private ViewTreeObserver mViewTreeObserber;
	 private int mWidth;//FlowLayout  的寬度
	 private int mHeight;//FlowLayout 的高度
	 private static final int TAG_LAYOUT_TOP_MERGIN = 10;
	 private static final String DEFAULT_DELETABLE_STRING = "×";//刪除的文字
	 private static final int LAYOUT_WIDTH_OFFSET = 5;
	private static final String TAG = "FlowLayout";
	 private List<TagView> mTags = new ArrayList<TagView>();
	 public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context,attrs,defStyle);
	 }

	public FlowLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context,attrs,0);
	}
	public FlowLayout(Context context) {
		super(context);
	}
	private void init(Context context, AttributeSet attrs, int defStyle) {
		mDisplay  = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
		mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);//獲取LayoutInflater物件
		TypedArray typeArray = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
		mTagBackgroundColor = typeArray.getColor(R.styleable.FlowLayout_tagBackgroundColor,DEFAULT_TAG_LAYOUT_COLOR);//標籤的背景顏色
        mTagTextColor = typeArray.getColor(R.styleable.FlowLayout_tagTextColor,DEFAULT_TAG_TEXT_COLOR);//標籤的文字顏色
        mTagTextSize = typeArray.getDimension(R.styleable.FlowLayout_tagTextSize,DEFAULT_TEXT_SIZE);//標籤的文字大小
        mIsDeletable = typeArray.getBoolean(R.styleable.FlowLayout_isDeletable, false);//是否設定刪除
        mDeletableTextColor = typeArray.getColor(R.styleable.FlowLayout_deletableTextColor,DEFAULT_TAG_TEXT_COLOR);//刪除文字的顏色
        mDeletableTextSize = typeArray.getDimension(R.styleable.FlowLayout_deletableTextSize,DEFAULT_DELETABLE_TEXT_COLOR);//刪除文字的大小
        typeArray.recycle();
        
        
        mViewTreeObserber = getViewTreeObserver();
        mViewTreeObserber.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if(!mInitialized) {//這是因為這個回撥會調好幾次,還有一種就是把這個監聽移除也是可以的
                    mInitialized = true;
                    drawTags();
                }
            }
        });
	}
	/**
	 * 當你view寬和高度發生改變會回撥
	 */
	@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mWidth = w;
        mHeight = h;
    }
	public void remove(int position){
		mTags.remove(position);//刪除後要重新畫過
		drawTags();
	}
	public void addTag(TagView tag){
		 mTags.add(tag);
	}
	public void drawTags() {
		if(!mInitialized){
			return;
		}
		removeAllViews();
		float total = getPaddingLeft() + getPaddingRight();//計算FlowLayout 這個控制元件設定的padding值
		int index = 1;//現在的位置
		int pindex = index;//相對起點位置
		for(TagView item:mTags){
			final TagView tag = item;//記錄當前的標籤view
			 View tagLayout = (View) mInflater.inflate(R.layout.layout_tag, null);
             tagLayout.setId(index);//設定每一tag  layout 對應的id 因為這是相對佈局用於換或者當一行中有二個以上的tagview時設定他相對某個tagview的位置
//             tagLayout.setBackgroundColor(mTagBackgroundColor);//設定tag背景顏色
             TextView tagView = (TextView) tagLayout.findViewById(R.id.tag_txt);
             tagView.setText(tag.getText());//設定標籤view顯示的文字
             tagView.setPadding(INNER_VIEW_PADDING, INNER_VIEW_PADDING, INNER_VIEW_PADDING, INNER_VIEW_PADDING);//設定tag標籤的內容和標籤之間的距離//設定textveiw中文字和邊框間的距離
             tagView.setTextColor(mTagTextColor);//設定文字的顏色
             tagView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTagTextSize);//設定文字的大小 單位為sp
             float tagWidth = tagView.getPaint().measureText(tag.getText()) + INNER_VIEW_PADDING * 2;  //獲取每個tag view的寬度
             TextView deletableView = (TextView) tagLayout.findViewById(R.id.delete_txt);
             if(mIsDeletable) {
                 deletableView.setVisibility(View.VISIBLE);
                 deletableView.setText(DEFAULT_DELETABLE_STRING);
                 deletableView.setPadding(INNER_VIEW_PADDING, INNER_VIEW_PADDING, INNER_VIEW_PADDING, INNER_VIEW_PADDING);
                 deletableView.setTextColor(mDeletableTextColor);
                 deletableView.setTextSize(mDeletableTextSize);
                 tagWidth += deletableView.getPaint().measureText(DEFAULT_DELETABLE_STRING)+ INNER_VIEW_PADDING * 2;
             } else {
                 deletableView.setVisibility(View.GONE);
             }
             LayoutParams tagParams = new LayoutParams(WIDTH, WIDTH);
             tagParams.setMargins(0, 0, 0, 0);
             if (mWidth <= total + tagWidth + LAYOUT_WIDTH_OFFSET) {//如果子view排放的寬度大於或者等於父view的寬度就換行
                 tagParams.addRule(RelativeLayout.BELOW, pindex);
                 tagParams.topMargin = TAG_LAYOUT_TOP_MERGIN;//向上的距離 也就是marginTop
                 total = getPaddingLeft() + getPaddingRight();//如果換行 對total重新賦值
                 pindex = index;//
             }else{
            	 tagParams.addRule(RelativeLayout.ALIGN_TOP, pindex);
                 tagParams.addRule(RelativeLayout.RIGHT_OF, index - 1);
                 if (index > 1) {//如果一行中有二個tagView 就要考慮位置了
                     tagParams.leftMargin = TAG_LAYOUT_LEFT_MERGIN;
                     total += TAG_LAYOUT_LEFT_MERGIN;
                 }
             }
             total += tagWidth;//子view的累加 和父view的寬度做對比
             addView(tagLayout, tagParams);//新增到相對佈局中
             index++;
		}
	}
}

attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
   <declare-styleable name="FlowLayout">
        <attr name="tagBackgroundColor" format="color" />
        <attr name="tagTextColor" format="color" />
        <attr name="tagTextSize" format="dimension" />
        <attr name="isDeletable" format="boolean" />
        <attr name="deletableTextColor" format="color" />
        <attr name="deletableTextSize" format="dimension" />
  </declare-styleable>
</resources>

TagView.java 封裝了textview顯示的文字,當要處理點選事件 比較方便獲取textview上的文字,
package com.zhimore.customflowlayout.bean;
public class TagView {
	    private int id;//設定view tag的id  用於點選事件
	    private String text;//設定tag view上的文字
	    public TagView(int id, String text){
	        this.id = id;
	        this.text = text;
	    }

	    public int getId() {
	        return id;
	    }

	    public void setId(int id) {
	        this.id = id;
	    }

	    public String getText() {
	        return text;
	    }

	    public void setText(String text) {
	        this.text = text;
	    }
}

MainActivity.java  新增tag  view
package com.zhimore.customflowlayout;
import android.app.Activity;
import android.os.Bundle;
import com.zhimore.customflowlayout.bean.TagView;
import com.zhimore.customflowlayout.view.FlowLayout;

public class MainActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		FlowLayout flowlayout = (FlowLayout) findViewById(R.id.flowlayout);
		for(int i=0;i<20;i++){
			TagView tagView = new TagView(i, "流式佈局"+i);
			flowlayout.addTag(tagView);
		}
		flowlayout.drawTags();
	}
}

ok,到此結束