1. 程式人生 > >Android TagFlowLayout完全解析 一款針對Tag的佈局(針對多個條目的單選操作)

Android TagFlowLayout完全解析 一款針對Tag的佈局(針對多個條目的單選操作)

目錄(?)[+]

一、概述

本文之前,先提一下關於上篇博文的100多萬訪問量請無視,博文被刷,我也很鬱悶,本來想把那個文章放到草稿箱,結果放不進去,還把日期弄更新了,實屬無奈。

因為本身FlowLayout本身的預期是提供一種新的佈局的方式,但是呢,在實際的開發中,大家更多的是使用在商品標籤,搜尋關鍵字的場景,那麼就涉及到一些互動:

  • 比如使用者選擇了某個標籤,首先你要去改變標籤的樣子給使用者一個反饋,其次你需要記錄使用者的選擇。
  • 那麼在選擇過程中還有多選的情況,比如4選2,4選3等等。
  • 還有…

類似京東的這個選擇商品的圖:

對於上述的情況呢,FlowLayout只能說能夠實現View的顯示沒有問題,而對於點選某個Tag,以及修改某個Tag的樣子,可能需要編寫大量的程式碼,且設計只要稍微的改下顯示的效果,估計就得加班了。

既然這麼多的不方便,那麼我們現在就在FlowLayout的基礎上,編寫TagFlowLayout去完善,目前支援:

  • 以setAdapter形式注入資料
  • 直接設定selector為background即可完成標籤選則的切換,類似CheckBox
  • 支援控制選擇的Tag數量,比如:單選、多選
  • 支援setOnTagClickListener,當點選某個Tag回撥
  • 支援setOnSelectListener,當選擇某個Tag後回撥
  • 支援adapter.notifyDataChanged
  • Activity重建(或者旋轉)後,選擇的狀態自動儲存

我們的效果圖:

我需要思考幾分鐘本文的敘述方式…

ok,由於本文並非從無到有的去構造一個新的東西,所以你肯定沒有辦法根據我的分析,然後就能完整的寫出來。這樣的話,就非常建議大家下載原始碼,拿著原始碼比對著看;或者看完本文後去下載原始碼;或者僅僅是看看思路學學知識點(eclipse的使用者,拷貝幾個類不是難事,不要私聊我問我怎麼整~)。

二、以setAdapter形式注入資料

首先我們完成的就是,去除大家痛苦的新增資料的方式。類似ListView,提供Adapter的方式,為我們的TagFlowLayout去新增資料,這種方式,大家用的肯定比較熟練了,而且也比較靈活。

(1) TagAdapter

那麼首先我們得有個Adapter,這裡叫做TagAdapter

<code class="language-java hljs  has-numbering"><span class="hljs-keyword">package</span> com.zhy.view.flowlayout;

<span class="hljs-keyword">import</span> android.view.View;

