超好用的流式布局控件xflowlayout

在开始之前,先来看下效果图吧
其实开始使用kotlin来写的,但是最后发布到jcenter的时候,说是不支持的包名名称,所以不得已改为了java

如有发现bug的或者有建议的话,请留言吧

调用

首先导入下依赖

implementation’com.mytimeshow:xflowlayout:1.1.1’

  flowlayout
            .setRadiuSize(radiu.toFloat())
            .setItemSelectedBackGroundColor(s_bc_c)
            .setItemDefaultBackGroundColor(dd_bc_c)
            .setDefaultStokeColor(d_sto_c)
            .setSelectedStokeColor(s_sto_c)
            .setTextSize(tests.toFloat())
            .setDefaultTextColor(d_text_c)
            .setSelectTextColor(s_text_c)
            .setStokeSize(d_sto_s)
            .setItemRandowColor(r)
            .setIsSingleSlect(s)
            .setMaxSelect(max_)
            .setIsMoreSelected(m)
            .setItemInternalHorizontal(space_h)
            .setOrirntation(if(l)Flowlayout.HORIZONTAL else Flowlayout.VERTICAL)
            .setItemInternalVertical(space_v)
            .setItemContentPadding(contentp)
            .setAllDefault(d)
            .setOnItemClickListener(object: ItemOnclickListener {
                override fun onClick(text: String, textView: TextView) {
                    flowlayout.getMoreSelectedText()?.forEach { t: String? ->Log.e("flowlayout",t)  }

                }
            })
             .setOnItemLongPressListener(object: ItemLongpressListener {
                override fun onLongClick(var1: String, var2: TextView) {
                   
                }
            })
            .setTexts(texts)

注意 flowlayout.settexts()必须要最后调用哦,否则设置的属性就没有效果了

删除

 flowlayout.delete(3);                     (删除指定的位置)
 flowlayout.delete(textView);          (删除指定的textview)

设置选中

 flowlayout.setSelected(new String[]{"我们","我爱你","哈哈哈"});          (设置选中指定的内容的textview)
 flowlayout.setSelected(3);                                            (设置选中指定位置的textview ->index)
 flowlayout.setSelected(3,6);                                           (设置选中开始到结束位置下标 ->index,end)
       

多选时,获取已选中的text列表

 flowlayout.getMoreSelectedText();

单选时,选中的text

 flowlayout.getSingleSelectedText();

注意事项 单选和多选是具有互斥作用的,所以不要设置了多选,又设置了单选

在写这个控件之前,其实我是有这方面的需求的,首先是项目中有用到历史搜索的功能,因此,作为讲究“效率”的程序员,想到的就是马上百度一波,看看有没有合适的,拿来就用。不过很可惜,我并没有找到合适项目ui要求的,因为我的项目中,不仅仅是普通的自动换行的流式布局控件,还要可以自行设置textview的圆角值,默认与选中时的文字颜色或背景颜色,还最特别的一点是,一般的flowlayout只支持横向的布局,我的项目中有些地方还要用到竖向的布局,所以,我就打算撸一个flowlayoutba吧,为了区别于其他的flowlayout,我特意起在前面加了一个X,叫XFlowlayout

当然啦,作为追求美观的程序员,肯定是支持在布局文件中配置这些个参数的,不喜欢在代码中设置的可以在布局文件中去配置一些参数,下面列出自定义属性吧

 

    

        
            
            
        

        

        

        

        

        

        

        

        

        

        

        

        

        

        

        
    




