實現一個可定製化的FlowLayout

FlowLayout 繼承於 ViewGroup ,能夠快速幫您實現 Tablayout 以及 Label 標籤,內含多種效果,幫您快速實現 APP UI 功能,讓您專一代碼架構,告別繁瑣UI。java

代碼工程:實現一個可定製化的FlowLayoutandroid

若是你也想本身寫一個,能夠參考如下幾篇文章git

實現一個可定製化的TabFlowLayout(一) -- 測量與佈局github

實現一個可定製化的TabFlowLayout(二) -- 實現滾動和平滑過渡canvas

實現一個可定製化的TabFlowLayout(三) -- 動態數據添加與經常使用接口封裝緩存

實現一個可定製化的TabFlowLayout(四) -- 與ViewPager 結合,實現炫酷效果bash

Recyclerview 實現雙聯表聯動網絡

一 關聯

allprojects {
    repositories {
       ...
        maven { url 'https://jitpack.io' }
        
    }
}
複製代碼
implementation 'com.github.LillteZheng:FlowHelper:v1.16'
複製代碼

2、效果

首先,就是 TabFlowLayout 的效果,它的佈局支持橫豎兩種方式,首先先看支持的效果:架構

結合ViewPager 沒有結合ViewPager
TabFlowLayout豎直,RecyclerView聯動效果

除了 TabFlowLayout,還有 LAbelFlowLayout 標籤式佈局,支持自動換行app

LAbelFlowLayout 標籤式佈局

能夠看到 目前 TabFlowLayout 支持如下效果:

  • 矩形
  • 三角形
  • 圓角
  • shape 或者 bitmap 等資源文件
  • 自定義功能
  • 放大Item效果,與上述效果可共用
  • 顏色漸變效果,須要使用 TabColorTextView 控件,與上述效果可共用,只支持有viewpager 的狀況
  • 豎直效果,須要設置 tab_orientation = vertical

3、使用

主要是 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,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.一、結合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.二、自定義屬性動態配置

可能你不想在 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.三、自定義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.四、參考代碼

上面的效果,能夠參考如下代碼:

有ViewPager的佈局代碼

有ViewPager的Activity

3.二、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代碼

4、Adpater 支持的方法

TabFlowLayout 和 LAbelFlowLayout 都是經過 setAdapter 的方式去加載數據的,除了支持 setText(..) ,setTextColor(..) ,setImageView 等,還支持 click 事件,具體的方法,能夠參考下面的代碼:

Adapter方法

5、自定義屬性列表

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 是否支持自動滾動
相關文章
相關標籤/搜索