本文目标
阅读完本文,你会get如下技能:
使用RecyclerView实现复杂布局;
使用RecyclerView实现ListView以及GirdView混排效果;
通过优化Model加深了解通过RecyclerView实现混排效果。
希望通过不断get小技能,让你在Android路上一路畅通无堵~
废话不多说,开车~
一、使用RecyclerView实现复杂布局
首先我们先做一些基本操作,类似于控件放置,初始化(控件,模拟数据)等。
1.1 引用RecyclerView,填充布局
1)Android Studio的小伙伴记得引入有关RecyclerView依赖; 2)Eclipse的小伙伴记得拷贝本地SDK下载包含的RecyclerView jar包即可。
布局放置如下:
放置好了一个RecyclerView,下面模拟数据时,来一个Model类。
1.2 设置Model类
package com.materialdesignstudy.complexrecycler.itemone;/** * Created by HLQ on 2017/10/22 */public class DataModel { // 状态标识位 public static final int TYPE_ONE = 1; public static final int TYPE_TWO = 2; public static final int TYPE_THREE = 3; public int type; // 类型 针对某种样式文件 也就是布局 public int avatarColor; // 头像颜色 public String name; // 姓名 public String content; // 内容 public int contentColor; // 内容颜色}
1.3 定义item布局
如效果图一般,我们创建如下item布局文件:
item_type_one
item_type_two:
item_layout_three:
1.4 编写对应ViewHolder
由于我们的ViewHolder都需要构造以及数据绑定,由此,我们拓展一个封装ViewHolder类,让其他的ViewHolder直接集成此类即可。
定义封装ViewHolder类:
package com.materialdesignstudy.complexrecycler.itemone;import android.support.v7.widget.RecyclerView;import android.view.View;/** * ViewHolder封装类 * Created by HLQ on 2017/10/22 */public abstract class TypeAbstractViewHolder extends RecyclerView.ViewHolder { public TypeAbstractViewHolder(View itemView) { super(itemView); } /** * 数据绑定 * * @param dataModel 数据源 由于我们模拟数据实体类暂定为DataModel 这里直接传入这个即可 * 后期可根据项目实际需求去设置 也可以直接写为T */ public abstract void bindHolder(DataModel dataModel); }
定义Item1对应的ViewHolder:
package com.materialdesignstudy.complexrecycler.itemone;import android.graphics.Color;import android.view.View;import android.widget.ImageView;import android.widget.TextView;import com.materialdesignstudy.R;/** * Created by HLQ on 2017/10/22 */public class TypeOneViewHolder extends TypeAbstractViewHolder { public ImageView avatar; public TextView name; public TypeOneViewHolder(View itemView) { super(itemView); // 初始化控件 avatar = itemView.findViewById(R.id.avatar); name = itemView.findViewById(R.id.name); // 为了区分item 这里为item设置背景颜色 itemView.setBackgroundColor(Color.YELLOW); } @Override public void bindHolder(DataModel dataModel) { // 数据绑定 avatar.setBackgroundResource(dataModel.avatarColor); name.setText(dataModel.name); } }
item2对应ViewHolder:
package com.materialdesignstudy.complexrecycler.itemone;import android.graphics.Color;import android.view.View;import android.widget.ImageView;import android.widget.TextView;import com.materialdesignstudy.R;/** * Created by HLQ on 2017/10/22 */public class TypeTwoViewHolder extends TypeAbstractViewHolder { public ImageView avatar; public TextView name, content; public TypeTwoViewHolder(View itemView) { super(itemView); avatar = itemView.findViewById(R.id.avatar); name = itemView.findViewById(R.id.name); content = itemView.findViewById(R.id.content); itemView.setBackgroundColor(Color.GRAY); } @Override public void bindHolder(DataModel dataModel) { avatar.setBackgroundResource(dataModel.avatarColor); name.setText(dataModel.name); content.setText(dataModel.content); } }
item3对应ViewHolder:
package com.materialdesignstudy.complexrecycler.itemone;import android.graphics.Color;import android.view.View;import android.widget.ImageView;import android.widget.TextView;import com.materialdesignstudy.R;/** * Created by HLQ on 2017/10/22 */public class TypeThreeViewHolder extends TypeAbstractViewHolder { public ImageView avatar, contentImg; public TextView name, content; public TypeThreeViewHolder(View itemView) { super(itemView); avatar = itemView.findViewById(R.id.avatar); name = itemView.findViewById(R.id.name); content = itemView.findViewById(R.id.content); contentImg = itemView.findViewById(R.id.contentImg); itemView.setBackgroundColor(Color.BLUE); } @Override public void bindHolder(DataModel dataModel) { avatar.setBackgroundResource(dataModel.avatarColor); name.setText(dataModel.name); content.setText(dataModel.content); contentImg.setBackgroundResource(dataModel.contentColor); } }
1.5 定义空的Adapter类,初始化控件、模拟数据
package com.materialdesignstudy.complexrecycler.itemone;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import com.materialdesignstudy.R;import java.util.ArrayList;import java.util.List;/** * 复杂布局实现 * Created by HLQ on 2017/10/22 */public class ComplexOneActivity extends AppCompatActivity { private RecyclerView mRecyclerView; private OneAdapter mOneAdapter; private int mColor[] = {android.R.color.holo_red_light, android.R.color.holo_green_light, android.R.color.holo_blue_light}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_complec_one); initItem(); initData(); } private void initData() { List dataList = new ArrayList<>(); for (int i = 0; i < 20; i++) { int type = (int) ((Math.random() * 3) + 1); // 随机type DataModel dataModel = new DataModel(); dataModel.avatarColor = mColor[type - 1]; dataModel.type = type; dataModel.name = "name" + i; dataModel.content = "content:" + i; dataModel.contentColor = mColor[(type + 1) % 3]; dataList.add(dataModel); } mOneAdapter.addList(dataList); mOneAdapter.notifyDataSetChanged(); } private void initItem() { mRecyclerView = findViewById(R.id.id_recy); mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); mOneAdapter = new OneAdapter(this); mRecyclerView.setAdapter(mOneAdapter); } }
以上这些都是很easy的,这里就不过多说明了。关键内容如下,让我们一起瞅瞅Adapter又是如何编写的。
1.6 打造属于you的Adapter
首先,这里需要再次说明一点:
getItemViewType():返回当前ItemView类型,而我们将会根据此类型进行相应的业务处理。
嗯那,就是这样,下面直接放出代码:
package com.materialdesignstudy.complexrecycler.itemone;import android.content.Context;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.ViewGroup;import com.materialdesignstudy.R;import java.util.ArrayList;import java.util.List;/** * Created by HLQ on 2017/10/22 */public class OneAdapter extends RecyclerView.Adapter { private LayoutInflater mLayoutInflater; private List mDataList = new ArrayList<>(); public OneAdapter(Context context) { this.mLayoutInflater = LayoutInflater.from(context); } public void addList(List dataModelList) { this.mDataList.addAll(dataModelList); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // 通过获取不同的viewType,返回对应的ViewHolder以及引入的布局文件进行渲染,从而实现复杂布局实现 switch (viewType) { case DataModel.TYPE_ONE: return new TypeOneViewHolder(mLayoutInflater.inflate(R.layout.item_type_one, parent, false)); case DataModel.TYPE_TWO: return new TypeTwoViewHolder(mLayoutInflater.inflate(R.layout.item_type_two, parent, false)); case DataModel.TYPE_THREE: return new TypeThreeViewHolder(mLayoutInflater.inflate(R.layout.item_type_three, parent, false)); } return null; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { // 由于子类ViewHolder继承自TypeAbstractViewHolder,这里可以直接通过强制转化去绑定数据 // 这也是我们为什么要对ViewHolder进行封装的一点优势 ((TypeAbstractViewHolder) holder).bindHolder(mDataList.get(position)); } @Override public int getItemCount() { return mDataList.size(); } @Override public int getItemViewType(int position) { return mDataList.get(position).type; } }
到现在为止,我们已经完成了通过RecyclerView实现复杂布局,不信你可以运行下你现在的代码喽~
二、使用RecyclerView实现ListView以及GirdView混排效果
本小节内容主要关注于LayoutManager的setSpanSizeLookup()方法,下面对其进行简述。
> setSpanSizeLookup(): 主要使用这个方法来展示不同的 item 屏幕跨度
>
> * spanCount:在创建 GridLayoutManager 对象的时候构造方法需要传入这个参数,也就是设置每行排列 item 个数。
><br/>
> * spanSize:在 setSpanSizeLookup() 方法中,这个方法返回的是当前位置的 item 跨度大小。
>
嗯哼,简单了解后,我们撸码呗`
package com.materialdesignstudy.complexrecycler.itemtwo;import android.graphics.Rect;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.GridLayoutManager;import android.support.v7.widget.RecyclerView;import android.view.View;import com.materialdesignstudy.R;import com.materialdesignstudy.complexrecycler.itemone.DataModel;import com.materialdesignstudy.complexrecycler.itemone.OneAdapter;import java.util.ArrayList;import java.util.List;/** * 使用RecyclerView实现ListView+GridView混排效果 * create by heliquan at 2017年10月23日 * 重点关注setSpanSizeLookup即可 获取当前跨度 */public class ComplexTwoActivity extends AppCompatActivity { private RecyclerView mRecyclerView; private OneAdapter mOneAdapter; private int mColor[] = {android.R.color.holo_red_light, android.R.color.holo_green_light, android.R.color.holo_blue_light}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_complec_one); initItem(); initData(); } private void initData() { List dataList = new ArrayList<>(); for (int i = 0; i < 30; i++) { // 模拟不同itemType int type; if (i 15 && i < 20)) { type = 1; } else if (i 20) { type = 2; } else { type = 3; } DataModel dataModel = new DataModel(); dataModel.avatarColor = mColor[type - 1]; dataModel.type = type; dataModel.name = "name" + i; dataModel.content = "content:" + i; dataModel.contentColor = mColor[(type + 1) % 3]; dataList.add(dataModel); } mOneAdapter.addList(dataList); mOneAdapter.notifyDataSetChanged(); } private void initItem() { mRecyclerView = findViewById(R.id.id_recy); // 更换显示布局样式为GirdLayoutManager final GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2); gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { // 获取当前位置下的ItemViewType int type = mRecyclerView.getAdapter().getItemViewType(position); // 由于item1和item2可以正常显示 而item3需要横跨2列 // 所以需要在这里对item3进行单独处理 if (type == DataModel.TYPE_THREE) { return gridLayoutManager.getSpanCount(); // item3需要横跨2列 } else { return 1; } } }); mRecyclerView.setLayoutManager(gridLayoutManager); mOneAdapter = new OneAdapter(this); mRecyclerView.setAdapter(mOneAdapter); // 为了显示效果更好 在此添加分割线 mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { GridLayoutManager.LayoutParams layoutParams = (GridLayoutManager.LayoutParams) view.getLayoutParams(); int spanSize = layoutParams.getSpanSize(); // 获取当前位置的 item 跨度大小 int spanIndex = layoutParams.getSpanIndex(); // 获取每行排列 item 个数 outRect.top = 20; // 如果当前跨度不等于当前索引 表示当前不属于item3 if (spanSize != gridLayoutManager.getSpanCount()) { // 针对item1,item2做分割线处理 if (spanIndex == 1) { outRect.left = 10; } else { outRect.right = 10; } } } }); } }
其实这部分的关键点就是在于那个方法,大家有时间可以细致了解。
好了,到现在为止,第二小节也over了。Nice。
三、复杂Model应对之策
以上俩点都是基于相同的Model,那么在实际开发中,我们Model也许各不相同,那么这时候该如何处理呢?
这里为大家提供下思路,可以顺着延伸下。
改造ViewHolder封装类,之前我们直接采用固定Model,而今替换T即可,而对应子类ViewHolder继承时需要指定Model类型;
Adapter中却需要记录当前itemType对应包含List.size()以及itemType对应类型。原因在于我们需要将数据进行组合,因为后台的数据不可能会按照我们所想的来,只能进行二次拼接。
关键内容如上,下面就简单贴出关键代码,完整代码请在下方查看GitHub。
3.1 改造后的ViewHolder封装类
package com.materialdesignstudy.complexrecycler.itemthree;import android.support.v7.widget.RecyclerView;import android.view.View;import com.materialdesignstudy.complexrecycler.itemone.DataModel;/** * Created by HLQ on 2017/10/22 */public abstract class TypeAbstractViewHolder extends RecyclerView.ViewHolder { public TypeAbstractViewHolder(View itemView) { super(itemView); } public abstract void bindHolder(T dataModel); }
3.2 改造后的数据初始化
private void initData() { List oneList = new ArrayList<>(); for (int i = 0; i < 10; i++) { DataModelOne dataModel = new DataModelOne(); dataModel.name = "name" + i; dataModel.avatatColor = mColor[0]; oneList.add(dataModel); } List twoList = new ArrayList<>(); for (int i = 0; i < 10; i++) { DataModelTwo dataModel = new DataModelTwo(); dataModel.name = "name" + i; dataModel.avatatColor = mColor[1]; dataModel.content = "content:" + i; twoList.add(dataModel); } List threeList = new ArrayList<>(); for (int i = 0; i < 10; i++) { DataModelThree dataModel = new DataModelThree(); dataModel.name = "name" + i; dataModel.avatatColor = mColor[2]; dataModel.content = "content:" + i; dataModel.contentColor = mColor[2]; threeList.add(dataModel); } mOneAdapter.addList(oneList,twoList,threeList); mOneAdapter.notifyDataSetChanged(); }
3.3 改造后的Adapter
package com.materialdesignstudy.complexrecycler.itemthree;import android.content.Context;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.ViewGroup;import com.materialdesignstudy.R;import com.materialdesignstudy.complexrecycler.itemone.DataModel;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/** * Created by HLQ on 2017/10/22 */public class TwoAdapter extends RecyclerView.Adapter { public static final int TYPE_ONE = 1; public static final int TYPE_TWO = 2; public static final int TYPE_THREE = 3; private LayoutInflater mLayoutInflater; private List mTypes = new ArrayList<>(); // 存放type private Map mPositions = new HashMap<>(); // 存放type下包含数据itemCount private List mOneList = new ArrayList<>(); private List mTwoList = new ArrayList<>(); private List mThreeList = new ArrayList<>(); public TwoAdapter(Context context) { this.mLayoutInflater = LayoutInflater.from(context); } public void addList(List oneList, List twoList, List threeList) { addListByType(TYPE_ONE, oneList); addListByType(TYPE_TWO, twoList); addListByType(TYPE_THREE, threeList); this.mOneList = oneList; this.mTwoList = twoList; this.mThreeList = threeList; } private void addListByType(int type, List list) { mPositions.put(type, mTypes.size()); for (int i = 0; i < list.size(); i++) { mTypes.add(type); } } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { case DataModel.TYPE_ONE: return new TypeOneViewHolder(mLayoutInflater.inflate(R.layout.item_type_one, parent, false)); case DataModel.TYPE_TWO: return new TypeTwoViewHolder(mLayoutInflater.inflate(R.layout.item_type_two, parent, false)); case DataModel.TYPE_THREE: return new TypeThreeViewHolder(mLayoutInflater.inflate(R.layout.item_type_three, parent, false)); } return null; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { int viewType = getItemViewType(position); int realPosition = position - mPositions.get(viewType); switch (viewType) { case DataModel.TYPE_ONE: ((TypeAbstractViewHolder) holder).bindHolder(mOneList.get(realPosition)); break; case DataModel.TYPE_TWO: ((TypeAbstractViewHolder) holder).bindHolder(mTwoList.get(realPosition)); break; case DataModel.TYPE_THREE: ((TypeAbstractViewHolder) holder).bindHolder(mThreeList.get(realPosition)); break; } } @Override public int getItemCount() { return mTypes.size(); } @Override public int getItemViewType(int position) { return mTypes.get(position); } }
到此,RecyclerView实现复杂布局结束了,还是希望大家有时间多看多敲,多去理解。
GitHub地址
> https://github.com/HLQ-Struggle/MaterialDesignStudy
学习地址
> 泥阿布慕课网视频地址:http://www.imooc.com/learn/731;
>
> 废大半天劲儿找到的GitHub地址:https://github.com/nimengbo
>
> 图片地址:http://www.jianshu.com/p/29465cce1131
结束
我们沿着前人的路慢慢前行,只要坚持,就会有收获,可能目前的现状有些不尽人意,但是只要坚持下去,希望的曙光始终会照亮前行的路~
致自己,致坚持看完本文的你~加油~