Android TagFlowLayout完全解析 一款针对Tag的布局(针对多个条目的单选操作)
2016-07-20 16:31
666 查看
版权声明:本文为博主原创文章,未经博主允许不得转载。
目录(?)[+]
转载请标明出处:
http://blog.csdn.net/lmj623565791/article/details/48393217;
本文出自:【张鸿洋的博客】
ok,开始今天的博文,今天要说的是TagFlowLayout,说这个之前必须提一下FlowLayout,如果你不了解,可以先阅读之前的博文:Android 自定义ViewGroup 实战篇 -> 实现FlowLayout或者观看视频
打造Android中的流式布局和热门标签 。
因为本身FlowLayout本身的预期是提供一种新的布局的方式,但是呢,在实际的开发中,大家更多的是使用在商品标签,搜索关键字的场景,那么就涉及到一些交互:
比如用户选择了某个标签,首先你要去改变标签的样子给用户一个反馈,其次你需要记录用户的选择。
那么在选择过程中还有多选的情况,比如4选2,4选3等等。
还有…
类似京东的这个选择商品的图:
对于上述的情况呢,FlowLayout只能说能够实现View的显示没有问题,而对于点击某个Tag,以及修改某个Tag的样子,可能需要编写大量的代码,且设计只要稍微的改下显示的效果,估计就得加班了。
既然这么多的不方便,那么我们现在就在FlowLayout的基础上,编写TagFlowLayout去完善,目前支持:
以setAdapter形式注入数据
直接设置selector为background即可完成标签选则的切换,类似CheckBox
支持控制选择的Tag数量,比如:单选、多选
支持setOnTagClickListener,当点击某个Tag回调
支持setOnSelectListener,当选择某个Tag后回调
支持adapter.notifyDataChanged
Activity重建(或者旋转)后,选择的状态自动保存
我们的效果图:
github地址:FlowLayout
我需要思考几分钟本文的叙述方式…
ok,由于本文并非从无到有的去构造一个新的东西,所以你肯定没有办法根据我的分析,然后就能完整的写出来。这样的话,就非常建议大家下载源码,拿着源码比对着看;或者看完本文后去下载源码;或者仅仅是看看思路学学知识点(eclipse的用户,拷贝几个类不是难事,不要私聊我问我怎么整~)。
可以看到很简单,这是一个抽象类,那么具体的View的展示需要大家通过复写getView,用法和ListView及其类似,同时我们提供了notifyDataChanged()的方法,当你的数据集发生变化的时候,你可以调用该方法,UI会自动刷新。
当然,仅仅有了Adapter是不行的,我们需要添加相应的代码对其进行支持。
ok,可以看到当你调用setAdapter进来,首先我们会注册
这里注意下:我们的上述的代码,对mAdapter.getView返回的View,外围报了一层TagView,这里暂时不要去想,我们后面会细说。
到此,我们的Adapter添加完毕。
我们从父控件下手,当我们确定用户点击在我们的TagFlowLayout上时,我们根据用户点击的坐标,看看是否点击的是我们的某个View,然后进行click回调。是不是有点像事件分发,哈,我们这里可以称为点击分发。
那么,我们需要关注的就是
可以看到我们这里巧妙的利用了
我们在
可以看到,如果设置了
* 直接设置selector为background即可完成标签选则的切换,类似CheckBox
* 支持控制选择的Tag数量,比如:单选、多选
* 支持setOnSelectListener,当选择某个Tag后回调
首先,我们提供了两个自定义的属性,
ok,那么核心的代码依然在performClick中被调用的:
ok,可以看到,如果点击了某个标签,进入doSelect方法,首先判断你是否开启了多选的支持(默认支持),然后判断当前的View是否是
ok,其实这里隐藏了一些东西,关于接口回调我们不多赘述了,大家都明白。这里主要看Checked。首先你肯定有几个问题:
childView哪来的isChecked(),setChecked()方法?
这么做就能改变UI了?
下面我一一解答:首先,我们并非知道adapter#getView返回的是什么View,但是可以肯定的是,大部分View都是没有isChecked(),setChecked()方法的。但是我们需要有,怎么做?还记得我们setAdapter的时候,给getView外层包了一层TagView么,没错,就是TagView起到的作用:
我们的TagView实现了Checkable接口,所以提供了问题一的方法。
下面解释问题二: 这么做就能改变UI了?
我们继续看TagView这个类,这个类中我们复写了
但是你可能又会问了,你这个是TagView支持了CHECKED状态,关它的子View什么事?我们的background可是设置在子View上的。
没错,这个问题问的相当好,你还记得我们在setAdapter,addView之前有一行非常核心的代码:
到这,你应该明白了吧~~
所以我们对于UI的变化,只需要设置View的Backgroud为:
这样,如果你的设计稿发生变化,大部分情况下,你只需要改改xml文件就可以了。
ok,到此我们的核心部分的剖析就结束了,接下来贴贴用法:
getView中回调,类似ListView等用法。
设置个background,上面一个状态为android:state_checked,另一个为正常。写写布局文件我都嫌慢,怎么能写一堆代码控制效果,设置改个效果,岂不是没时间dota了。
点击标签时的回调。
选择多个标签时的回调。
最后肯定有人会问,支持字体变色吗?ScrollView会冲突吗?
附上最新效果图:
大家就自行查看源码了。
最后,源码下载地址:https://github.com/hongyangAndroid/FlowLayout
欢迎关注我的微博:
http://weibo.com/u/3165018720
目录(?)[+]
转载请标明出处:
http://blog.csdn.net/lmj623565791/article/details/48393217;
本文出自:【张鸿洋的博客】
一、概述
本文之前,先提一下关于上篇博文的100多万访问量请无视,博文被刷,我也很郁闷,本来想把那个文章放到草稿箱,结果放不进去,还把日期弄更新了,实属无奈。ok,开始今天的博文,今天要说的是TagFlowLayout,说这个之前必须提一下FlowLayout,如果你不了解,可以先阅读之前的博文:Android 自定义ViewGroup 实战篇 -> 实现FlowLayout或者观看视频
打造Android中的流式布局和热门标签 。
因为本身FlowLayout本身的预期是提供一种新的布局的方式,但是呢,在实际的开发中,大家更多的是使用在商品标签,搜索关键字的场景,那么就涉及到一些交互:
比如用户选择了某个标签,首先你要去改变标签的样子给用户一个反馈,其次你需要记录用户的选择。
那么在选择过程中还有多选的情况,比如4选2,4选3等等。
还有…
类似京东的这个选择商品的图:
对于上述的情况呢,FlowLayout只能说能够实现View的显示没有问题,而对于点击某个Tag,以及修改某个Tag的样子,可能需要编写大量的代码,且设计只要稍微的改下显示的效果,估计就得加班了。
既然这么多的不方便,那么我们现在就在FlowLayout的基础上,编写TagFlowLayout去完善,目前支持:
以setAdapter形式注入数据
直接设置selector为background即可完成标签选则的切换,类似CheckBox
支持控制选择的Tag数量,比如:单选、多选
支持setOnTagClickListener,当点击某个Tag回调
支持setOnSelectListener,当选择某个Tag后回调
支持adapter.notifyDataChanged
Activity重建(或者旋转)后,选择的状态自动保存
我们的效果图:
github地址:FlowLayout
我需要思考几分钟本文的叙述方式…
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回调。是不是有点像事件分发,哈,我们这里可以称为点击分发。
那么,我们需要关注的就是
onTouchEvent和
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">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,我们显示的设置了父View
setClickable(true)。以防万一父View不具备消费事件的能力。
四、全面支持Checked
这一节呢,主要包含支持几个功能:* 直接设置selector为background即可完成标签选则的切换,类似CheckBox
* 支持控制选择的Tag数量,比如:单选、多选
* 支持setOnSelectListener,当选择某个Tag后回调
首先,我们提供了两个自定义的属性,
multi_suppout和
max_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会冲突吗?
附上最新效果图:
大家就自行查看源码了。
最后,源码下载地址:https://github.com/hongyangAndroid/FlowLayout
欢迎关注我的微博:
http://weibo.com/u/3165018720
相关文章推荐
- 编译期注解框架浅析
- android存储问题
- Android 全局修改字体
- Android客户端性能优化(魅族资深工程师毫无保留奉献)(转)
- mac设置android的adb环境变量
- Android 开源框架Universal-Image-Loader完全解析(二)--- 图片缓存策略详解
- 创业干货分享:初创公司移动应用开发工具库(集合)
- windows下Android利用ant自动编译、修改配置文件、批量多渠道,打包生成apk文件
- Android项目开发错误锦集-DuplicateFileException
- 【java|Android接口回调】小小猿理解的接口回调
- Android 开源框架Universal-Image-Loader完全解析(一)--- 基本介绍及使用
- 浅析多线程同步
- 菜鸟都能理解的Android中View的事件分发机制及滑动冲突处理
- js可以关闭android页面上的键盘输入法
- 只用TextView实现知乎主页底部Tab
- Android中的屏幕适配
- Android Bitmap是不能比较的,这样做是错误的
- Android中获取控件的位置
- Android Service完全解析,关于服务你所需知道的一切(上)
- Android studio更新到2.2之后出现的问题