FlowLayout 繼承於 ViewGroup ,可以快速幫您實現 Tablayout 以及 Label 標籤,內含多種效果,幫您快速實現 APP UI 功能,讓您專注代碼架構,告別繁瑣UI。
如果你也想自己寫一個,可以參考以下幾篇文章
實現一個可定製化的TabFlowLayout(一) – 測量與佈局
實現一個可定製化的TabFlowLayout(二) – 實現滾動和平滑過渡
實現一個可定製化的TabFlowLayout(三) – 動態數據添加與常用接口封裝
實現一個可定製化的TabFlowLayout(四) – 與ViewPager 結合,實現炫酷效果
FlowLayout 和 Recyclerview 實現雙聯表聯動
如果您也想快速實現banner,可以使用這個庫 https://github.com/LillteZheng/ViewPagerHelper
一 關聯
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
最新版本請以工程爲準:代碼工程:實現一個可定製化的FlowLayout
implementation 'com.github.LillteZheng:FlowHelper:v1.17'
二、效果
首先,就是 TabFlowLayout 的效果,它的佈局支持橫豎兩種方式,首先先看支持的效果:
沒有結合ViewPager | 結合ViewPager |
---|---|
TabFlowLayout豎直,RecyclerView聯動效果 |
---|
除了 TabFlowLayout,還有 LAbelFlowLayout 標籤式佈局,支持自動換行與顯示更多
LabelFlowLayout | LabelFlowLayout 顯示更多 |
---|---|
可以看到
目前 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.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.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.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
* @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<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 即可,上面效果圖中的 圓點,其實是繼承 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.1.4、參考代碼
上面的效果,可以參考以下代碼:
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 的聯動效果,可以參考在這個:
3.3 LabelFlowLayout
LabelFlowLayout 豎向佈局,支持自動換行,單選、多選、長按等功能.
它的狀態變化,根據 view 的 selected 來,所以大家可以寫 selector 當背景,或者在方法中自己設置
3.3.1 使用
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){
/**
* 綁定數據,可以使用 setText(..) 等快捷方式,也可以視同 view.findViewById()
* 同時,當你的子控件需要點擊事件時,可以通過 addChildrenClick() 註冊事件,
* 然後重寫 onItemChildClick(..) 即可拿到事件,否則就自己寫。
* 自己的點擊和長按不需要註冊
*/
@Override
public void bindView(View view, String data, int position) {
setText(view,R.id.item_text,data);
// 註冊子控件的點擊事件
//addChildrenClick(view,R.id.item_text,position);
}
@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);
}
}
});
3.3.2 多選
其實只需要配置 flowLayout.setMaxSelectCount(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);
3.3.3 長按
其實就是長按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.5 顯示更多
LabelFlowLayout 還支持顯示更多的功能,如圖:
配置的 xml 如下:
<com.zhengsr.tablib.view.flow.LabelFlowLayout
android: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);
3.3.7參考代碼:
四、Adpater 支持的方法
TabFlowLayout 和 LAbelFlowLayout 都是通過 setAdapter 的方式去加載數據的,除了支持 setText(…) ,setTextColor(…) ,setImageView 等,還支持 click 事件,具體的方法,可以參考下面的代碼:
五、自定義屬性列表
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 | 是否支持自動滾動 |
label_showLine | integer | 最多顯示的行數 |
label_showMore_layoutId | integer | 顯示更多的layoutId |
label_showMore_Color | color | 顯示更多的背景色,爲了虛化作用 |