<span class="hljs-keyword">import</span> java.util.ArrayList;
<span class="hljs-keyword">import</span> java.util.Arrays;
<span class="hljs-keyword">import</span> java.util.List;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TagAdapter</span><<span class="hljs-title">T</span>>
{</span>
    <span class="hljs-keyword">private</span> List<T> mTagDatas;
    <span class="hljs-keyword">private</span> OnDataChangedListener mOnDataChangedListener;

    <span class="hljs-keyword">public</span> <span class="hljs-title">TagAdapter</span>(List<T> datas)
    {
        mTagDatas = datas;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-title">TagAdapter</span>(T[] datas)
    {
        mTagDatas = <span class="hljs-keyword">new</span> ArrayList<T>(Arrays.asList(datas));
    }

    <span class="hljs-keyword">static</span> interface OnDataChangedListener
    {
        <span class="hljs-keyword">void</span> onChanged();
    }

    <span class="hljs-keyword">void</span> setOnDataChangedListener(OnDataChangedListener listener)
    {
        mOnDataChangedListener = listener;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getCount</span>()
    {
        <span class="hljs-keyword">return</span> mTagDatas == <span class="hljs-keyword">null</span> ? <span class="hljs-number">0</span> : mTagDatas.size();
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">notifyDataChanged</span>()
    {
        mOnDataChangedListener.onChanged();
    }

    <span class="hljs-keyword">public</span> T <span class="hljs-title">getItem</span>(<span class="hljs-keyword">int</span> position)
    {
        <span class="hljs-keyword">return</span> mTagDatas.get(position);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> View <span class="hljs-title">getView</span>(FlowLayout parent, <span class="hljs-keyword">int</span> position, T t);

}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li></ul>

可以看到很簡單,這是一個抽象類,那麼具體的View的展示需要大家通過複寫getView,用法和ListView及其類似,同時我們提供了notifyDataChanged()的方法,當你的資料集發生變化的時候,你可以呼叫該方法,UI會自動重新整理。

當然,僅僅有了Adapter是不行的,我們需要新增相應的程式碼對其進行支援。

(2)TagFlowLayout對Adapter的支援

那麼最主要就是提供一個setAdapter的方法:

<code class="language-java hljs  has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setAdapter</span>(TagAdapter adapter)
{
   mTagAdapter = adapter;
   mTagAdapter.setOnDataChangedListener(<span class="hljs-keyword">this</span>);
   changeAdapter();

}

<span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">changeAdapter</span>()
{
   removeAllViews();
   TagAdapter adapter = mTagAdapter;
   TagView tagViewContainer = <span class="hljs-keyword">null</span>;

   <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < adapter.getCount(); i++)
   {
       View tagView = adapter.getView(<span class="hljs-keyword">this</span>, i, adapter.getItem(i));
       tagView.setDuplicateParentStateEnabled(<span class="hljs-keyword">true</span>);
       tagViewContainer.setLayoutParams(tagView.getLayoutParams());
       tagViewContainer.addView(tagView);
       addView(tagViewContainer);
   }

}
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onChanged</span>()
{
   changeAdapter();
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li></ul>

ok,可以看到當你呼叫setAdapter進來,首先我們會註冊mTagAdapter.setOnDataChangedListener這個回撥,主要是用於響應notifyDataSetChanged()。然後進入changeAdapter方法,在這裡首先移除所有的子View,然後根據mAdapter.getView的返回,開始逐個構造子View,然後進行新增。

這裡注意下:我們的上述的程式碼,對mAdapter.getView返回的View,外圍報了一層TagView,這裡暫時不要去想,我們後面會細說。

到此,我們的Adapter新增完畢。

三、支援onTagClickListener

ok,這個介面也非常重要,當然我私下了解了下,很多同學都加上了,但是基本都是對單個標籤View去setOnClickListener,然後去比對Tag確定點選的是哪個標籤,最後回調出來。當然,我們這裡考慮一種更優雅的方式:

我們從父控制元件下手,當我們確定使用者點選在我們的TagFlowLayout上時,我們根據使用者點選的座標,看看是否點選的是我們的某個View,然後進行click回撥。是不是有點像事件分發,哈,我們這裡可以稱為點選分發。

那麼,我們需要關注的就是onTouchEventperformClick方法。

<code class="language-java hljs  has-numbering"> <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onTouchEvent</span>(MotionEvent event)
    {
        <span class="hljs-keyword">if</span> (event.getAction() == MotionEvent.ACTION_UP)
        {
            mMotionEvent = MotionEvent.obtain(event);
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.onTouchEvent(event);
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">performClick</span>()
    {
        <span class="hljs-keyword">if</span> (mMotionEvent == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.performClick();

        <span class="hljs-keyword">int</span> x = (<span class="hljs-keyword">int</span>) mMotionEvent.getX();
        <span class="hljs-keyword">int</span> y = (<span class="hljs-keyword">int</span>) mMotionEvent.getY();
        mMotionEvent = <span class="hljs-keyword">null</span>;

        TagView child = findChild(x, y);
        <span class="hljs-keyword">int</span> pos = findPosByView(child);
        <span class="hljs-keyword">if</span> (child != <span class="hljs-keyword">null</span>)
        {
            doSelect(child, pos);
            <span class="hljs-keyword">if</span> (mOnTagClickListener != <span class="hljs-keyword">null</span>)
            {
                <span class="hljs-keyword">return</span> mOnTagClickListener.onTagClick(child.getTagView(), pos, <span class="hljs-keyword">this</span>);
            }
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.performClick();
    }

    <span class="hljs-keyword">private</span> TagView <span class="hljs-title">findChild</span>(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)
    {
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> cCount = getChildCount();
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < cCount; i++)
        {
            TagView v = (TagView) getChildAt(i);
            <span class="hljs-keyword">if</span> (v.getVisibility() == View.GONE) <span class="hljs-keyword">continue</span>;
            Rect outRect = <span class="hljs-keyword">new</span> Rect();
            v.getHitRect(outRect);
            <span class="hljs-keyword">if</span> (outRect.contains(x, y))
            {
                <span class="hljs-keyword">return</span> v;
            }
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li></ul>

可以看到我們這裡巧妙的利用了performClick這個回撥,來確定的確是觸發了click事件,而不是自己去判斷什麼算click的條件。但是呢,我們的performClick沒有提供MotionEvent的引數,不過不要緊,我們都清楚click的事件發生在ACTION_UP之後,所以我們提供一個變數去記錄最後一次觸發ACTION_UP的mMotionEvent。

我們在performClick裡面,根據mMotionEvent,去查詢是否落在某個子View身上,如果落在,那麼就確定點選在它身上了,直接回調即可,關於介面的定義如下,(ps:關於doSelect方法,我們後面說):

<code class="language-java hljs  has-numbering"> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">OnTagClickListener</span>
    {</span>
        <span class="hljs-keyword">boolean</span> onTagClick(View view, <span class="hljs-keyword">int</span> position, FlowLayout parent);
    }

    <span class="hljs-keyword">private</span> OnTagClickListener mOnTagClickListener;


    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setOnTagClickListener</span>(OnTagClickListener onTagClickListener)
    {
        mOnTagClickListener = onTagClickListener;
        <span class="hljs-keyword">if</span> (onTagClickListener != <span class="hljs-keyword">null</span>) setClickable(<span class="hljs-keyword">true</span>);
    }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li></ul>

可以看到,如果設定了setOnTagClickListener,我們顯示的設定了父ViewsetClickable(true)。以防萬一父View不具備消費事件的能力。

四、全面支援Checked

這一節呢,主要包含支援幾個功能:
* 直接設定selector為background即可完成標籤選則的切換,類似CheckBox
* 支援控制選擇的Tag數量,比如:單選、多選
* 支援setOnSelectListener,當選擇某個Tag後回撥

首先,我們提供了兩個自定義的屬性,multi_suppoutmax_select。一個是指出是否支援選擇(如果為false,意味著你只能通過setOnTagClickListener去做一些操作),一個是設定最大的選擇數量,-1為不限制數量。

ok,那麼核心的程式碼依然在performClick中被呼叫的:

<code class="language-java hljs  has-numbering">
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">performClick</span>()
{
     <span class="hljs-comment">//省略了一些程式碼...</span>
      doSelect(child, pos);
      <span class="hljs-keyword">if</span> (mOnTagClickListener != <span class="hljs-keyword">null</span>)
      {
          <span class="hljs-keyword">return</span> mOnTagClickListener.onTagClick(child.getTagView(), pos, <span class="hljs-keyword">this</span>);
      }
      <span class="hljs-comment">//省略了一些程式碼...</span>

}

<span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doSelect</span>(TagView child, <span class="hljs-keyword">int</span> position)
{
   <span class="hljs-keyword">if</span> (mSupportMulSelected)
   {
       <span class="hljs-keyword">if</span> (!child.isChecked())
       {
           <span class="hljs-keyword">if</span> (mSelectedMax > <span class="hljs-number">0</span> && mSelectedView.size() >= mSelectedMax)
               <span class="hljs-keyword">return</span>;
           child.setChecked(<span class="hljs-keyword">true</span>);
           mSelectedView.add(position);
       } <span class="hljs-keyword">else</span>
       {
           child.setChecked(<span class="hljs-keyword">false</span>);
           mSelectedView.remove(position);
       }
       <span class="hljs-keyword">if</span> (mOnSelectListener != <span class="hljs-keyword">null</span>)
       {
           mOnSelectListener.onSelected(<span class="hljs-keyword">new</span> HashSet<Integer>(mSelectedView));
       }
   }
}
</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li></ul>

ok,可以看到,如果點選了某個標籤,進入doSelect方法,首先判斷你是否開啟了多選的支援(預設支援),然後判斷當前的View是否是非Checked的狀態,如果是非Checked狀態,則判斷最大的選擇數量,如果沒有達到,則設定checked=true,同時加入已選擇的集合;反之已經是checked狀態,就是取消選擇狀態了。同時如果設定了mOnSelectListener,回撥一下。

ok,其實這裡隱藏了一些東西,關於介面回撥我們不多贅述了,大家都明白。這裡主要看Checked。首先你肯定有幾個問題:

  • childView哪來的isChecked(),setChecked()方法?
  • 這麼做就能改變UI了?

下面我一一解答:首先,我們並非知道adapter#getView返回的是什麼View,但是可以肯定的是,大部分View都是沒有isChecked(),setChecked()方法的。但是我們需要有,怎麼做?還記得我們setAdapter的時候,給getView外層包了一層TagView麼,沒錯,就是TagView起到的作用:

<code class="language-java hljs  has-numbering"><span class="hljs-keyword">package</span> com.zhy.view.flowlayout;

<span class="hljs-keyword">import</span> android.content.Context;
<span class="hljs-keyword">import</span> android.view.View;
<span class="hljs-keyword">import</span> android.widget.Checkable;
<span class="hljs-keyword">import</span> android.widget.FrameLayout;

<span class="hljs-javadoc">/**
 * Created by zhy on 15/9/10.
 */</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TagView</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">FrameLayout</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Checkable</span>
{</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> isChecked;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span>[] CHECK_STATE = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[]{android.R.attr.state_checked};

    <span class="hljs-keyword">public</span> <span class="hljs-title">TagView</span>(Context context)
    {
        <span class="hljs-keyword">super</span>(context);
    }

    <span class="hljs-keyword">public</span> View <span class="hljs-title">getTagView</span>()
    {
        <span class="hljs-keyword">return</span> getChildAt(<span class="hljs-number">0</span>);
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span>[] <span class="hljs-title">onCreateDrawableState</span>(<span class="hljs-keyword">int</span> extraSpace)
    {
        <span class="hljs-keyword">int</span>[] states = <span class="hljs-keyword">super</span>.onCreateDrawableState(extraSpace + <span class="hljs-number">1</span>);
        <span class="hljs-keyword">if</span> (isChecked())
        {
            mergeDrawableStates(states, CHECK_STATE);
        }
        <span class="hljs-keyword">return</span> states;
    }


    <span class="hljs-javadoc">/**
     * Change the checked state of the view
     *
     *<span class="hljs-javadoctag"> @param</span> checked The new checked state
     */</span>
    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setChecked</span>(<span class="hljs-keyword">boolean</span> checked)
    {
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.isChecked != checked)
        {
            <span class="hljs-keyword">this</span>.isChecked = checked;
            refreshDrawableState();
        }
    }

    <span class="hljs-javadoc">/**
     *<span class="hljs-javadoctag"> @return</span> The current checked state of the view
     */</span>
    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isChecked</span>()
    {
        <span class="hljs-keyword">return</span> isChecked;
    }

    <span class="hljs-javadoc">/**
     * Change the checked state of the view to the inverse of its current state
     */</span>
    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">toggle</span>()
    {
        setChecked(!isChecked);
    }
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li></ul>

我們的TagView實現了Checkable介面,所以提供了問題一的方法。

下面解釋問題二: 這麼做就能改變UI了?

我們繼續看TagView這個類,這個類中我們複寫了onCreateDrawableState,在裡面添加了CHECK_STATE的支援。當我們呼叫setChecked方法的時候,我們會呼叫refreshDrawableState()來更新我們的UI。

但是你可能又會問了,你這個是TagView支援了CHECKED狀態,關它的子View什麼事?我們的background可是設定在子View上的。

沒錯,這個問題問的相當好,你還記得我們在setAdapter,addView之前有一行非常核心的程式碼:#mAdapter.getView().setDuplicateParentStateEnabled(true);setDuplicateParentStateEnabled這個方法允許我們的CHECKED狀態向下傳遞。

到這,你應該明白了吧~~

所以我們對於UI的變化,只需要設定View的Backgroud為:

<code class="language-xml hljs  has-numbering"><span class="hljs-pi"><?xml version="1.0" encoding="utf-8"?></span>
<span class="hljs-tag"><<span class="hljs-title">selector</span> <span class="hljs-attribute">xmlns:android</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/android"</span>></span>
    <span class="hljs-tag"><<span class="hljs-title">item
</span>          <span class="hljs-attribute">android:drawable</span>=<span class="hljs-value">"@drawable/checked_bg"</span>
          <span class="hljs-attribute">android:state_checked</span>=<span class="hljs-value">"true"</span>></span><span class="hljs-tag"></<span class="hljs-title">item</span>></span>
    <span class="hljs-tag"><<span class="hljs-title">item</span> <span class="hljs-attribute">android:drawable</span>=<span class="hljs-value">"@drawable/normal_bg"</span>></span><span class="hljs-tag"></<span class="hljs-title">item</span>></span>
<span class="hljs-tag"></<span class="hljs-title">selector</span>></span></code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul>

這樣,如果你的設計稿發生變化,大部分情況下,你只需要改改xml檔案就可以了。

ok,到此我們的核心部分的剖析就結束了,接下來貼貼用法:

五、用法

用法其實很簡單,大家可以參考例子,我這裡大致貼一下:

(1)設定資料

<code class="language-java hljs  has-numbering">mFlowLayout.setAdapter(<span class="hljs-keyword">new</span> TagAdapter<String>(mVals)
   {
       <span class="hljs-annotation">@Override</span>
       <span class="hljs-keyword">public</span> View <span class="hljs-title">getView</span>(FlowLayout parent, <span class="hljs-keyword">int</span> position, String s)
       {
           TextView tv = (TextView) mInflater.inflate(R.layout.tv,
                   mFlowLayout, <span class="hljs-keyword">false</span>);
           tv.setText(s);
           <span class="hljs-keyword">return</span> tv;
       }
   });</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li></ul>

getView中回撥,類似ListView等用法。

(2)對於選中狀態

你還在複雜的寫程式碼設定選中後標籤的顯示效果麼,翔哥說No!

<code class="language-java hljs  has-numbering"><?xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"utf-8"</span>?>
<selector xmlns:android=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>>
    <item android:color=<span class="hljs-string">"@color/tag_select_textcolor"</span>
          android:drawable=<span class="hljs-string">"@drawable/checked_bg"</span>
          android:state_checked=<span class="hljs-string">"true"</span>></item>
    <item android:drawable=<span class="hljs-string">"@drawable/normal_bg"</span>></item>
</selector>
</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li></ul>

設定個background,上面一個狀態為android:state_checked,另一個為正常。寫寫佈局檔案我都嫌慢,怎麼能寫一堆程式碼控制效果,設定改個效果,豈不是沒時間dota了。

(3)事件

<code class="language-java hljs  has-numbering">mFlowLayout.setOnTagClickListener(<span class="hljs-keyword">new</span> TagFlowLayout.OnTagClickListener()
{
  <span class="hljs-annotation">@Override</span>
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onTagClick</span>(View view, <span class="hljs-keyword">int</span> position, FlowLayout parent)
  {
      Toast.makeText(getActivity(), mVals[position], Toast.LENGTH_SHORT).show();
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
  }
});</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>

點選標籤時的回撥。

<code class="language-java hljs  has-numbering">mFlowLayout.setOnSelectListener(<span class="hljs-keyword">new</span> TagFlowLayout.OnSelectListener()
{
  <span class="hljs-annotation">@Override</span>
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onSelected</span>(Set<Integer> selectPosSet)
  {
      getActivity().setTitle(<span class="hljs-string">"choose:"</span> + selectPosSet.toString());
  }
});</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li></ul>

選擇多個標籤時的回撥。

最後肯定有人會問,支援字型變色嗎?ScrollView會衝突嗎?
附上最新效果圖:

大家就自行檢視原始碼了。