Implement a customized FlowLayout

FlowLayout inherited ViewGroup, can quickly help you achieve Tablayout and Label label, containing a variety of effects to help you quickly realize APP UI feature that allows you to focus on the code architecture, say goodbye to the tedious UI.

Code project: to achieve a customizable FlowLayout

If you want to write one yourself, here are a few articles

Implement a customized TabFlowLayout (a) - measuring and layout

Implement a customized TabFlowLayout (b) - rolling and achieve a smooth transition

Implement a customized TabFlowLayout (C) - Dynamic data is added with a common interface package

Implement a customized TabFlowLayout (IV) - in combination with ViewPager, cool effect achieved

Recyclerview achieve double linkage table

A related

allprojects {
    repositories {
       ...
        maven { url 'https://jitpack.io' }
        
    }
}
复制代码
implementation 'com.github.LillteZheng:FlowHelper:v1.16'
复制代码

Second, the effect of

First of all, it is TabFlowLayout effect, it supports horizontal and vertical layout in two ways, first of all look at the effect of support:

Combined ViewPager No binding ViewPager
TabFlowLayout vertical, RecyclerView linkage effect

In addition to TabFlowLayout, there LAbelFlowLayout tabbed layout, supports word wrap

LAbelFlowLayout tabbed layout

You can see the current TabFlowLayout supports the following effects:

  • rectangle
  • triangle
  • Rounded
  • shape or bitmap files and other resources
  • Custom Functions
  • Item amplification effect, the above effect can be shared with
  • Color gradient, it needs to TabColorTextView control can be shared with the above-described effects, the case where only supports viewpager
  • Vertical results, set tab_orientation = vertical

Third, the use

Mainly TabFlowLayout and LabelFlowLayout two controls

3.1 TabFlowLayout

首先是在 xml 中,填写 TabFlowLayout 控件,它支持横竖排列,默认横向,可以使用tab_orientation = "vertical" 更换成竖直排列,一个不带效果,支持横向的 TabFlowLayout 如下:

XML

<com.zhengsr.tablib.view.flow.TabFlowLayout
    android:id="@+id/resflow"
    android:layout_width="wrap_content"
    android:layout_marginTop="5dp"
    android:background="#6D8FB0"
    android:layout_height="wrap_content"/>
复制代码

比如要加矩形,三角形,可以使用 app:tab_type 这个属性,比如一个矩形:

<com.zhengsr.tablib.view.flow.TabFlowLayout
    android:id="@+id/rectflow"
    android:layout_width="wrap_content"
    android:layout_marginTop="5dp"
    app:tab_type="rect"
    app:tab_color="@color/colorPrimary"
    app:tab_height="3dp"
    app:tab_width="20dp"
    app:tab_margin_b="3dp"
    android:background="@color/black_ff_bg"
    app:tab_scale_factor="1.2"
    app:tab_item_autoScale="true"
    android:layout_height="wrap_content"/>
复制代码

这里解释一下关键几个自定参数

  • tab_type : 填写效果类型,这里使用 rect
  • tab_color : tab 的颜色
  • tab_width :tab 的宽度,不填的话,默认宽肩宽度
  • tab_height : tab 的 高度
  • tab_margin_b :margin_bottom 的意思,相应的还有 l,t,r,b
  • tab_item_autoScale : 是否自动放大缩小效果,默认false
  • tab_scale_factor :放大因子,这里放大1.2 倍,默认为 1

上面几个为基础属性,这里填写 tri 也可以同样设置。当 type 为 round 或者 res 时,width 和 height 则可以不填,因为要根据控件本书去适配大小。

其他说明,可以参看下面的自定义属性说明。

Java

那么,在 xml 写好了,接着,就是在 Activity 中,这样写:

