上文提到,重构后,SuspensionDecoration
数据源依赖的接口是ISuspensionInterface
,
如下:
public interface ISuspensionInterface {
//是否需要显示悬停title
boolean isShowSuspension();
//悬停的title
String getSuspensionTag();
}
在BaseIndexBean
里实现,默认显示悬停,分组title和IndexBar的Tag是一样的。
public abstract class BaseIndexBean implements ISuspensionInterface {
private String baseIndexTag;//所属的分类(城市的汉语拼音首字母)
@Override
public String getSuspensionTag() {
return baseIndexTag;
}
@Override
public boolean isShowSuspension() {
return true;
}
}
而BaseIndexPinyinBean
类,现在如下:
public abstract class BaseIndexPinyinBean extends BaseIndexBean {
private String baseIndexPinyin;//城市的拼音
//是否需要被转化成拼音, 类似微信头部那种就不需要 美团的也不需要
//微信的头部 不需要显示索引
//美团的头部 索引自定义
//默认应该是需要的
public boolean isNeedToPinyin() {
return true;
}
//需要转化成拼音的目标字段
public abstract String getTarget();
}
所以我们需要实现微信那种效果,只需要重写isShowSuspension()
和isNeedToPinyin()
这两个方法,并setBaseIndexTag()
直接设置tag即可。
仿美团选择城市
=======
这个页面还是挺麻烦的,所以步骤也最多。建议结合代码阅读Demo及库地址。
分析美团选择城市列表:
* 主体部分仍旧是一个普通的 分组悬停&索引导航 的列表(美团没有悬停功能)。
* 头部是由若干复杂HeaderView组成。
* 从右侧索引栏可以看出,定位、最近、热门这三个Item对应了列表三个HeaderView。
* 最顶部的HeaderView是不需要分组,也不需要索引的。
那么逐一实现:
主体部分
很简单,根据前文最后的封装( 第二篇戳我),如果只有主体部分,我们需要让主体部分的JavaBean继承自BaseIndexPinyinBean
,然后正常构建数据,最终设置给IndexBar和SuspensionDecoration即可。
public class MeiTuanBean extends BaseIndexPinyinBean {
private String city;//城市名字
…
@Override
public String getTarget() {
return city;
}
}
头部若干HeaderViews
这里不管是通过HeaderView添加进来头部布局,还是通过itemViewType自己去实现,核心都是通过itemViewType去做的。
也就是说头部的HeaderView也是RecyclerView的Item。
既然是Item一定对应着相应的JavaBean。
我们需要针对这些JavaBean让其分别继承BaseIndexPinyinBean
。
具体怎么实现头部布局不是本文重点,不再赘述,Demo里有代码可细看Demo及库地址。
定、近、热三个HeaderView的处理
定、近、热三个HeaderView有如下特点:
* 右侧导航索引的title 为自定义,不是拼音首字母则也不需要排序。
* 悬停分组的title 和 右侧导航索引的title 不一样,则悬停分组的title也需要自定义。
做法:
不过既然是RecyclerView里的Item,又有 悬停分组、索引导航 特性。那么就要继承BaseIndexPinyinBean
。
* 不需要转化成拼音且不排序,则重写isNeedToPinyin()
返回false,并调用setBaseIndexTag(indexBarTag)
给右侧索引赋值。
* 需要自定义悬停分组的title,则重写getSuspensionTag()
返回title。
public class MeituanHeaderBean extends BaseIndexPinyinBean {
private List cityList;
//悬停ItemDecoration显示的Tag
private String suspensionTag;
public MeituanHeaderBean(List cityList, String suspensionTag, String indexBarTag) {
this.cityList = cityList;
this.suspensionTag = suspensionTag;
this.setBaseIndexTag(indexBarTag);
}
@Override
public String getTarget() {
return null;
}
@Override
public boolean isNeedToPinyin() {
return false;
}
@Override
public String getSuspensionTag() {
return suspensionTag;
}
}
用private List<MeituanHeaderBean> mHeaderDatas;
保存定、近、热头部数据源,最终需要将其设置给IndexBar
和SuspensionDecoration
。
mHeaderDatas = new ArrayList<>();
List locationCity = new ArrayList<>();
locationCity.add(“定位中”);
mHeaderDatas.add(new MeituanHeaderBean(locationCity, “定位城市”, “定”));
List recentCitys = new ArrayList<>();
mHeaderDatas.add(new MeituanHeaderBean(recentCitys, “最近访问城市”, “近”));
List hotCitys = new ArrayList<>();
mHeaderDatas.add(new MeituanHeaderBean(hotCitys, “热门城市”, “热”));
最顶部的HeaderView
最顶部的HeaderView,由于不需要右侧索引,也没有悬停分组。它只是一个普通的HeaderView即可。
对于这种需求的HeaderView,只需要将它们的数量传给IndexBar
和SuspensionDecoration
即可。
在内部我已经做了处理,保证联动坐标和数据源下标的正确。
mDecoration.setHeaderViewCount(mHeaderAdapter.getHeaderViewCount() - mHeaderDatas.size()));
mIndexBar.setHeaderViewCount(mHeaderAdapter.getHeaderViewCount() - mHeaderDatas.size());
这里用headerView一共的count=4,减去上步中mHeaderDatas
的size =3,得出不需要右侧索引,也没有悬停分组 头部的数量。
将主体数据集和头部数据集合并
我们前几步中,设计到了三部分数据集,
一部分是主体数据集,
//主体部分数据源(城市数据)
private List mBodyDatas;
第二部分是需要特性的头部数据集
//头部数据源
private List mHeaderDatas;
第三部分是不需要特性的数据集,这里忽略。我们只用到它的count。
我们需要将第一和第二部分融合,并且设置给IndexBar
和SuspensionDecoration
。
则我们利用它们共同的基类,BaseIndexPinyinBean
来存储。
核心代码如下:
//设置给InexBar、ItemDecoration的完整数据集
private List mSourceDatas;
mSourceDatas.addAll(mHeaderDatas);
mSourceDatas.addAll(mBodyDatas);
设置给IndexBar
:
mIndexBar.setmPressedShowTextView(mTvSideBarHint)//设置HintTextView
.setNeedRealIndex(true)//设置需要真实的索引
.setmLayoutManager(mManager)//设置RecyclerView的LayoutManager
.setHeaderViewCount(mHeaderAdapter.getHeaderViewCount() - mHeaderDatas.size());
.setmSourceDatas(mSourceDatas)//设置数据
设置给SuspensionDecoration
:
mRv.addItemDecoration(new SuspensionDecoration(this, mSourceDatas)
.setHeaderViewCount(mHeaderAdapter.getHeaderViewCount() - mHeaderDatas.size()));
效果图如文首。
核心代码
这里再提一点,我已经将排序功能抽离至IndexBar
的IIndexBarDataHelper
类型变量中去做,
在mIndexBar.setmSourceDatas(mSourceDatas)
时会自动排序。
也可以手动调用mIndexBar.getDataHelper().sortSourceDatas(mBodyDatas);
排序。
像本节的案例,可以选择先排序bodyDatas,然后再合并至sourceDatas,最终设置给IndexBar
和SuspensionDecoration
。
如:
//先排序
mIndexBar.getDataHelper().sortSourceDatas(mBodyDatas);
mSourceDatas.addAll(mBodyDatas);
mIndexBar.setmSourceDatas(mSourceDatas)//设置数据
.invalidate();
mDecoration.setmDatas(mSourceDatas);
涉及到的重构代码:
除了上节提到的那些数据结构的重构,
我还将以前在IndexBar里完成的:
-
1 将汉语转成拼音
-
2 填充indexTag
-
3 排序源数据源
-
4 根据排序后的源数据源->indexBar的数据源
抽成一个接口表示,与IndexBar分离。
/**
-
介绍:IndexBar 的 数据相关帮助类
-
1 将汉语转成拼音
-
2 填充indexTag
-
3 排序源数据源
-
4 根据排序后的源数据源->indexBar的数据源
-
作者:zhangxutong
-
邮箱:mcxtzhang@163.com
-
主页:http://blog.youkuaiyun.com/zxt0601
-
时间: 2016/11/28.
*/
public interface IIndexBarDataHelper {
//汉语-》拼音
IIndexBarDataHelper convert(List<? extends BaseIndexPinyinBean> data);
//拼音->tag
IIndexBarDataHelper fillInexTag(List<? extends BaseIndexPinyinBean> data);
//对源数据进行排序(RecyclerView)
IIndexBarDataHelper sortSourceDatas(List<? extends BaseIndexPinyinBean> datas);
//对IndexBar的数据源进行排序(右侧栏),在 sortSourceDatas 方法后调用
IIndexBarDataHelper getSortedIndexDatas(List<? extends BaseIndexPinyinBean> sourceDatas, List datas);
}
IndexBar内部持有这个接口的变量,调用其中方法完成需求:
public IndexBar setmSourceDatas(List<? extends BaseIndexPinyinBean> mSourceDatas) {
this.mSourceDatas = mSourceDatas;
initSourceDatas();//对数据源进行初始化
return this;
}
/**
-
初始化原始数据源,并取出索引数据源
-
@return
*/
private void initSourceDatas() {
//add by zhangxutong 2016 09 08 :解决源数据为空 或者size为0的情况,
if (null == mSourceDatas || mSourceDatas.isEmpty()) {
return;
}
if (!isSourceDatasAlreadySorted) {
//排序sourceDatas
mDataHelper.sortSourceDatas(mSourceDatas);
} else {
//汉语->拼音
mDataHelper.convert(mSourceDatas);
//拼音->tag
mDataHelper.fillInexTag(mSourceDatas);
}
if (isNeedRealIndex) {
mDataHelper.getSortedIndexDatas(mSourceDatas, mIndexDatas);
computeGapHeight();
}
}
我在sortSourceDatas()
实现里,已经调用了convert(datas);
和 fillInexTag(datas);
如何做好面试突击,规划学习方向?
面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。
学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。
同时我还搜集整理2020年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。
在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。
点击:《Android架构视频+BAT面试专题PDF+学习笔记》即可免费获取~
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
片化的学习,对自己的提升是很慢的。
同时我还搜集整理2020年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。
[外链图片转存中…(img-om9xfHK2-1646238628530)]
在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。
[外链图片转存中…(img-cux7p74G-1646238628531)]
点击:《Android架构视频+BAT面试专题PDF+学习笔记》即可免费获取~
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。