flowlayout布局怎么换行_实现一个可定制化的FlowLayout

49e08d4b371012d976d59010735fb18d.png

作者:夏至的稻穗 地址:https://juejin.im/post/5e2f9dc7e51d4558836e3f99

FlowLayout 继承于 ViewGroup ,可以快速帮您实现 Tablayout 以及 Label 标签,内含多种效果,帮您快速实现 APP UI 功能,让您专注代码架构,告别繁琐UI。

一 添加依赖

最外层build.gradle中添加如下代码:

allprojects {
repositories {
...
maven { url 'https://jitpack.io' }

}
}

appbuild.gradle中添加如下代码,最新版本请以工程为准:

implementation 'com.github.LillteZheng:FlowHelper:v1.17'

二、效果

首先,就是 TabFlowLayout 的效果,它的布局支持横竖两种方式,首先先看支持的效果:

没有结合ViewPager结合ViewPager
0fd2e66c4ca5e5f21709059a737e13c9.gife6c25b03867317507011ed8b0401bb2b.gif

TabFlowLayout竖直,RecyclerView联动效果:

6eccd465ac73b9c4cc9387b8b727ad6e.gif

除了 TabFlowLayout,还有 LAbelFlowLayout 标签式布局,支持自动换行与显示更多

LabelFlowLayoutLabelFlowLayout 显示更多
967de22158860bf346e37a8665f09b11.gif9193a134b3a85848405d34b7a4684151.gif

可以看到 目前 TabFlowLayout 支持以下效果:

  • 矩形
  • 三角形
  • 圆角
  • shape 或者 bitmap 等资源文件
  • 自定义功能
  • 放大Item效果,与上述效果可共用
  • 颜色渐变效果,需要使用 TabColorTextView 控件,与上述效果可共用,只支持有viewpager 的情况
  • 竖直效果,需要设置 tab_orientation = vertical

三、使用

主要是 TabFlowLayout 和 LabelFlowLayout 这两个控件

3.1 TabFlowLayout

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

XML:

<com.zhengsr.tablib.view.flow.TabFlowLayoutandroid: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.TabFlowLayoutandroid: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 则可以不填,因为要根据控件本身去适配大小。其他说明,可以参看下面的自定义属性说明。

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

private void rectFlow(){
TabFlowLayout flowLayout = findViewById(R.id.rectflow);
//设置数据,这里以 setAdapter 的形式
flowLayout.setAdapter(new TabFlowAdapter(R.layout.item_msg,mTitle) {@Overridepublic 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));
}
}@Overridepublic void bindView(View view, String data, int position) {/**
* 绑定数据,可以使用 setText(..) 等快捷方式,也可以视同 view.findViewById()
* 同时,当你的子控件需要点击事件时,可以通过 addChildrenClick() 注册事件,
* 然后重写 onItemChildClick(..) 即可拿到事件,否则就自己写。
* 自己的点击和长按不需要注册
*/
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);
}// 注册子控件的点击事件//addChildrenClick(view,R.id.item_text,position);//注册子控件的长按事件//addChildrenLongClick(view,R.id.item_text,position);
}
});
}

可以看到,只需要设置 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.TabColorTextViewandroid: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"/>

<TextViewandroid: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
* @param viewPager
* @param textId view 中 textview 的id,用于TextView的颜色变化
* @param selectedIndex 默认选中的item,初始值为0,也可以从第二页或者其他 位置
* @param unselectedColor 没有选中的颜色,如果为 TabColorTextView 不需要些这个
* @param selectedColor 选中的颜色,如果为 TabColorTextView 不需要些这个
*/
public void setViewPager(
ViewPager viewPager,int textId,int selectedIndex,int unselectedColor,int selectedColor) {}

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

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

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

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(R.layout.item_msg,mTitle) {@Overridepublic void bindView(View view, String data, int position) {
setText(view,R.id.item_text,data);
}@Overridepublic void onItemClick(View view, String data, int position) {super.onItemClick(view, data, position);
mViewPager.setCurrentItem(position);
}
});
}
3.1.3、自定义action

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


/**
* 绘制一个圆的指示器
* 除了继承 BaseAction,还需要些 TabRect
*/
class CircleAction extends BaseAction{
@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;
//先设置 TabRect 的范围,即一个 view 的左边,方便后面的移动
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.2、TabFlowLayout 竖直效果

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

    <com.zhengsr.tablib.view.flow.TabFlowLayoutandroid: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"
/>

效果如下:

869853c32438771be84a31b835c66c10.png

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

Recyclerview 实现双联表联动

3.3 LabelFlowLayout

LabelFlowLayout 竖向布局,支持自动换行,单选、多选、长按等功能. 它的状态变化,根据 view 的 selected 来,所以大家可以写 selector 当背景,或者在方法中自己设置

3.3.1 使用

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

<com.zhengsr.tablib.view.flow.LabelFlowLayoutandroid: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(R.layout.item_textview,mTitle){/**
* 绑定数据,可以使用 setText(..) 等快捷方式,也可以视同 view.findViewById()
* 同时,当你的子控件需要点击事件时,可以通过 addChildrenClick() 注册事件,
* 然后重写 onItemChildClick(..) 即可拿到事件,否则就自己写。
* 自己的点击和长按不需要注册
*/@Overridepublic void bindView(View view, String data, int position) {
setText(view,R.id.item_text,data);// 注册子控件的点击事件//addChildrenClick(view,R.id.item_text,position);
}@Overridepublic 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);
}
}
});
3.3.2 多选

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

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

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

