android 自定義view實現流式佈局
阿新 • • 發佈:2019-02-18
今天搞了一個流式佈局:如圖
網上也有部落格講這方面的,只是每個人實現思路不一樣,這是在網上看到一篇文章講這個,我看了下,說下這個怎麼實現原理,網上好多是直接繼承了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,到此結束