大部分都是望文生义的属性,我就不专门写注释了
下面就简单来讲解下代码吧

  public static final int  HORIZONTAL = 10010;
    public static final int VERTICAL = 10011;
    protected int itemInternal_horizontal;  									//水平方向的间隙
    protected int itemInternal_vertical;										//垂直方向的间隙
    protected int itemContentPadding;											//内间距  相当于padding
    protected int orientation;												    //布局方向
    protected int defaultBackGroundColor;						                //默认背景颜色
    protected int selectedbackGroundColor;							            //多选 或单选时,选中的时候的背景颜色
    protected float radius;														//圆角值
    protected int slectedTextColor;											    //多选 或单选时,被选中的文字颜色
    protected int defaultTextColor;										        //默认文字颜色
    protected String singleTeSelectedText;									    //单选时,被选中的text
    protected ArrayList selectedTexts;									//多选时,被选中的text列表
    protected boolean isSingleSelect;										    //是否是单选
    protected boolean isMoreSelect;										        //是否多选
    protected float textSize;													//文字大小
    protected int defaultStokeColor;											//默认的边框颜色
    protected int selectedStokeColor;											//被选中时的边框颜色
    protected int maxSelect;													//多选时,最多能选中多少个
    protected int stokeWidth;													//边框大小
    protected int drawableId;													//传入自己的drawable id  比如      R.drawable.bg-round
    protected boolean isAllAttrsDefault;								        //是否全部属性都设为默认配置,当isAllAttrsDefault为true时,再去设置其他属性是不起作用的了
  

    public Flowlayout(Context context) {
       this(context,null);
    }

    public Flowlayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typeArray = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
       orientation = typeArray.getInt(R.styleable.FlowLayout_orientation, HORIZONTAL);
       itemInternal_horizontal = typeArray.getInt(R.styleable.FlowLayout_itemInternal_horizontal, 20);
       itemInternal_vertical = typeArray.getInt(R.styleable.FlowLayout_itemInternal_vertical, 20);
       radius = typeArray.getFloat(R.styleable.FlowLayout_item_radius,getResources().getDimension(R.dimen.dp_4));
       isSingleSelect = typeArray.getBoolean(R.styleable.FlowLayout_singgle_select, false);
       isMoreSelect = typeArray.getBoolean(R.styleable.FlowLayout_moreselect_select, false);
       maxSelect = typeArray.getInt(R.styleable.FlowLayout_max_select, 3);
       textSize = typeArray.getFloat(R.styleable.FlowLayout_text_size, 15.0F);
       itemContentPadding = typeArray.getInt(R.styleable.FlowLayout_item_content_internal, (int)this.getResources().getDimension(R.dimen.dp_6));
       defaultTextColor = typeArray.getColor(R.styleable.FlowLayout_default_textColor, Color.parseColor("#CCCCCC"));
       slectedTextColor = typeArray.getColor(R.styleable.FlowLayout_selected_textColor, Color.parseColor("#CCCCCC"));
       defaultBackGroundColor = typeArray.getColor(R.styleable.FlowLayout_item_default_backGroundDrawable, Color.WHITE);
       selectedbackGroundColor = typeArray.getColor(R.styleable.FlowLayout_item_select_backGroundDrawable, Color.WHITE);
       stokeWidth = typeArray.getInt(R.styleable.FlowLayout_stoke_width, 1);
       defaultStokeColor = typeArray.getColor(R.styleable.FlowLayout_default_stoke_color, Color.parseColor("#CCCCCC"));
       selectedStokeColor = typeArray.getColor(R.styleable.FlowLayout_selected_stoke_color, Color.RED);
       typeArray.recycle();
       layoutManager=new LayoutManage(this);
    }

	//默认的textview的drawable样式,可以设置圆角值,边框,和填充颜色等
    public  GradientDrawable defaultDrawable() {
        Drawable var10000 =getContext().getDrawable(R.drawable.bg_default);
        if (var10000 == null) {
            throw new TypeCastException("null cannot be cast to non-null type android.graphics.drawable.GradientDrawable");
        } else {
            GradientDrawable drawable = (GradientDrawable)var10000;
            drawable.setCornerRadius(radius);
            drawable.setColor(defaultBackGroundColor);
            drawable.setStroke(stokeWidth,defaultStokeColor);
            drawable.setShape(GradientDrawable.RECTANGLE);
            return drawable;
        }
    }

	//当属于单选或多选时,被选中的textview的样式
    public  GradientDrawable selectedDrawable() {
        Drawable var10000 =getContext().getDrawable(R.drawable.bg_red);
        if (var10000 == null) {
            throw new TypeCastException("null cannot be cast to non-null type android.graphics.drawable.GradientDrawable");
        } else {
        	**//GradientDrawable 相当于xml中定义的shape类型的drawable对象,在xml定义的属性 都可以在代码中来设置,比如圆角值CornerRadius 等**
     
            GradientDrawable drawable = (GradientDrawable)var10000;
            drawable.setCornerRadius(this.radius);
            drawable.setStroke(this.stokeWidth,selectedStokeColor);
            drawable.setColor(this.selectedbackGroundColor);
            drawable.setShape(GradientDrawable.RECTANGLE);
            return drawable;
        }
    }
	//测量的地方,这里包括了水平方向和垂直方向的测量
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (this.orientation == HORIZONTAL) {
           measyreByHorizontal(widthMeasureSpec, heightMeasureSpec);
        } else {
           measureByVertical(widthMeasureSpec, heightMeasureSpec);
        }

    }
	//当布局方向是竖直方向时的测量
    protected  void measureByVertical(int widthMeasureSpec, int heightMeasureSpec) {
        if (this.getChildCount() != 0) {
            int totalHeight =getPaddingTop() +getPaddingBottom();
            int totalWidth = 0;
            int childCount =getChildCount();
            int i = 0;

            for(int var7 = childCount; i < var7; ++i) {
                View child =getChildAt(i);
               measureChild(child, widthMeasureSpec, heightMeasureSpec);
                Intrinsics.checkExpressionValueIsNotNull(child, "child");
                totalWidth = Math.max(totalWidth, child.getMeasuredWidth());
                totalHeight += child.getMeasuredHeight() +itemInternal_vertical;
            }

           setMeasuredDimension(totalWidth +getPaddingLeft() +getPaddingRight(), totalHeight);
        }
    }
	//当布局方向是水平方向时的测量
    protected  void measyreByHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
        if (this.getChildCount() != 0) {
            int totalHeight =getPaddingTop();
            int totalWidth = MeasureSpec.getSize(widthMeasureSpec);
            int width =getPaddingLeft();
            int childCount =getChildCount();
            int i = 0;

            for(int var8 = childCount; i < var8; ++i) {
                View child =getChildAt(i);
               measureChild(child, widthMeasureSpec, heightMeasureSpec);
                if (totalHeight ==getPaddingTop()) {
                    Intrinsics.checkExpressionValueIsNotNull(child, "child");
                    totalHeight = child.getMeasuredHeight() +getPaddingTop();
                }

                Intrinsics.checkExpressionValueIsNotNull(child, "child");
                if (width + child.getMeasuredWidth() +getPaddingRight() > totalWidth -getPaddingLeft()) {
                    totalHeight += child.getMeasuredHeight() +itemInternal_vertical;
                    width =getPaddingLeft() + child.getMeasuredWidth() +itemInternal_horizontal;
                } else {
                    width += child.getMeasuredWidth() +itemInternal_horizontal;
                }
            }

           setMeasuredDimension(totalWidth, totalHeight +getPaddingBottom());
        }
    }
	//这里我把布局的逻辑抽取到了layoutmaneger类中去,主要是想减少这个类中的代码,容易阅读
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (this.layoutManager != null) {
            layoutManager.onLayout(changed, l, t, r, b);
        }

    }
}