3.3.3 长按

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

    private void canLongFlow(){
LabelFlowLayout flowLayout = findViewById(R.id.longflow);
flowLayout.setAdapter(new LabelFlowAdapter(R.layout.item_search_layout,mTitle2) {@Overridepublic void bindView(View view, String data, int position) {
setText(view,R.id.search_msg_tv,data)
.addChildrenClick(view,R.id.search_delete_iv,position);
}@Overridepublic 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);
}
}@Overridepublic void onItemClick(View view, String data, int position) {super.onItemClick(view, data, position);
Toast.makeText(LabelActivity.this, "点击了: "+data, Toast.LENGTH_SHORT).show();
}@Overridepublic void onItemChildClick(View childView, int position) {super.onItemChildClick(childView, position);if (childView.getId() == R.id.search_delete_iv){
mTitle2.remove(position);
notifyDataChanged();
}
}@Overridepublic 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.5 显示更多

LabelFlowLayout 还支持显示更多的功能,如图:

c7cb2be9d53ff80795483fde0f941d6a.png

配置的 xml 如下:

<com.zhengsr.tablib.view.flow.LabelFlowLayoutandroid:id="@+id/labelflow"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:layout_marginStart="10dp"app:label_showLine="2"app:label_showMore_layoutId="@layout/show_more"app:label_showMore_Color="@color/white"/>
  • label_showLine 表示最多显示的行数
  • label_showMore_layoutId 表示显示更多的layoutId,这样方便客制化
  • label_showMore_Color 表示主背景色,用来设置 shader 虚化

上面的 label_showMore_Color 可能不太理解,其实就是把 显示更多的 LayoutId 转成bitmap,显示在下面;虚化怎么办呢?

其实就是给 paint 设置一个 shader,上面为透明色,下面给背景色一样,就能达到虚化的效果。如:

 /**
* 同时加上一个 shader,让它有模糊效果
*/
Shader shader = new LinearGradient(0, 0, 0,
getHeight(), Color.TRANSPARENT, mShowMoreColor, Shader.TileMode.CLAMP);
mPaint.setShader(shader);
3.3.6 配置自定义属性

当然,也支持动态配置自定义属性。如下:

LabelBean bean = new LabelBean();
bean.showLines = 2;
bean.showMoreLayoutId = R.layout.show_more;
bean.showMoreColor = Color.WHITE;
flowLayout.setLabelBean(bean);

四、Adpater 支持的方法

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

五、自定义属性列表

TabFlowLayout

cac8f7d247b8db911b0afee73c355adc.png

0673485cae7a36c42f462f20ec17a08d.png

23d51fb5971f53cc953d89ba1318d5a3.png

86fb4747fe918392e7169c267f45fb1e.png

Github地址:https://github.com/LillteZheng/FlowHelper

---END---

推荐阅读:
重磅!开工首日,新潮传媒宣布裁员500人,所有高管降薪20%
Android 绘制中国地图
远程上班第一天,钉钉、企业微信集体“崩溃”
Android 10 适配指南,有这几点需要注意!
d18efcb475de129b1047fbd4842265b6.png 每一个“在看”,我都当成真的喜欢60c92bab2bdf14d89601cea6e0de6ce6.gif
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FlowLayoutPanel 一些应用程序需要一个布局可随窗体大小的调整或其中内容大小的改变而自动进行适当排列的窗体。在需要动态布局并且不希望在代码中显式处理 Layout 事件,可考虑使用布局面板。 FlowLayoutPanel是.NET Framework的新增控件。顾名思义,面板可以采用Web窗体的方式给Windows窗体布局FlowLayoutPanel一个容器,允许以垂直或水平的方式放置包含的控件。除了放置控件之外,还可以剪辑控件。放置的方向使用FlowDirection属性和FlowDirection枚举来设置。WrapContents属性确定在重新设置窗体的大小控件是放在下一行、下一列,还是剪辑控件FlowLayoutPanel 按特定的流方向排列其内容:水平或垂直。其内容可从一行换到下一行,或者从一列换到下一列。另一种情况是不换行,而是将其内容截掉。 相信大家在做WinForm项目的候,要对大量的控件进行排序(位置摆放),这个容器肯定最受欢迎,但很遗憾的是,此容器本身虽支持Dock和Anchor属性,但不支持放入此容器内的控件的Dock和Anchor属性(自动调整宽度),也就说,但窗体伸缩,FlowLayoutPanel容器自身可以缩放,但是里面的控件就没那么幸运了,不支持自动缩放,这样就必须写方法来触发新的事件来调整控件的大小,这样就会导致窗体的闪烁(重绘)。 借助ManagedSpy工具,我们可以看到此容器里面的器件的结构,我们可以在Form1里面添加一个事件SizeChanged 对容器里面每个器件重新给它大小 就行了。 附件:FlowLayoutPanel的Demo
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值