今天我们一起探讨一下通过RecyclerView实现二级联动,在这里我做的是仿京东的分类页面,京东的分类页面是一个非常经典的项目,今天我们就来写一下.
GitHub源码地址:https://github.com/BnerFang/Day_1219
首先,第一步:搭建环境(依赖和权限)
在这里首先看一下所需依赖:在这里图片的记载我使用的是Glide
//圆角依赖
implementation 'de.hdodenhof:circleimageview:2.0.0'
//Gson依赖
implementation 'com.google.code.gson:gson:2.2.2'
//glide图片加载库
implementation 'com.github.bumptech.glide:glide:4.8.0'
//guava依赖
implementation 'com.google.guava:guava:16.0.1'
//okhttp3依赖
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
//拦截器
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
//Recyclerview依赖
implementation 'com.android.support:recyclerview-v7:28.0.0'
权限添加:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
环境搭建完毕开始上代码了
注意:图片标红部位代码相同,只需要更改 api 和 bean类就行,在这里我就只贴一个mvp的分层了
封装一个联网工具类 utils OkHttpUtil
public class OkHttpUtil {
private static volatile OkHttpUtil mInstance;
private final OkHttpClient mClient;
private OkHttpListener mOkHttpListener;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0://成功
String data = (String) msg.obj;
mOkHttpListener.OkHttpSuccess(data);
break;
case 1://失败
String error = (String) msg.obj;
mOkHttpListener.OkHttpError(error);
break;
}
}
};
//生成 OkHttpListener set方法
public void setOkHttpListener(OkHttpListener okHttpListener) {
mOkHttpListener = okHttpListener;
}
/**
* 第一步,写一个单例,这里用的懒汉式,也可以使用饿汉
* @return
*/
public static OkHttpUtil getInstance() {
if (mInstance == null) {
synchronized (OkHttpUtil.class) {
if (null == mInstance) {
mInstance = new OkHttpUtil();
}
}
}
return mInstance;
}
/**
* 完成构造方法,OkHttpClient
*/
public OkHttpUtil() {
//日志拦截器
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//使用构造者模式
mClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)//设置连接超时
.readTimeout(10, TimeUnit.SECONDS)//读取超时
.writeTimeout(10, TimeUnit.SECONDS)//写超时
.addInterceptor(interceptor)//添加拦截器
.build();
}
/**
* 异步set请求
* @param url
* @return
*/
public OkHttpUtil OkHttpGet(String url){
//获取clench对象
OkHttpClient okHttpClient = new OkHttpClient();
//获取Request对象
Request request = new Request.Builder().url(url).build();
okHttpClient.newCall(request).enqueue(new Callback() {
/**
* 失败回调方法
* @param call
* @param e
*/
@Override
public void onFailure(Call call, IOException e) {
Message message = new Message();
message.what = 1;
message.obj = e.getMessage();
mHandler.sendMessage(message);
}
/**
* 成功回调方法
* @param call
* @param response
* @throws IOException
*/
@Override
public void onResponse(Call call, Response response) throws IOException {
Message message = new Message();
message.what = 0;
message.obj = response.body().string();
mHandler.sendMessage(message);
}
});
return this;
}
/**
* 异步Post请求
* @param url
* @param formBody
* @return
*/
public OkHttpUtil OkHttoPost(String url, FormBody formBody){
//创建 clench 对象
OkHttpClient okHttpClient = new OkHttpClient();
//获取Request
Request request = new Request.Builder().url(url).post(formBody).build();
okHttpClient.newCall(request).enqueue(new Callback() {
/**
* 失败回调方法
* @param call
* @param e
*/
@Override
public void onFailure(Call call, IOException e) {
Message message = new Message();
message.what = 1;
message.obj = e.getMessage();
mHandler.sendMessage(message);
}
/**
* 成功回调方法
* @param call
* @param response
* @throws IOException
*/
@Override
public void onResponse(Call call, Response response) throws IOException {
Message message = new Message();
message.what = 0;
message.obj = response.body().string();
mHandler.sendMessage(message);
}
});
return this;
}
//定义一个接口,进行回调
public interface OkHttpListener{
//成功
void OkHttpSuccess(String data);
//失败
void OkHttpError(String error);
}
}
bean类 LeftBean
public class LeftBean {
private String msg;
private String code;
private List<DataBean> data;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public List<DataBean> getData() {
return data;
}
public void setData(List<DataBean> data) {
this.data = data;
}
public static class DataBean {
private int cid;
private String createtime;
private String icon;
private int ishome;
private String name;
private boolean isClick = false;
public boolean isClick() {
return isClick;
}
public void setClick(boolean click) {
isClick = click;
}
public int getCid() {
return cid;
}
public void setCid(int cid) {
this.cid = cid;
}
public String getCreatetime() {
return createtime;
}
public void setCreatetime(String createtime) {
this.createtime = createtime;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public int getIshome() {
return ishome;
}
public void setIshome(int ishome) {
this.ishome = ishome;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
CallBack接口 MyLeftCallBack
public interface MyLeftCallBack {
void onSuccess(List<LeftBean.DataBean> dataBeans);
void onFailed(String error);
}
view层接口 LeftView
public interface LeftView {
void onLeftSuccess(List<LeftBean.DataBean> dataBeans);
void onLeftFailed(String error);
}
model层 LeftModel
public class LeftModel {
public void leftData(List<LeftBean.DataBean> dataBeans, final MyLeftCallBack callBack) {
new OkHttpUtil().OkHttpGet(Apis.LEFT_URL).setOkHttpListener(new OkHttpUtil.OkHttpListener() {
@Override
public void OkHttpSuccess(String data) {
Gson gson = new Gson();
LeftBean leftBean = gson.fromJson(data, LeftBean.class);
List<LeftBean.DataBean> beans = leftBean.getData();
if (leftBean.getCode().equals("0")) {
callBack.onSuccess(beans);
} else {
callBack.onFailed(leftBean.getMsg());
}
}
@Override
public void OkHttpError(String error) {
callBack.onFailed(error);
}
});
}
}
RightModel
public class RightModel {
public void rightData(List<RightBean.DataBean> list, int uid, final MyRightCallBack callBack) {
new OkHttpUtil().OkHttpGet(Apis.RIGHT_URL + uid).setOkHttpListener(new OkHttpUtil.OkHttpListener() {
@Override
public void OkHttpSuccess(String data) {
Gson gson = new Gson();
RightBean rightBean = gson.fromJson(data, RightBean.class);
List<RightBean.DataBean> beans = rightBean.getData();
if (rightBean.getCode().equals("0")) {
callBack.onSuccesss(beans);
} else {
callBack.onFaileds(rightBean.getMsg());
}
}
@Override
public void OkHttpError(String error) {
callBack.onFaileds(error);
}
});
}
}
presenter层 用于view层和model层的交互 LeftPresenter
public class LeftPresenter {
private LeftView mLeftView;
private LeftModel mLeftModel;
public LeftPresenter(LeftView leftView) {
mLeftView = leftView;
mLeftModel = new LeftModel();
}
//解绑
public void datechView() {
mLeftView = null;
}
public void leftDatas(List<LeftBean.DataBean> dataBeans) {
mLeftModel.leftData(dataBeans, new MyLeftCallBack() {
@Override
public void onSuccess(List<LeftBean.DataBean> dataBeans) {
mLeftView.onLeftSuccess(dataBeans);
}
@Override
public void onFailed(String error) {
mLeftView.onLeftFailed(error);
}
});
}
}
适配器adapter MyLeftAdapter
public class MyLeftAdapter extends RecyclerView.Adapter<MyLeftAdapter.MyViewHolder> {
private Context mContext;
private List<LeftBean.DataBean> mDataBeans = new ArrayList<>();
public MyLeftAdapter(Context context, List<LeftBean.DataBean> dataBeans) {
mContext = context;
mDataBeans = dataBeans;
}
//更新适配器
public void update(List<LeftBean.DataBean> dataBeans) {
this.mDataBeans = dataBeans;
notifyDataSetChanged();
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
//布局
View view = LayoutInflater.from(mContext).inflate(R.layout.left_layout_view, viewGroup, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final MyViewHolder myViewHolder, final int i) {
myViewHolder.mTextViewTitle.setText(mDataBeans.get(i).getName());
myViewHolder.mTextViewTitle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mLeftCheckListener.onItemClick(i);
Toast.makeText(mContext, "点击了:" + mDataBeans.get(i).getName(), Toast.LENGTH_SHORT).show();
}
});
}
@Override
public int getItemCount() {
return mDataBeans == null ? 0 : mDataBeans.size();
}
//自定义viewholder
class MyViewHolder extends RecyclerView.ViewHolder {
TextView mTextViewTitle;
LinearLayout mLinearLayout;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
mTextViewTitle = itemView.findViewById(R.id.left_txt_title);
mLinearLayout = itemView.findViewById(R.id.left_layout);
}
}
private LeftCheckListener mLeftCheckListener;
public void setLeftCheckListener(LeftCheckListener leftCheckListener) {
mLeftCheckListener = leftCheckListener;
}
//自定义接口
public interface LeftCheckListener {
void onItemClick(int position);
}
}
MyRightAdapter
public class MyRightAdapter extends RecyclerView.Adapter<MyRightAdapter.MyRightViewHolder> {
private Context mContext;
private List<RightBean.DataBean> mDataBeanList = new ArrayList<>();
private MyGoodsRightAdapter mMyGoodsRightAdapter;
public MyRightAdapter(Context context, List<RightBean.DataBean> dataBeans) {
mContext = context;
mDataBeanList = dataBeans;
}
//更新适配器
public void update(List<RightBean.DataBean> dataBeans){
this.mDataBeanList = dataBeans;
notifyDataSetChanged();
}
@NonNull
@Override
public MyRightViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
//布局
View view = LayoutInflater.from(mContext).inflate(R.layout.right_layout_view, viewGroup, false);
return new MyRightViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyRightViewHolder myRightViewHolder, int i) {
myRightViewHolder.mTextViewTitle.setText(mDataBeanList.get(i).getName());
List<RightBean.DataBean.ListBean> list = mDataBeanList.get(i).getList();
//添加子布局适配器
mMyGoodsRightAdapter = new MyGoodsRightAdapter(mContext, list);
myRightViewHolder.mRecyclerView.setLayoutManager(new GridLayoutManager(mContext,3));//网格布局
myRightViewHolder.mRecyclerView.setAdapter(mMyGoodsRightAdapter);
mMyGoodsRightAdapter.setData(list);
}
@Override
public int getItemCount() {
return mDataBeanList == null ? 0 : mDataBeanList.size();
}
//自定义viewholder
class MyRightViewHolder extends RecyclerView.ViewHolder{
TextView mTextViewTitle;
RecyclerView mRecyclerView;
public MyRightViewHolder(@NonNull View itemView) {
super(itemView);
mRecyclerView = itemView.findViewById(R.id.right_rv);
mTextViewTitle = itemView.findViewById(R.id.right_txt_title);
}
}
}
MyGoodsRightAdapter
public class MyGoodsRightAdapter extends RecyclerView.Adapter<MyGoodsRightAdapter.MyGoodsViewHolder> {
private Context mContext;
private List<RightBean.DataBean.ListBean> mListBeans = new ArrayList<>();
public MyGoodsRightAdapter(Context context, List<RightBean.DataBean.ListBean> listBeans) {
mContext = context;
mListBeans = listBeans;
}
//更新适配器
public void setData(List<RightBean.DataBean.ListBean> listBeans){
mListBeans = listBeans;
notifyDataSetChanged();
}
@NonNull
@Override
public MyGoodsViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
//布局
View view = LayoutInflater.from(mContext).inflate(R.layout.right_goods_view, viewGroup, false);
return new MyGoodsViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyGoodsViewHolder myGoodsViewHolder, int i) {
Glide.with(mContext).load(mListBeans.get(i).getIcon()).into(myGoodsViewHolder.mCircleImageViewImg);
myGoodsViewHolder.mTextViewName.setText(mListBeans.get(i).getName());
}
@Override
public int getItemCount() {
return mListBeans == null ? 0 : mListBeans.size();
}
//自定义viewholder
class MyGoodsViewHolder extends RecyclerView.ViewHolder{
CircleImageView mCircleImageViewImg;
TextView mTextViewName;
public MyGoodsViewHolder(@NonNull View itemView) {
super(itemView);
mCircleImageViewImg = itemView.findViewById(R.id.goods_c_imgicon);
mTextViewName = itemView.findViewById(R.id.goods_right_txt_name);
}
}
}
接下来是MainActivity代码 MainActivity
public class MainActivity extends AppCompatActivity implements LeftView, RightView {
private RecyclerView mLeftRv;
private RecyclerView mRightRv;
private MyLeftAdapter mMyLeftAdapter;
private LeftPresenter mLeftPresenter;
private List<LeftBean.DataBean> mDataBeans = new ArrayList<>();
private List<RightBean.DataBean> mDataBeanList = new ArrayList<>();
private MyRightAdapter mMyRightAdapter;
private RightPresenter mRightPresenter;
private int mCid;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化控件
initView();
}
//初始化控件
private void initView() {
mLeftRv = (RecyclerView) findViewById(R.id.left_rv_view);
mRightRv = (RecyclerView) findViewById(R.id.right_rv_view);
mLeftPresenter = new LeftPresenter(this);//左侧 presenter层
mLeftPresenter.leftDatas(mDataBeans);
mRightPresenter = new RightPresenter(this);//右侧 presenter层
mRightPresenter.rightDatas(mDataBeanList, 1);// uid 默认值为 1
//线性布局管理器
mLeftRv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
//调用系统自带分割线
mLeftRv.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
//线性布局管理器
mRightRv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
//调用系统自带分割线
mRightRv.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
}
//左侧 访问成功接口回调
@Override
public void onLeftSuccess(List<LeftBean.DataBean> dataBeans) {
// TODO: 2018/12/19 必须添加,否则空指针
mDataBeans = dataBeans;
mMyLeftAdapter = new MyLeftAdapter(this, dataBeans);
mLeftRv.setAdapter(mMyLeftAdapter);
mMyLeftAdapter.update(mDataBeans);
mMyLeftAdapter.notifyDataSetChanged();
}
//左侧 访问失败接口回调
@Override
public void onLeftFailed(String error) {
Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
}
//右侧 访问成功接口回调
@Override
public void onRightSuccess(final List<RightBean.DataBean> list) {
// TODO: 2018/12/19 必须添加,否则空指针
mDataBeanList = list;
mMyRightAdapter = new MyRightAdapter(this, list);
mRightRv.setAdapter(mMyRightAdapter);
mMyRightAdapter.update(mDataBeanList);
mMyRightAdapter.notifyDataSetChanged();
mMyLeftAdapter.setLeftCheckListener(new MyLeftAdapter.LeftCheckListener() {
@Override
public void onItemClick(int position) {
for (int i = 0; i < mDataBeans.size(); i++) {
mDataBeans.get(i).setClick(true);//默认选中第一个
mCid = mDataBeans.get(position).getCid();//获取每次点击的 cid
mRightPresenter.rightDatas(mDataBeanList, mCid);//每次点击获取cid 传给相应的接口相应
}
mMyLeftAdapter.notifyDataSetChanged();//刷新适配器
}
});
}
//右侧 访问失败接口回调
@Override
public void onRightFailed(String error) {
Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
}
//防止内存泄露
@Override
protected void onDestroy() {
super.onDestroy();
mLeftPresenter.datechView();
mRightPresenter.datechView();
}
}
接下来是布局文件 main_activity
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/left_rv_view"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3" />
<android.support.v7.widget.RecyclerView
android:id="@+id/right_rv_view"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="7" />
</LinearLayout>
左侧RecycleView条目布局 left_layout_view
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:id="@+id/left_layout"
android:orientation="vertical">
<TextView
android:id="@+id/left_txt_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="XXXXXXX"
android:textSize="24sp"/>
</LinearLayout>
右侧RecycleView条目布局 right_layout_view
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/right_txt_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="36sp"
android:text="XXXXXXXXXXX"
android:gravity="center"
android:textColor="@color/colorW"
android:background="@color/colorA"
android:layout_margin="5dp"/>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/right_rv"/>
</LinearLayout>
右侧子RecyclerView条目布局 right_goods_view
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="vertical">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/goods_c_imgicon"
android:layout_width="65dp"
android:layout_height="65dp"
android:layout_margin="5dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/goods_right_txt_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/goods_c_imgicon"
android:layout_margin="5dp"
android:gravity="center"
android:text="XXXXX"
android:textColor="#999999"
android:textSize="16sp" />
</LinearLayout>
到此,一个仿京东分类项目就做完了