private void rectFlow(){
    TabFlowLayout flowLayout = findViewById(R.id.rectflow);
    //设置数据,这里以 setAdapter 的形式
    flowLayout.setAdapter(new TabFlowAdapter<String>(R.layout.item_msg,mTitle) {
        @Override
        public void onItemSelectState(View view, boolean isSelected) {
            super.onItemSelectState(view, isSelected);
            //选中时,可以改变不同颜色,如果你的background 为 selector,可以不写这个
            if (isSelected){
                setTextColor(view,R.id.item_text,Color.WHITE);
            }else{
                setTextColor(view,R.id.item_text,getResources().getColor(R.color.unselect));
            }
        }

        @Override
        public void bindView(View view, String data, int position) {
            //绑定数据
            setText(view,R.id.item_text,data)
                    .setTextColor(view,R.id.item_text,getResources().getColor(R.color.unselect));
            if (position == 0){
                setVisible(view,R.id.item_msg,true);
            }


        }
    });

}
复制代码

可以看到,只需要设置 adapter 就行了,需要注意的是你要传入子控件的 layout,这样方便你自定义你的布局,比如一个TextView 和一个红点点。具体细节,可以参看这个:

实现一个可定制化的TabFlowLayout(三) -- 动态数据添加与常用接口封装

如果你需要使用颜色渐变的效果,TextView 换成 TabColorTextView 就可以了,比如:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <com.zhengsr.tablib.view.TabColorTextView
        android:id="@+id/item_text"
        android:layout_width="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        tools:text="测试"
        android:paddingTop="6dp"
        android:paddingBottom="6dp"
        android:paddingStart="12dp"
        android:paddingEnd="12dp"
        android:textSize="14sp"
        app:colortext_default_color="@color/unselect"
        app:colortext_change_color="@color/colorAccent"
        android:gravity="center"
        android:layout_height="wrap_content"/>

    <TextView
        android:id="@+id/item_msg"
        android:layout_width="5dp"
        android:layout_height="5dp"
        android:gravity="center"
        android:textSize="8dp"
        android:background="@drawable/shape_red_radius"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_margin="5dp"
        android:visibility="gone"
       />



</android.support.constraint.ConstraintLayout>
复制代码

3.1.1、结合Viewpager

结合 ViewPager 非常简单,如下:

flowLayout.setViewPager(...) 即可.

复制代码

为了避免卡顿,当viewpager结合fragment时,可以有以下优化手段:

  • fragment 布局复杂或者网络加载数据时,建议在懒加载中去初始化或者加载数据
  • viewpager 增加缓存,setOffscreenPageLimit(3)。
  • setCurrentItem(position,false),滚动设置为false,然后用 flowLayout 实现item的动画,flowLayout.setItemAnim(position)

**如果您觉得viewpager切换太快,可以使用 ViewPagerHelperUtils.initSwitchTime(getContext(), viewPager, 600) 改变滚动速度 **

它有几个方法,参考这个解释就可以了。

  /**
   * 配置viewpager
   * @param viewPager
   * @param textId  view 中 textview 的id
   * @param selectedIndex 默认选中的item,初始值为0,也可以从第二页或者其他 位置
   * @param unselectedColor 没有选中的颜色 TabColorTextView 中失效
   * @param selectedColor 选中的颜色 TabColorTextView 中失效
   */
  public void setViewPager(ViewPager viewPager, int textId, int selectedIndex, int unselectedColor, int selectedColor) {

  }
复制代码

3.1.2、自定义属性动态配置

可能你不想在 xml 直接定死,那么可以直接使用 TabBean 去配置,比如使用 tab_type=res, 你的drawable 使用 shape 当移动背景的配置如下:

private void resFlow(){
    final TabFlowLayout flowLayout = findViewById(R.id.resflow);

    flowLayout.setViewPager(mViewPager);
    /**
     * 配置自定义属性
     */

    TabBean bean = new TabBean();
    bean.tabType = FlowConstants.RES;
    bean.tabItemRes = R.drawable.shape_round;
    //点击的动画执行时间 ms
    bean.tabClickAnimTime = 300;
    bean.tabMarginLeft = 5;
    bean.tabMarginTop = 12;
    bean.tabMarginRight = 5;
    bean.tabMarginBottom = 10;
    
    //设置进去即可
    flowLayout.setTabBean(bean);

    flowLayout.setAdapter(new TabFlowAdapter<String>(R.layout.item_msg,mTitle) {
        @Override
        public void bindView(View view, String data, int position) {
            setText(view,R.id.item_text,data);
        }

        @Override
        public void onItemClick(View view, String data, int position) {
            super.onItemClick(view, data, position);
            mViewPager.setCurrentItem(position);
        }
    });
}
复制代码

