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,到此结束



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值