1. 程式人生 > >使用Adapter設計模式打造一個流式佈局FlowLayout

使用Adapter設計模式打造一個流式佈局FlowLayout

流式佈局可以說是在各種軟體中的出場率都很高的一個佈局方式,被廣泛使用,像一些關鍵字搜尋,標籤等等的場景,更是隨處可見,今天我們就來手把手打造一個FlowLayout。
FlowLayout由於是以一個容器的身份存在的,所以其需要繼承的是ViewGroup而不是View,也就是說,我們今天所要做的,就是去自定義一個ViewGroup。
自定義ViewGroup和自定義View的套路基本都是一致的,但也有部分差異,下面,我們先來說說自定義ViewGroup的套路的幾個關鍵點:

  1. 自定義屬性(這個就不說了,在之前的文章中已經說了很多次了)

  2. 實現onMeasure()方法,通過測量每一個子View的寬高,來計算自身的寬高,最後達到測量自身寬高的目的。

  3. 實現onLayout()方法,該方法用於按照自己的想法來擺放可見(不為GONE)的子View。

  4. 實現onDraw()方法,默然情況下繼承ViewGroup是不會呼叫該方法的,如果需要繪製一些介面,可以實現dispatchDraw()方法

  5. 注意點:在確定要自定義ViewGroup的時候,可以先考慮下先繼承自一些已經封裝好的ViewGroup,如LinearLayout等進行開發。

好了,下面,我們開始自定義FlowLayout
首先,自定義屬性這一塊,我們就直接忽略了,直接從onMeasure方法開始做起(判斷view.visibility != GONE,之前忘加了):

/**
     * 測量該控制元件的寬高
     * 思路:通過測量每一個子view的寬高   來得到該layout的整體寬高
     * @param
widthMeasureSpec * @param heightMeasureSpec */
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); /** * 解決onMeasure被呼叫多次,導致在擺放子View的時候出現多次擺放的情況 */ mChildViews.clear(); //獲取子View的數量
int childCount = getChildCount(); //獲取該layout的寬度 以方便控制後面TAG的擺放 和計算該Layout的高度 int width = MeasureSpec.getSize(widthMeasureSpec); //初始化layout的高度 int height = getPaddingTop() + getPaddingBottom(); int lineWidth = getPaddingLeft(); //初始化一個每一行的高度 計算layout的總高度時,取每一行的最高 int maxHeight = 0; ArrayList<View> views = new ArrayList<>(); mChildViews.add(views); //for迴圈測量子View for(int i = 0;i < childCount; i++){ View childView = getChildAt(i); measureChild(childView,widthMeasureSpec,heightMeasureSpec); ViewGroup.MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams(); //當疊加的寬度大於該Layout的總寬度時,則換行 if((lineWidth + childView.getMeasuredWidth() + params.leftMargin + params.rightMargin) > width){ maxHeight = Math.max(maxHeight,childView.getMeasuredHeight() + params.topMargin + params.bottomMargin); height += maxHeight; lineWidth = getPaddingLeft() + childView.getMeasuredWidth() + params.leftMargin + params.rightMargin; views = new ArrayList<>(); mChildViews.add(views); views.add(childView); }else{ maxHeight = Math.max(maxHeight,childView.getMeasuredHeight() + params.topMargin + params.bottomMargin); lineWidth += childView.getMeasuredWidth() + params.leftMargin + params.rightMargin; views.add(childView); } } height += maxHeight; setMeasuredDimension(width,height); }

在計算寬度時,我們需要獲取到每個子View的margin值,但是由於ViewGroup本身是沒有LayoutParams的,所以這裡模仿了下LinearLayout的方法,來獲取MarginLayoutParams:

/**
     * 重寫ViewGroup的該方法   獲取到margin值(可以模仿LinearLayout)
     * @param attrs
     * @returnop
     */
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(),attrs);
    }

在onMeasure方法中,當幾個子View的寬度總和大於FlowLayout的寬度時,這個時候就需要換行。其他的,註釋應該都有了!

下一步,就是實現onLayout()方法,來擺放所有的子View(判斷view.visibility != GONE,之前忘加了)