3.1.3、自定义action

如果上面没有你想要的怎么办?,那么项目也支持用户自定义,比如自定义一个白色圆点效果,只需要继承 BaseAction 即可:


    /**
     * 绘制一个圆的指示器
     */
    class CircleAction extends BaseAction{
        private static final String TAG = "CircleAction";
        @Override
        public void config(TabFlowLayout parentView) {
            super.config(parentView);
            View child = parentView.getChildAt(0);
            if (child != null) {
                float l = parentView.getPaddingLeft() + child.getMeasuredWidth()/2;
                float t = parentView.getPaddingTop() +  child.getMeasuredHeight() - mTabHeight/2 -mMarginBottom;
                float r = mTabWidth + l;
                float b = child.getMeasuredHeight() - mMarginBottom;
                mTabRect.set(l,t,r,b);
            }
        }


        @Override
        protected void valueChange(TabValue value) {
            super.valueChange(value);
            
            /**
             * value 子控件在滚动时的 left 和 right,可以理解为偏移量
             * TabRect 为控件移动时的局域。
             */
            //由于自定义的,都是从left 开始算起的,所以这里还需要加上圆的半径
            mTabRect.left = value.left + mTabWidth/2;
        }

        @Override
        public void draw(Canvas canvas) {
            canvas.drawCircle(mRect.left,mRect.top,mTabWidth/2,mPaint);
        }
    }
复制代码

通过重写 valueChange 拿到移动偏移量,然后通过 flowLayout.setAction(new CircleAction()) 即可。如果是竖直方向,拿到 value.top,value.bottom 再去写逻辑即可。

3.1.4、参考代码

上面的效果,可以参考以下代码:

有ViewPager的布局代码

有ViewPager的Activity

3.2、TabFlowLayout 竖直效果

前面说到,只需要把 tab_orientation 设置成 vertical 即可,相应的 当 type 为 rect 或者 tri 时,还可以通过 tab_tab_action_orientaion 选择 left 还是right 的效果:

    <com.zhengsr.tablib.view.flow.TabFlowLayout
        android:id="@+id/tabflow"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:tab_type="rect"
        app:tab_color="@color/colorPrimary"
        app:tab_orientation="vertical"
        app:tab_width="2dp"
        app:tab_height="20dp"
        app:tab_action_orientaion="left"
        android:background="@color/page_gray_cccc"
        />
复制代码

效果如下:

和 recyclerview 的联动效果,可以参考在这个:

Recyclerview 实现双联表联动

3.3 LabelFlowLayout

LabelFlowLayout 竖向布局,支持自动换行,单选、多选、长按等功能.

它的状态变化,根据 view 的 selected 来,所以大家可以写 selector 当背景,或者在方法中自己设置

使用

LabelFlowLayout 默认单选,在 xml 这样配置:

<com.zhengsr.tablib.view.flow.LabelFlowLayout
    android:id="@+id/singleflow"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
/>
复制代码

FlowLayout 使用也使用 adapter 去配置数据:

LabelFlowLayout flowLayout = findViewById(R.id.singleflow);
final LabelFlowAdapter adapter;
flowLayout.setAdapter(adapter = new LabelFlowAdapter<String>(R.layout.item_textview,mTitle) {
    @Override
    public void bindView(View view, String data, int position) {
        setText(view,R.id.item_text,data);
    }
    @Override
    public void onItemSelectState(View view, boolean isSelected) {
        super.onItemSelectState(view, isSelected);
        TextView textView = view.findViewById(R.id.item_text);
        if (isSelected) {
            textView.setTextColor(Color.WHITE);
        } else {
            textView.setTextColor(Color.GRAY);
        }
    }
});
复制代码

