本文目标
阅读完本文,你会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 个数。
>
> * 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 改造后的Adapterpackage 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地址
学习地址
结束
我们沿着前人的路慢慢前行,只要坚持,就会有收获,可能目前的现状有些不尽人意,但是只要坚持下去,希望的曙光始终会照亮前行的路~
致自己,致坚持看完本文的你~加油~