动手写一个好看的城市导航列表

2024-06-03 12:48

本文主要是介绍动手写一个好看的城市导航列表,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发。

最近一个月实在是太忙了,博客也快一个月没更新了。。。刚好最近公司项目需要一个城市导航的列表,自己捣鼓两天之后实现的效果图如下:

这里写图片描述

左侧的列表根据拼音自动排序,支持头部悬停,点击Item会提示选择的城市;右侧是一个快速导航栏,点击字母会提示选择的字母,左侧列表会滑动到对应位置,支持导航栏快速滑动。

OK,整体效果就是这样,真机测试也挺流畅,一起看看怎么实现这个炫酷的城市导航列表。

1.数据准备

1.构建城市实体类
假如服务器返回的是一堆杂乱无章的城市数据,我们需要对这些数据根据拼音的先后顺序进行排序。对应的实体类如下:

<code class="hljs java has-numbering"><span class="hljs-javadoc">/*** Created by tangyangkai on 16/7/26.*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">City</span> {</span><span class="hljs-keyword">private</span> String cityPinyin;<span class="hljs-keyword">private</span> String cityName;<span class="hljs-keyword">private</span> String firstPinYin;<span class="hljs-keyword">public</span> String <span class="hljs-title">getCityPinyin</span>() {<span class="hljs-keyword">return</span> cityPinyin;}<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setCityPinyin</span>(String cityPinyin) {<span class="hljs-keyword">this</span>.cityPinyin = cityPinyin;}<span class="hljs-keyword">public</span> String <span class="hljs-title">getCityName</span>() {<span class="hljs-keyword">return</span> cityName;}<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setCityName</span>(String cityName) {<span class="hljs-keyword">this</span>.cityName = cityName;}<span class="hljs-keyword">public</span> String <span class="hljs-title">getFirstPinYin</span>() {firstPinYin = cityPinyin.substring(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>);<span class="hljs-keyword">return</span> firstPinYin;}
}</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></ul>

cityPinyin代表城市名称的拼音,cityName代表城市名称,firstPinYin则代表城市拼音的第一个字母,也就是索引。

2.将汉字转换为拼音
这里我用的是TinyPinyin,一个适用于Java和Android的快速、低内存占用的汉字转拼音库。TinyPinyin的特点有:生成的拼音不包含声调,也不处理多音字,默认一个汉字对应一个拼音;拼音均为大写;无需初始化,执行效率很高(Pinyin4J的4倍);很低的内存占用(小于30KB)。使用起来也很简单:

<code class="hljs cs has-numbering">    <span class="hljs-keyword">public</span> String <span class="hljs-title">transformPinYin</span>(String character) {StringBuffer buffer = <span class="hljs-keyword">new</span> StringBuffer();<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < character.length(); i++) {buffer.append(Pinyin.toPinyin(character.charAt(i)));}<span class="hljs-keyword">return</span> buffer.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></ul>

比如传入一个汉字“安庆”,返回的结果就是“ANQING”

3.根据拼音进行排序
这里用的是java中的compareto方法,返回参与比较的前后两个字符串的asc码的差值,举个栗子:
若a=”b”,b=”a”,输出1;
若a=”abcdef”,b=”a”输出5;
若a=”abcdef”,b=”ace”输出-1;
即参与比较的两个字符串如果首字符相同,则比较下一个字符,直到有不同的为止,返回该不同的字符的asc码差值。

<code class="hljs axapta has-numbering">    <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PinyinComparator</span> <span class="hljs-inheritance"><span class="hljs-keyword">implements</span></span> <span class="hljs-title">Comparator</span><<span class="hljs-title">City</span>> {</span>@Override<span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> compare(City cityFirst, City citySecond) {<span class="hljs-keyword">return</span> cityFirst.getCityPinyin().compareTo(citySecond.getCityPinyin());}}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li></ul>

使用的时候实现Comparator接口,传入需要比较的实体类,然后将返回值作为 Collections.sort(cityList, pinyinComparator)中的第二个参数,Collections.sort方法会根据这个传入的int值对cityList进行排序。

2.自定义快速导航栏

1.重写onDraw()方法
右侧快速导航栏是一个自定义View,这里重点说一下onDraw()方法。

<code class="hljs avrasm has-numbering">@Overrideprotected void onDraw(Canvas canvas) {super<span class="hljs-preprocessor">.onDraw</span>(canvas)<span class="hljs-comment">;</span>paint<span class="hljs-preprocessor">.setColor</span>(backgroundColor)<span class="hljs-comment">;</span>canvas<span class="hljs-preprocessor">.drawRect</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, (float) mWidth, mHeight, paint)<span class="hljs-comment">;</span>for (int i = <span class="hljs-number">0</span><span class="hljs-comment">; i < CityActivity.pinyinList.size(); i++) {</span>String textView = CityActivity<span class="hljs-preprocessor">.pinyinList</span><span class="hljs-preprocessor">.get</span>(i)<span class="hljs-comment">;</span>if (i == position - <span class="hljs-number">1</span>) {paint<span class="hljs-preprocessor">.setColor</span>(getResources()<span class="hljs-preprocessor">.getColor</span>(R<span class="hljs-preprocessor">.color</span><span class="hljs-preprocessor">.error</span>_color))<span class="hljs-comment">;</span>selectTxt = CityActivity<span class="hljs-preprocessor">.pinyinList</span><span class="hljs-preprocessor">.get</span>(i)<span class="hljs-comment">;</span>listener<span class="hljs-preprocessor">.showTextView</span>(selectTxt, false)<span class="hljs-comment">;</span>} else {paint<span class="hljs-preprocessor">.setColor</span>(getResources()<span class="hljs-preprocessor">.getColor</span>(R<span class="hljs-preprocessor">.color</span><span class="hljs-preprocessor">.white</span>))<span class="hljs-comment">;</span>}paint<span class="hljs-preprocessor">.setTextSize</span>(<span class="hljs-number">40</span>)<span class="hljs-comment">;</span>paint<span class="hljs-preprocessor">.getTextBounds</span>(textView, <span class="hljs-number">0</span>, textView<span class="hljs-preprocessor">.length</span>(), mBound)<span class="hljs-comment">;</span>canvas<span class="hljs-preprocessor">.drawText</span>(textView, (mWidth - mBound<span class="hljs-preprocessor">.width</span>()) * <span class="hljs-number">1</span> / <span class="hljs-number">2</span>, mTextHeight - mBound<span class="hljs-preprocessor">.height</span>(), paint)<span class="hljs-comment">;</span>mTextHeight += mHeight / CityActivity<span class="hljs-preprocessor">.pinyinList</span><span class="hljs-preprocessor">.size</span>()<span class="hljs-comment">;</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></ul>

这里的pinyinList是去除重复的,按照A-Z顺序排列的字母索引集合。遍历这个集合,依次绘制出这些字母。在 i 等于 position -1(点击触摸的位置)的时候,将字体颜色设置为红色,否则字体颜色为白色。这一点在演示动态图中有所体现,触摸点击的字体颜色会改变。

2.重写onTouchEvent()方法

<code class="hljs avrasm has-numbering">    @Overridepublic boolean onTouchEvent(MotionEvent event) {int action = event<span class="hljs-preprocessor">.getAction</span>()<span class="hljs-comment">;</span>int <span class="hljs-built_in">y</span> = (int) event<span class="hljs-preprocessor">.getY</span>()<span class="hljs-comment">;</span>switch (action) {case MotionEvent<span class="hljs-preprocessor">.ACTION</span>_DOWN:backgroundColor = getResources()<span class="hljs-preprocessor">.getColor</span>(R<span class="hljs-preprocessor">.color</span><span class="hljs-preprocessor">.font</span>_text)<span class="hljs-comment">;</span>mTextHeight = mHeight / CityActivity<span class="hljs-preprocessor">.pinyinList</span><span class="hljs-preprocessor">.size</span>()<span class="hljs-comment">;</span>position = <span class="hljs-built_in">y</span> / (mHeight / (CityActivity<span class="hljs-preprocessor">.pinyinList</span><span class="hljs-preprocessor">.size</span>() + <span class="hljs-number">1</span>))<span class="hljs-comment">;</span>invalidate()<span class="hljs-comment">;</span><span class="hljs-keyword">break</span><span class="hljs-comment">;</span>case MotionEvent<span class="hljs-preprocessor">.ACTION</span>_MOVE:if (isSlide) {backgroundColor = getResources()<span class="hljs-preprocessor">.getColor</span>(R<span class="hljs-preprocessor">.color</span><span class="hljs-preprocessor">.font</span>_text)<span class="hljs-comment">;</span>mTextHeight = mHeight / CityActivity<span class="hljs-preprocessor">.pinyinList</span><span class="hljs-preprocessor">.size</span>()<span class="hljs-comment">;</span>position = <span class="hljs-built_in">y</span> / (mHeight / CityActivity<span class="hljs-preprocessor">.pinyinList</span><span class="hljs-preprocessor">.size</span>() + <span class="hljs-number">1</span>) + <span class="hljs-number">1</span><span class="hljs-comment">;</span>invalidate()<span class="hljs-comment">;</span>}<span class="hljs-keyword">break</span><span class="hljs-comment">;</span>case MotionEvent<span class="hljs-preprocessor">.ACTION</span>_UP:backgroundColor = getResources()<span class="hljs-preprocessor">.getColor</span>(R<span class="hljs-preprocessor">.color</span><span class="hljs-preprocessor">.font</span>_info)<span class="hljs-comment">;</span>mTextHeight = mHeight / CityActivity<span class="hljs-preprocessor">.pinyinList</span><span class="hljs-preprocessor">.size</span>()<span class="hljs-comment">;</span>position = <span class="hljs-number">0</span><span class="hljs-comment">;</span>invalidate()<span class="hljs-comment">;</span>listener<span class="hljs-preprocessor">.showTextView</span>(selectTxt, true)<span class="hljs-comment">;</span><span class="hljs-keyword">break</span><span class="hljs-comment">;</span>}return true<span class="hljs-comment">;</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></ul>

case MotionEvent.ACTION_DOWN:设置背景颜色,设置字体初始高度,计算触摸位置,调用invalidate()进行重绘;
case MotionEvent.ACTION_MOVE:与ACTION_DOWN一样的操作,加上一个判断,让滑动的距离大于默认的最小滑动距离才设置滑动有效;
case MotionEvent.ACTION_UP:设置背景颜色,设置字体初始高度,将position设置为0,进行重置操作,调用invalidate()进行重绘;

3.触摸监听

屏幕中间是一个自定义的圆形TextView,默认设置为View.GONE,触摸的时候设置为View.VISIBLE,并将TextView的值设置为点击触摸的字母。因此我们的接口设计如下:

<code class="hljs cs has-numbering">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> onTouchListener {<span class="hljs-keyword">void</span> showTextView(String textView, boolean dismiss);}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>

在MotionEvent.ACTION_DOWN与MotionEvent.ACTION_MOVE的时候:

<code class="hljs bash has-numbering">listener.showTextView(selectTxt, <span class="hljs-literal">false</span>);</code><ul style="" class="pre-numbering"><li>1</li></ul>

在MotionEvent.ACTION_UP的时候:

<code class="hljs bash has-numbering">listener.showTextView(selectTxt, <span class="hljs-literal">true</span>);</code><ul style="" class="pre-numbering"><li>1</li></ul>

然后让Activity实现该接口,通过传过来的boolean值控制圆形TextView是否显示:

<code class="hljs java has-numbering">    <span class="hljs-annotation">@Override</span><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">showTextView</span>(String textView, <span class="hljs-keyword">boolean</span> dismiss) {<span class="hljs-keyword">if</span> (dismiss) {circleTxt.setVisibility(View.GONE);} <span class="hljs-keyword">else</span> {circleTxt.setVisibility(View.VISIBLE);circleTxt.setText(textView);}<span class="hljs-keyword">int</span> selectPosition = <span class="hljs-number">0</span>;<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < cityList.size(); i++) {<span class="hljs-keyword">if</span> (cityList.get(i).getFirstPinYin().equals(textView)) {selectPosition = i;<span class="hljs-keyword">break</span>;}}recyclerView.scrollToPosition(selectPosition);}     </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></ul>

点击触摸的同时,需要让recyclerView滑动到对应的位置。遍历cityList数组,得到拼音的第一个字母,与传递过来的索引字母进行对比,相等则将
i 设置为selectPosition。最后调用recyclerView.scrollToPosition()方法,滑动到对应的位置,达到索引导航的作用。

3.RecyclerView的悬停实现

1.布局文件
头部布局:layout_sticky_header_view.xml,也就是示例图中红色的部分,里面包含一个索引字母TextView
主界面的布局:一共两层,头部布局覆盖在RecyclerView上面

<code class="hljs xml has-numbering">        <span class="hljs-tag"><<span class="hljs-title">FrameLayout
</span>            <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span><span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span><span class="hljs-attribute">android:layout_toLeftOf</span>=<span class="hljs-value">"@+id/my_slide_view"</span>></span><span class="hljs-tag"><<span class="hljs-title">android.support.v7.widget.RecyclerView
</span>                <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/rv_sticky_example"</span><span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span><span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span><span class="hljs-attribute">android:scrollbars</span>=<span class="hljs-value">"none"</span> /></span><span class="hljs-tag"><<span class="hljs-title">include</span> <span class="hljs-attribute">layout</span>=<span class="hljs-value">"@layout/layout_sticky_header_view"</span> /></span><span class="hljs-tag"></<span class="hljs-title">FrameLayout</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><li>8</li><li>9</li><li>10</li><li>11</li></ul>

子item的布局:线性布局竖直排列,上面引入头部布局,下面为显示城市名字的布局

2.构建CityAdapter

<code class="hljs avrasm has-numbering">    @Overridepublic void onBindViewHolder(RecyclerView<span class="hljs-preprocessor">.ViewHolder</span> holder, final int position) {if (holder instanceof CityViewHolder) {CityViewHolder viewHolder = (CityViewHolder) holder<span class="hljs-comment">;</span>City cityModel = cityLists<span class="hljs-preprocessor">.get</span>(position)<span class="hljs-comment">;</span>viewHolder<span class="hljs-preprocessor">.tvCityName</span><span class="hljs-preprocessor">.setText</span>(cityModel<span class="hljs-preprocessor">.getCityName</span>())<span class="hljs-comment">;</span>if (position == <span class="hljs-number">0</span>) {viewHolder<span class="hljs-preprocessor">.tvStickyHeader</span><span class="hljs-preprocessor">.setVisibility</span>(View<span class="hljs-preprocessor">.VISIBLE</span>)<span class="hljs-comment">;</span>viewHolder<span class="hljs-preprocessor">.tvStickyHeader</span><span class="hljs-preprocessor">.setText</span>(cityModel<span class="hljs-preprocessor">.getFirstPinYin</span>())<span class="hljs-comment">;</span>viewHolder<span class="hljs-preprocessor">.itemView</span><span class="hljs-preprocessor">.setTag</span>(FIRST_STICKY_VIEW)<span class="hljs-comment">;</span>} else {if (!TextUtils<span class="hljs-preprocessor">.equals</span>(cityModel<span class="hljs-preprocessor">.getFirstPinYin</span>(), cityLists<span class="hljs-preprocessor">.get</span>(position - <span class="hljs-number">1</span>)<span class="hljs-preprocessor">.getFirstPinYin</span>())) {viewHolder<span class="hljs-preprocessor">.tvStickyHeader</span><span class="hljs-preprocessor">.setVisibility</span>(View<span class="hljs-preprocessor">.VISIBLE</span>)<span class="hljs-comment">;</span>viewHolder<span class="hljs-preprocessor">.tvStickyHeader</span><span class="hljs-preprocessor">.setText</span>(cityModel<span class="hljs-preprocessor">.getFirstPinYin</span>())<span class="hljs-comment">;</span>viewHolder<span class="hljs-preprocessor">.itemView</span><span class="hljs-preprocessor">.setTag</span>(HAS_STICKY_VIEW)<span class="hljs-comment">;</span>} else {viewHolder<span class="hljs-preprocessor">.tvStickyHeader</span><span class="hljs-preprocessor">.setVisibility</span>(View<span class="hljs-preprocessor">.GONE</span>)<span class="hljs-comment">;</span>viewHolder<span class="hljs-preprocessor">.itemView</span><span class="hljs-preprocessor">.setTag</span>(NONE_STICKY_VIEW)<span class="hljs-comment">;</span>}}viewHolder<span class="hljs-preprocessor">.itemView</span><span class="hljs-preprocessor">.setContentDescription</span>(cityModel<span class="hljs-preprocessor">.getFirstPinYin</span>())<span class="hljs-comment">;</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></ul>

这里重点说一下onBindViewHolder这个方法:

每一个RecyclerView的item的布局里面都包含一个头部布局,然后判断当前item和上一个item的头部布局里的索引字母是否相同,来决定是否展示item的头部布局。

第一个item的头部布局是显示的,设置为View.VISIBLE,标记tag为FIRST_STICKY_VIEW;
item布局中,索引字母不相同的头部布局是显示的,设置为View.VISIBLE,标记tag为HAS_STICKY_VIEW;
item布局中,索引字母相同的头部布局是隐藏的,设置为View.GONE,标记tag为NONE_STICKY_VIEW;

最后为每一个item设置一个ContentDescription ,用来记录并获取头部布局展示的信息。

3.RecyclerView的滑动监听

主界面的布局中,最上层有一个头部布局tvStickyHeaderView,通过监听RecyclerView的滚动,根据RecyclerView的滚动距离,决定头部布局向上或者向下滚动的距离,实现悬停效果:

<code class="hljs java has-numbering">        recyclerView.addOnScrollListener(<span class="hljs-keyword">new</span> RecyclerView.OnScrollListener() {<span class="hljs-annotation">@Override</span><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onScrolled</span>(RecyclerView recyclerView, <span class="hljs-keyword">int</span> dx, <span class="hljs-keyword">int</span> dy) {<span class="hljs-keyword">super</span>.onScrolled(recyclerView, dx, dy);View stickyInfoView = recyclerView.findChildViewUnder(tvStickyHeaderView.getMeasuredWidth() / <span class="hljs-number">2</span>, <span class="hljs-number">5</span>);<span class="hljs-keyword">if</span> (stickyInfoView != <span class="hljs-keyword">null</span> && stickyInfoView.getContentDescription() != <span class="hljs-keyword">null</span>) {tvStickyHeaderView.setText(String.valueOf(stickyInfoView.getContentDescription()));}View transInfoView = recyclerView.findChildViewUnder(tvStickyHeaderView.getMeasuredWidth() / <span class="hljs-number">2</span>, tvStickyHeaderView.getMeasuredHeight() + <span class="hljs-number">1</span>);<span class="hljs-keyword">if</span> (transInfoView != <span class="hljs-keyword">null</span> && transInfoView.getTag() != <span class="hljs-keyword">null</span>) {<span class="hljs-keyword">int</span> transViewStatus = (<span class="hljs-keyword">int</span>) transInfoView.getTag();<span class="hljs-keyword">int</span> dealtY = transInfoView.getTop() - tvStickyHeaderView.getMeasuredHeight();<span class="hljs-keyword">if</span> (transViewStatus == CityAdapter.HAS_STICKY_VIEW) {<span class="hljs-keyword">if</span> (transInfoView.getTop() > <span class="hljs-number">0</span>) {tvStickyHeaderView.setTranslationY(dealtY);} <span class="hljs-keyword">else</span> {tvStickyHeaderView.setTranslationY(<span class="hljs-number">0</span>);}} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (transViewStatus == CityAdapter.NONE_STICKY_VIEW) {tvStickyHeaderView.setTranslationY(<span class="hljs-number">0</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></ul>

1.第一次调用RecyclerView的findChildViewUnder()方法,返回指定位置的childView,这里也就是item的头部布局,因为我们的tvStickyHeaderView展示的肯定是最上面item的头部布局里的索引字母信息。
2.第二次调用RecyclerView的findChildViewUnder()方法,这里返回的是固定在屏幕上方那个tvStickyHeaderView下面一个像素位置的RecyclerView的item,根据这个item来更新tvStickyHeaderView要translate多少距离。
3.如果tag为HAS_STICKY_VIEW,表示当前item需要展示头部布局,那么根据这个item的getTop和tvStickyHeaderView的高度相差的距离来滚动tvStickyHeaderView;如果tag为NONE_STICKY_VIEW,表示当前item不需要展示头部布局,那么就不会引起tvStickyHeaderView的滚动。

参考资料

最后使用接口回调处理RecyclerView的点击事件即可。由于篇幅有限,这里就只介绍了一些重点。项目完整源码已经上传到我的github上:

https://github.com/18722527635/CityDemo

除了这个项目,这里面还包括之前写的一些自定义View。热烈欢迎大家Star&&Fork,有问题与意见可以随时向我提问,我一定努力改进。


 我的另外一个具有此功能的项目地址,可下载:
http://download.csdn.net/detail/u011277123/9600136

这篇关于动手写一个好看的城市导航列表的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1027029

相关文章

Python实现将实体类列表数据导出到Excel文件

《Python实现将实体类列表数据导出到Excel文件》在数据处理和报告生成中,将实体类的列表数据导出到Excel文件是一项常见任务,Python提供了多种库来实现这一目标,下面就来跟随小编一起学习一... 目录一、环境准备二、定义实体类三、创建实体类列表四、将实体类列表转换为DataFrame五、导出Da

Python中列表的高级索引技巧分享

《Python中列表的高级索引技巧分享》列表是Python中最常用的数据结构之一,它允许你存储多个元素,并且可以通过索引来访问这些元素,本文将带你深入了解Python列表的高级索引技巧,希望对... 目录1.基本索引2.切片3.负数索引切片4.步长5.多维列表6.列表解析7.切片赋值8.删除元素9.反转列表

基于 YOLOv5 的积水检测系统:打造高效智能的智慧城市应用

在城市发展中,积水问题日益严重,特别是在大雨过后,积水往往会影响交通甚至威胁人们的安全。通过现代计算机视觉技术,我们能够智能化地检测和识别积水区域,减少潜在危险。本文将介绍如何使用 YOLOv5 和 PyQt5 搭建一个积水检测系统,结合深度学习和直观的图形界面,为用户提供高效的解决方案。 源码地址: PyQt5+YoloV5 实现积水检测系统 预览: 项目背景

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

Spring+MyBatis+jeasyui 功能树列表

java代码@EnablePaging@RequestMapping(value = "/queryFunctionList.html")@ResponseBodypublic Map<String, Object> queryFunctionList() {String parentId = "";List<FunctionDisplay> tables = query(parent

动手学深度学习【数据操作+数据预处理】

import osos.makedirs(os.path.join('.', 'data'), exist_ok=True)data_file = os.path.join('.', 'data', 'house_tiny.csv')with open(data_file, 'w') as f:f.write('NumRooms,Alley,Price\n') # 列名f.write('NA

Exchange 服务器地址列表的配置方法与注意事项

Exchange Server 是微软推出的一款企业级邮件服务器软件,广泛应用于企业内部邮件系统的搭建与管理。配置 Exchange 服务器地址列表是其中一个关键环节。本文将详细介绍 Exchange 服务器地址列表的配置方法与注意事项,帮助系统管理员顺利完成这一任务。 内容目录 1. 引言 2. 准备工作 3. 配置地址列表 3.1 创建地址列表 3.2 使用 Exchange

如何设置好看的电脑屏保?电脑屏保设置教程

如何设置好看的电脑屏保?电脑屏保设置教程。大家好,今天小编给大家带来了好看的电脑屏保,教大家如何设置一个好看的电脑屏保。屏保软件很多,今天我们介绍一款比较有特殊的屁屏保软件:芝麻时钟(芝麻时钟 桌面时钟软件 桌面日历 时钟屏保 世界时钟软件下载芝麻时钟是很有个性的时钟软件,支持桌面时钟,任务栏时钟美化,世界时钟,桌面日历,桌面天气,记事便签,时钟屏保。把时钟放到桌面,选择自己喜欢的主题修改任务栏时

Python--列表简介

列表是什么 列表让你能够在⼀个地方存储成组的信息,其中既可以只包含几个元素,也可以包含数百万个元素。列表是新手可直接使用的最强大的Python 功能之⼀。 列表(list)是一种可变的序列类型,用于存储一系列有序的元素。这些元素可以是任何类型,包括整数、浮点数、字符串、其他列表(即嵌套列表)等。列表是动态的,可以在运行时增加或删除元素。 用方括号([ ])表示列表,用逗号分隔其中的元素。

【动手学深度学习】04 数据操作 + 数据预处理(个人向笔记)

数据操作 N维数组是机器学习和神经网络的主要数据结构其中 2-d 矩阵中每一行表示每一行表示一个样本 当维度来到三维的时候则可以表示成一张图片,再加一维就可以变成多张图片,再加一维则可以变成一个视频 访问元素 冒号表示从冒号左边的元素到冒号右边的前一个元素(开区间),其中如果左边为空,那么表示从第一个开始,如果右边为空,那么表示访问到最后一个,如果两边都为空,则表示全部访问其中一行中我们指