大家都知道, RecycleView 默认只能通过 setLayoutManager() 方法指定一种布局结构, 那么像支付宝首页这样复杂的多布局情况如何处理呢? 在 ListView 中, 我们也遇到过这种情况, 是通过 getItemViewType 结合其他方法, 通过判断类型来加载不同的布局. 在 RecycleView 中也同样使用, 今天我们主要介绍 RecycleView 多布局实现, 其中布局嵌套了 GridView, 有些坑需要注意, 避免陷入.
一, 效果图
废话不多说, 献上效果图.
从图中可以看出, 一共有三种布局样式. 第一种: 头布局, 包含各种类型的新闻频道. 第二种布局: 普通的新闻. 第三种布局: 多图图集新闻.
二, 多布局实现的思路
步骤一, getItemViewType() 方法判断不同的布局, 返回判断结果.
步骤二, 创建不同的 ViewHolder 类, 针对不同的布局类型, 进行对应的布局内控件的初始化.
步骤三, onCreateViewHolder() 方法根据第一步的判断结果, 得到 viewType, 根据 viewType 值, 实例化不同类型的 ViewHolder 对象.
步骤四, onBindViewHolder() 方法判断 holder 所属类型, 进行相对应类型的布局内控件内容的赋值.
以上就是实现多布局的思路, 很简单 (可能描述不到位, 不易理解), 下面我们以代码的形式更直观的来了解具体步骤.
三, RecycleView 多布局实现代码
首先定义三种类型常量
- // 新闻模式
- private static final int TYPE_SINGLE = 0;
- // 图集模式
- private static final int TYPE_MULTI = 1;
- // 头布局模式
- private static final int TYPE_HEADER = 2;
getItemViewType() 方法判断不同的布局, 返回判断结果
- @Override
- public int getItemViewType(int position) {
- // 所有的新闻详情数据, 在第一个位置自己手动加入了一个假的新闻数据, 把它的 title 设置为 "头布局"
- NewsInfo info = list.get(position);
- // 识别出 "头布局", 则是第一个数据, 把它归于头布局类型
- if(info.getTitle().endsWith("头布局")){
- return TYPE_HEADER;
- }
- // 有新闻具体内容, 是普通新闻
- if (!TextUtils.isEmpty(info.getDigest()))
- {
- return TYPE_SINGLE;
- }
- // 其他情况, 是图集新闻
- return TYPE_MULTI;
- }
创建不同的 ViewHolder 类, 针对不同的布局类型, 进行对应的布局内控件的初始化.
省略了具体的初始化操作
- public class SingleViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
- .......
- .......
- public SingleViewHolder(View itemView) {
- super(itemView);
- .......
- .......
- }
- @Override
- public void onClick(View view) {
- Toast.makeText(context,"点击了",Toast.LENGTH_SHORT).show();
- }
- }
- public class MultiViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
- .......
- .......
- public MultiViewHolder(View itemView) {
- super(itemView);
- .......
- .......
- }
- @Override
- public void onClick(View view) {
- Toast.makeText(context,"点击了",Toast.LENGTH_SHORT).show();
- }
- }
- public class HeaderViewHolder extends RecyclerView.ViewHolder{
- .......
- .......
- public HeaderViewHolder(View itemView) {
- super(itemView);
- .......
- .......
- }
- }
onCreateViewHolder() 方法根据第一步的判断结果, 得到 viewType, 根据 viewType 值, 实例化不同类型的 ViewHolder 对象.
- @Override
- public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- switch (viewType) {
- case TYPE_SINGLE:
- return new SingleViewHolder(inflater.inflate(R.layout.item_news, parent, false));
- case TYPE_MULTI:
- return new MultiViewHolder(inflater.inflate(R.layout.item_news_photo, parent, false));
- case TYPE_HEADER:
- return new HeaderViewHolder(inflater.inflate(R.layout.item_news_header,parent,false));
- }
- return null;
- }
onBindViewHolder() 方法判断 holder 所属类型, 进行相对应类型的布局内控件内容的赋值.
- @Override
- public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
- // 设置不同的布局内控件的值
- if (holder instanceof SingleViewHolder) {
- setNormalItemValues((SingleViewHolder) holder,position);
- }else if(holder instanceof MultiViewHolder){
- setMultiItemValues((MultiViewHolder) holder,position);
- }else{
- setHeaderItemValues((HeaderViewHolder) holder);
- }
- }
针对不同类型, 设置布局内控件的值的具体内容就不介绍了, 比较繁多, 下面有源码链接.
由于本例中头布局是个 GridView, 需要注意的一点是 RecycleView 中嵌套 GridView 会出现只显示一行的问题. 下面给出具体解决方法:
在 setHeaderItemValues((HeaderViewHolder) holder); 中, 主动测量出 GridView 的高度, 并设置给布局. 本例中默认 GridView 是两行.
- // 解决 GridView 只显示一行的原因 主动设置 GridView 的高度
- ViewGroup.LayoutParams params = holder.mGridView.getLayoutParams();
- View view = mAdapter.getView(0, null, holder.mGridView);
- view.measure(0,0);
- int height =view.getMeasuredHeight();
- int totalHeight = holder.mGridView.getVerticalSpacing() * 2 + height * 2;
- params.height = totalHeight;
- holder.mGridView.setLayoutParams(params);
源码地址比较乱, 直接看 NewsFragment 相关内容即可.
自己是从事了七年开发的 Android 工程师, 不少人私下问我, 2019 年 Android 进阶该怎么学, 方法有没有?
没错, 年初我花了一个多月的时间整理出来的学习资料, 希望能帮助那些想进阶提升 Android 开发, 却又不知道怎么进阶学习的朋友.[包括高级 UI, 性能优化, 架构师课程, NDK,Kotlin, 混合式开发 (ReactNative+Weex),Flutter 等架构技术资料] , 希望能帮助到您面试前的复习且找到一个好的工作, 也节省大家在网上搜索资料的时间来学习.
来源: http://www.jianshu.com/p/740b46a45f0e