下面是布局管理器的代码

public class LayoutManage {

    @NotNull
    private  Flowlayout flowLayout;


    @NotNull
    public  Flowlayout getFlowLayout() {
        return this.flowLayout;
    }

    public  void onLayout(boolean changed, int l, int t, int r, int b) {
        if (this.flowLayout.orientation == Flowlayout.VERTICAL) {
            this.layoutVertical();
        }

        if (this.flowLayout.orientation == Flowlayout.HORIZONTAL) {
            this.layoutHrizontal();
        }

    }

    private  void layoutHrizontal() {
        int top = this.flowLayout.getPaddingTop();
        int left = 0;
        int childCount = this.flowLayout.getChildCount();
        int i = 0;

        for(int var5 = childCount; i < var5; ++i) {
            View child = this.flowLayout.getChildAt(i);
            Intrinsics.checkExpressionValueIsNotNull(child, "child");
            if (left + child.getMeasuredWidth() > this.flowLayout.getMeasuredWidth() - this.flowLayout.getPaddingLeft()) {
                left = this.flowLayout.getPaddingLeft();
                top += child.getMeasuredHeight() + this.flowLayout.itemInternal_vertical;
            }

            if (i == 0) {
                left = this.flowLayout.getPaddingLeft();
            }

            child.layout(left, top, child.getMeasuredWidth() + left, child.getMeasuredHeight() + top);
            left += child.getMeasuredWidth() + this.flowLayout.itemInternal_horizontal;
        }

    }

    private  void layoutVertical() {
        int top = this.flowLayout.getPaddingTop();
        int childCount = this.flowLayout.getChildCount();
        int i = 0;

        for(int var4 = childCount; i < var4; ++i) {
            View child = this.flowLayout.getChildAt(i);
            int var10001 = this.flowLayout.getPaddingLeft();
            Intrinsics.checkExpressionValueIsNotNull(child, "child");
            child.layout(var10001, top, child.getMeasuredWidth() + this.flowLayout.getPaddingLeft(), child.getMeasuredHeight() + top);
            top += child.getMeasuredHeight() + this.flowLayout.itemInternal_vertical;
        }

    }

    public LayoutManage(@NotNull Flowlayout flowLayout) {

        this.flowLayout = flowLayout;
    }
}

由于代码太简单,我就不作太多的解释了。我相信,这个流式控件,几乎能满足绝大多数的需求了吧

需要看源代码的到github上去看吧 [传送门]
(https://github.com/mytimeshow/XFlowlayout)
如果感觉帮到您的话,别忘了给个star哦

你可能感兴趣的:(android)