多选

其实只需要配置 flowLayout.setMaxCount(3); 就可以了,然后adapter 中重写:

@Override
public void onReachMacCount(List<Integer> ids, int count) {
    super.onReachMacCount(ids, count);
    Toast.makeText(LabelActivity.this, "最多只能选中 "+count+" 个"+" 已选中坐标: "+ids, Toast.LENGTH_SHORT).show();
}
复制代码

如果您选默认选中其中一些item,可以使用 flowLayout.setSelects(2,3,5);

长按

其实就是长按view,至于状态的变化,由自己去写:

    private void canLongFlow(){
        LabelFlowLayout flowLayout = findViewById(R.id.longflow);
        flowLayout.setAdapter(new LabelFlowAdapter<String>(R.layout.item_search_layout,mTitle2) {
            @Override
            public void bindView(View view, String data, int position) {
                setText(view,R.id.search_msg_tv,data)
                        .addChildrenClick(view,R.id.search_delete_iv,position);
            }

            @Override
            public void onItemSelectState(View view, boolean isSelected) {
                super.onItemSelectState(view, isSelected);
                if (!isSelected){
                    view.setBackgroundResource(R.drawable.shape_search);
                    setVisible(view,R.id.search_delete_iv,false);
                }
            }

            @Override
            public void onItemClick(View view, String data, int position) {
                super.onItemClick(view, data, position);
                Toast.makeText(LabelActivity.this, "点击了: "+data, Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onItemChildClick(View childView, int position) {
                super.onItemChildClick(childView, position);
                if (childView.getId() == R.id.search_delete_iv){
                    mTitle2.remove(position);
                    notifyDataChanged();
                }
            }

            @Override
            public boolean onItemLongClick(View view,int position) {
                /**
                 * 置所有view 的 select 为 false
                 */
                resetStatus();
                view.setBackgroundResource(R.drawable.shape_search_select);
                setVisible(view,R.id.search_delete_iv,true);
                return super.onItemLongClick(view,position);
            }


        });
    }

复制代码

3.3.1参考代码:

布局代码

Activity代码

四、Adpater 支持的方法

TabFlowLayout 和 LAbelFlowLayout 都是通过 setAdapter 的方式去加载数据的,除了支持 setText(..) ,setTextColor(..) ,setImageView 等,还支持 click 事件,具体的方法,可以参考下面的代码:

Adapter方法

五、自定义属性列表

TabFlowLayout

名称 类型 说明
tab_type rect,tri,round,color,res tab的类型,目前支持矩形,三角形、圆角、颜色渐变、资源res
tab_color color 指示器的颜色,当类型为 rect、tri、roud是可以通过它定义
tab_width dimension 指示器的宽度,如果不写,则根据控件自身大小
tab_height dimension 指示器高度
tab_item_res reference 指示器的背景,比如shape,bitmap等,只对 res 起作用
tab_round_size dimension 圆角的大小,只对round起作用
tab_margin_l dimension 左偏移
tab_margin_t dimension 上偏移
tab_margin_r dimension 右偏移
tab_margin_b dimension 下偏移
tab_click_animTime integer 点击动画的时间,默认300ms
tab_item_autoScale boolean 开启放大缩小的效果
tab_scale_factor float 放大倍数
tab_orientation integer vertical竖直防线,horizontal横向,默认横向
tab_action_orientaion integer left坐标,right右边,只支持 tri、rect 两种效果
tab_isAutoScroll boolean 是否支持自动滚动,默认为true

TabColorTextView

名称 类型 说明
colortext_default_color color 默认颜色
colortext_change_color color 需要渐变颜色

LabelFlowLayout

名称 类型 说明
label_maxcount integer 最大选择个数
label_iaAutoScroll boolean 是否支持自动滚动

Guess you like

Origin juejin.im/post/5e2f9dc7e51d4558836e3f99