“`
/**
* 擺放子view
* @param changed
* @param l
* @param t
* @param r
* @param b
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int maxHeight,left,right,top,bottom;
top = getPaddingTop();

    //迴圈遍歷mChildViews
    for(ArrayList<View> views : mChildViews){
        left = getPaddingLeft();
        maxHeight = 0;

        for(int i = 0;i<views.size();i++){
            View childView = views.get(i);
            ViewGroup.MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
            maxHeight = Math.max(maxHeight,childView.getMeasuredHeight() + marginLayoutParams.topMargin + marginLayoutParams.bottomMargin);
            left += marginLayoutParams.leftMargin;
            right = left + childView.getMeasuredWidth();
            int childTop = top + marginLayoutParams.topMargin;
            bottom = childTop + childView.getMeasuredHeight();

            childView.layout(left,childTop,right,bottom);
            left += marginLayoutParams.rightMargin + childView.getMeasuredWidth();
        }

        top += maxHeight;
    }
}

到這裡呢,基本是這個流式佈局就完成了一大半了,下面要做的,就是往這個佈局裡面扔資料,測試其是否會按照我們既定的規則擺放。

在設定資料這一步,我們使用了Adapter的設計模式。下面來看具體的步驟:
1:定義一個抽象的BaseAdapter

/**
 * Created by DELL on 2017/9/9.
 * Description :
 */

public abstract class BaseAdapter {

    //獲取view的數量
    public abstract int getCount();

    //獲取View
    public abstract View getView(int position, ViewGroup parent);

}

這邊暫時只定義了兩個方法,一個是獲取子View數量的,另外一個是獲取到每一個子View的,暫時沒有定義notifySetDataChanged等方法,這個可以根據自己的需求,給其設定一個觀察者來處理。

下一步,需要在FloawLayout中去設定Adapter達到添加布局的效果

public void setAdapter(BaseAdapter adapter){
        //當Adapter為空的時候,丟擲異常
        if(adapter == null){
            throw new NullPointerException("adapter not null");
        }

        this.mAdapter = adapter;

        int count = mAdapter.getCount();
        for(int i = 0;i<count;i++){
            View view = mAdapter.getView(i,this);
            addView(view);
        }
    }

每一個子View的佈局:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/item_textview"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:background="@drawable/bg_textview"
          android:padding="10dp"
          android:layout_margin="5dp"
    >
</TextView>

子view背景程式碼:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <stroke android:color="@color/colorAccent" android:width="1dp"/>
    <corners android:radius="8dp"/>

</shape>

下面,在Activity中設定Adapter,來測試下效果:

public class MainActivity extends AppCompatActivity {

    private FlowLayout mFlowLayout;
    private ArrayList<String> mStrList;
    private LayoutInflater mLayoutInflater;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        initView();
    }

    private void initData(){
        mStrList = new ArrayList<>();
        mStrList.add("JUSTH HELLO!");
        mStrList.add("JUSTH HEJUSTH HELLO!LLO!");
        mStrList.add("JUSTH LLO!");
        mStrList.add("JUSTH HEJUSTH HELLO!LLO!");
        mStrList.add("JUSTH HELLO!");
        mStrList.add("JUSTLO!");
        mStrList.add("JUSTH HELLO!");
        mStrList.add("JUSTH HEJUSTH HELLO!LLO!");
        mStrList.add("JUSTH HELO!");
        mStrList.add("JUSTH HELLO!");
    }

    private void initView() {
        mFlowLayout = (FlowLayout) findViewById(R.id.taglayout);

        mLayoutInflater = LayoutInflater.from(this);

        mFlowLayout.setAdapter(new BaseAdapter() {
            @Override
            public int getCount() {
                return mStrList.size();
            }

            @Override
            public View getView(int position, ViewGroup parent) {
                View view = mLayoutInflater.inflate(R.layout.layout_textview,parent,false);
                ((TextView)view.findViewById(R.id.item_textview)).setText(mStrList.get(position));
                return view;
            }
        });
    }
}

最後,來一波效果圖吧:

這裡寫圖片描述

相關推薦

使用Adapter設計模式打造一個佈局FlowLayout

流式佈局可以說是在各種軟體中的出場率都很高的一個佈局方式,被廣泛使用,像一些關鍵字搜尋,標籤等等的場景,更是隨處可見,今天我們就來手把手打造一個FlowLayout。 FlowLayout由於是以一個容器的身份存在的,所以其需要繼承的是ViewGroup而不是

100行Android程式碼自定義一個佈局FlowLayout

首先來看一下 手淘HD - 商品詳情 - 選擇商品屬性 頁面的UI 商品有很多尺碼,而且展現每個尺碼所需要的View的大小也不同(主要是寬度),所以在從伺服器端拉到資料之前,展現所有尺碼所需要的行數和每一行的個數都無法確定,因此不能直接使用GridView

解決:Android中常見的熱門標籤的佈局flowlayout不能wrap_content

最近在專案中藥使用流式佈局,但是在網上找的都不能滿足要求,這篇部落格內容只支援match_parent,我改後的程式碼可以支援wrap_content,原文也僅僅是少加一行高度而已。。新部落格希望大家多多評論。。原文連結 一:概述: 1.流式佈局的特點以

Android中的封裝佈局FlowLayout

鴻洋的GitHub:https://github.com/hongyangAndroid/FlowLayout 第一步:加依賴 implementation 'com.hyman:flowlayout-lib:1.1.2' 第二步:建立一個Adapter繼承TagAdapter pu

Android 佈局FlowLayout 實現關鍵字標籤

1.介紹 流式佈局的應用還是很廣泛的,比如搜尋熱詞、關鍵詞標籤等,GitHub上已經有很多這樣的佈局了,但是還是想著自己實現一下,最近一直在學自定義控制元件,也鞏固一下所學的知識。 本文實現的效果如下圖所示: 2.思路 繼承自RelativeL

Android開發佈局FlowLayout

先說說我的需求吧,我需要做商品規格選擇、類似淘寶京東選擇產品加入購物車。沒辦法只有選擇流式佈局了。最後在改造張鴻洋前輩的Flowlayout實現了該功能。我說說怎麼快速使用流式佈局吧,自定義控制元件說明看這張鴻洋的部落格:http://blog.csdn.net/lmj62

Android佈局FlowLayout

現在商城類的APP幾乎都要用到流式佈局來實現選擇屬性功能,在我的demo中是通過FlowLayout工具類實現流式佈局 使用起來非常簡單,十幾行程式碼就可以實現; 在我們的專案中大部分都是單選效果,為了防止用到多選,demo中也實現了多選; FlowLayout大家不用研

自定義控制元件之-佈局FlowLayout

前言 其實對於流式佈局控制元件,很多人並不陌生,專案中或多或少都會用到的.但是有多少人會寫一個流式佈局的控制元件這就不知道了,所以博主這裡對流式佈局進行一個講解,並且封裝一個比較完善的控制元件 效果圖 看到的這個整個就是一個流式佈局,裡面是

佈局 FlowLayout 的簡單使用

先給大家看效果圖:歷史記錄本地儲存到share裡面,在onStart裡面獲取呼叫資料,點選搜尋的時候新增進share。匯入依賴:compile 'com.nex3z:flow-layout:1.2.2'https://github.com/nex3z/FlowLayoutUs

Android 實現一個簡易橫向佈局

SimpleFlowLayout:一個簡易的橫向流式佈局,只實現核心功能,使用者可自行擴充套件   Demo圖片如下所示: SimpleFlowLayout直接繼承自ViewGroup,主要負責

二十九、Java圖形化介面設計——佈局管理器之FlowLayout佈局

         前文講解了JFrame、JPanel,其中已經涉及到了空佈局的使用。Java雖然可以以畫素為單位對元件進行精確的定位,但是其在不同的系統中將會有一定的顯示差異,使得顯示效果不盡相同,為此java提供了佈局管理器,以使編寫的圖形介面具有良好的平臺無關性。

跟我學JAVA(5)圖形化介面設計——佈局管理器之FlowLayout佈局

一、佈局管理器所屬類包 所屬類包 佈局管理器名稱 說明 Java.awt FlowLayout(流式佈局) 元件按照加入的先後順序按照設定的對齊方式從左向右排列,一

一個非常好用的Android佈局

效果圖鎮樓 首先我們先新增依賴 compile 'com.zhy:flowlayout-lib:1.0.3' 然後將以下標籤和佈局新增到專案中。 主佈局layouy->activity_flow_layout.xml <?xml v

自定義View,佈局

  寫的比較基礎, 備忘使用。 public class FlowLayout extends ViewGroup { public FlowLayout(Context context) { this(context, null); }

佈局簡單實現

//主方法 //建立集合 private List<String> mStrings=new ArrayList<>(); final EditText ed_search=(EditText)findViewById(R.id.ed_search

設計模式 _第二招_工廠方法模式

一、定義 工廠方法模式使用的頻率非常高,在我們日常的開發中總能看到它的身影。其定義為:定義一個用於建立物件的介面,讓子類決定例項化哪一個類。工程方法使用一個類的例項化延遲到其子類。 二、程式碼演示 在工廠方法模式中, 抽象產品類Product負責定義產品的共性,實現對事物最抽象的定

設計模式 _第一招_單例模式

一、定義 單例模式(Singleton Pattern)是一個比較簡單的模式,確保某一個類只有一個例項,而且自行例項化並向整個系統提供這個例項。 二、程式碼演示 Singleton 類稱為單例類,通過使用private的建構函式確保在一個應用中只產生一個例項,並且自行例項化,單例

佈局 佈局xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app=

笨鳥兒 靜態佈局、自適應佈局佈局、響應佈局、彈性佈局等的概念和區別

一、靜態佈局(Static Layout) 即傳統Web設計,網頁上的所有元素的尺寸一律使用px作為單位。 1、佈局特點:不管瀏覽器尺寸具體是多少,網頁佈局始終按照最初寫程式碼時的佈局來顯示。常規的pc的網站都是靜態(定寬度)佈局的,也就是設定了min-width,這樣的話,如果小於這個寬度就會

Flex佈局實戰(二):網格 \ 聖盃 \ 輸入框 \ 懸掛 \ 固定底欄 \ 佈局

參考:http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html 下面程式碼可能會比較多,但核心CSS程式碼已經用 /**/ 的標記標出,直接看核心程式碼就好。 一、網格佈局 1、基本網格佈局 最簡單