前面一篇介绍了RecyclerView的下拉刷新和上拉加载,今天介绍非树状列表的展开和隐藏功能。数据是在同一级列表返回。
项目说明:
一,使用的AndroidStudio版本为3.2.1,gradle-4.6
二,使用的RecyclerView的adapter是BaseRecyclerViewAdapterHelper,github上对应的地址如下
github地址为:https://github.com/CymChad/BaseRecyclerViewAdapterHelper
对应的简书说明地址为:https://www.jianshu.com/p/b343fcff51b0
展示效果:
为什么写这篇简书的说明和坑点:
一,这其实是一个很小的功能点,但是这其中还是有很多坑,所以写这篇简书来记录和分享给其他需要的人。
二,列表数据超过10条的时候,点击第一条的数据,会造成第11条数据也会有相应的改变。
三,列表加载了数据,在向上滑动的过程中,明明是展开的状态,但是他会因为列表的重用导致数据错乱,会重新显示隐藏状态。
现在正式开始
1,基本配置如依赖包,下拉刷新功能等在RecyclerView系列(二)中已经说明,不做描述,重点说坑点以及实现方法。
2,一般在实现每一个item的点击事件的时候使用的方法如下:在对应的activity中使用对应的adapter实现点击事件(如下),这样做的弊端是拿不到每一个你所点击的item所对应的控件,而且传参中View view对应的view也无法拿到外部view对应的id,从而无法控制外部view的显示和隐藏。
//6.1,给recyclerView的每一个子列表添加点击事件
// mTestAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
//// @Override
//// public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
//// Toast.makeText(MainActivity.this, "我点击了第" + position + "个子view",
//// Toast.LENGTH_SHORT).show();
//// }
//// });
3,正确做法是如下中6.2所示,使用对应的recyclerview实现点击事件,点击事件中更新实例中自定义的isShow参数的状态,这个isShow是对应到每个item的,所以不会串。然后 mTestAdapter.notifyDataSetChanged();更新数据。对应的适配器中就会刷新数据。
package com.mumu.jsrecyclerview2;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.listener.OnItemChildClickListener;
import com.scwang.smartrefresh.layout.SmartRefreshLayout;
import com.scwang.smartrefresh.layout.api.RefreshLayout;
import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.rv_test)
RecyclerView rvTest;
@BindView(R.id.srl_test)
SmartRefreshLayout srlTest;
private TestAdapter mTestAdapter;
private ArrayList<TestEntity.ResultBean.ListBean> mTestList;
private Unbinder unbinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
unbinder = ButterKnife.bind(this);
initView();
}
private void initView() {
//初始化的时候默认没有数据,显示空的布局
getData(1);
refreshView();
smartRefreshView();
}
@Override
protected void onDestroy() {
super.onDestroy();
unbinder.unbind();
}
/**
* 刷新消息列表
*/
private void refreshView() {
//1,加载空布局文件,便于第五步适配器在没有数据的时候加载
View emptyView = View.inflate(this, R.layout.empty_view, null);
//2,设置LayoutManager,LinearLayoutManager表示竖直向下
rvTest.setLayoutManager(new LinearLayoutManager(this));
//3,初始化一个无数据的适配器
mTestAdapter = new TestAdapter();
//4,绑定recyclerView和适配器
rvTest.setAdapter(mTestAdapter);
//5,给recyclerView设置空布局
mTestAdapter.setEmptyView(emptyView);
//6.1,给recyclerView的每一个子列表添加点击事件
// mTestAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
//// @Override
//// public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
//// Toast.makeText(MainActivity.this, "我点击了第" + position + "个子view",
//// Toast.LENGTH_SHORT).show();
//// }
//// });
//6.2,给recyclerView的每一个子列表添加点击事件
// TODO: 2019/1/10 这不能使用上面6.1的点击事件的方法,使用以上方法会导致点击混乱,滑动导致已经展开的item隐藏
rvTest.addOnItemTouchListener(new OnItemChildClickListener() {
@Override
public void onSimpleItemChildClick(BaseQuickAdapter adapter, View view, int position) {
switch (view.getId()) {
case R.id.rl_item_title:
// TODO: 2019/1/11 1,点击按钮的时候更新在实例中对应的isShow数据
if (mTestList.get(position).isShow()) {
mTestList.get(position).setShow(false);
Toast.makeText(MainActivity.this,"第"+position+"个隐藏",Toast.LENGTH_SHORT).show();
} else {
mTestList.get(position).setShow(true);
Toast.makeText(MainActivity.this,"第"+position+"个展开",Toast.LENGTH_SHORT).show();
}
// TODO: 2019/1/11,2,适配器更新数据,刷新列表,更新状态
mTestAdapter.notifyDataSetChanged();
break;
default:
break;
}
}
});
}
/**
* MainActivity中增加下拉刷新和上拉加载的监听方法
*/
private void smartRefreshView() {
srlTest.setOnRefreshLoadMoreListener(new OnRefreshLoadMoreListener() {
@Override
public void onRefresh(@NonNull RefreshLayout refreshLayout) {
//下拉刷新,一般添加调用接口获取数据的方法
getData(2);
refreshLayout.finishRefresh();
}
@Override
public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
//上拉加载,一般添加调用接口获取更多数据的方法
getData(3);
refreshLayout.finishLoadMoreWithNoMoreData();
}
});
}
/**
* 获取数据的方法
* 该方法纯属展示各种效果,实际应用时候请自己根据需求做判断即可
*
* @param mode 模式:1为刚开始进来加载数据 空数据 2为下拉刷新 3为上拉加载
*/
private void getData(int mode) {
//添加临时数据,一般直接从接口获取
switch (mode) {
case 1:
break;
case 2:
mTestList = new ArrayList<>();
for (int i = 0; i < 15; i++) {
mTestList.add(new TestEntity.ResultBean.ListBean("我有一个小狗", "我有一个小狗我有一个小狗我有一个小狗我有一个小狗我有一个小狗"));
}
//更新数据
mTestAdapter.setNewData(mTestList);
break;
case 3:
for (int i = 0; i < 15; i++) {
mTestList.add(new TestEntity.ResultBean.ListBean("我有一个小狗", "我有一个小狗我有一个小狗我有一个小狗我有一个小狗我有一个小狗"));
}
mTestAdapter.setNewData(mTestList);
break;
default:
mTestList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
mTestList.add(new TestEntity.ResultBean.ListBean("我有一个小狗", "我有一个小狗我有一个小狗我有一个小狗我有一个小狗我有一个小狗"));
}
break;
}
}
}
4,对应是实例中只增加了一个isShow参数来记录状态
package com.mumu.jsrecyclerview2;
import java.io.Serializable;
import java.util.List;
public class TestEntity implements Serializable {
/**
* Success : true
* StatusCode : 200
* Result : {"list":[{"TestTitle":"我爱小狗","TestNessage":"我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗"},{"TestTitle":"我爱小狗","TestNessage":"我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗"},{"TestTitle":"我爱小狗","TestNessage":"我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗"},{"TestTitle":"我爱小狗","TestNessage":"我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗"},{"TestTitle":"我爱小狗","TestNessage":"我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗"}]}
*/
private ResultBean Result;
public ResultBean getResult() {
return Result;
}
public void setResult(ResultBean Result) {
this.Result = Result;
}
public static class ResultBean {
private List<ListBean> list;
public List<ListBean> getList() {
return list;
}
public void setList(List<ListBean> list) {
this.list = list;
}
public static class ListBean {
/**
* TestTitle : 我爱小狗
* TestMessage : 我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗我爱小狗
* isShow: 是否显示详细消息,后台没有返回,属于自己添加赋值,用来记录每一个item的显示详情状态
*/
private String TestTitle;
private String TestMessage;
private boolean isShow = false;
public boolean isShow() {
return isShow;
}
public void setShow(boolean show) {
isShow = show;
}
public ListBean(String aaa, String bbb) {
TestTitle = aaa;
TestMessage = bbb;
}
public String getTestTitle() {
return TestTitle;
}
public void setTestTitle(String TestTitle) {
this.TestTitle = TestTitle;
}
public String getTestMessage() {
return TestMessage;
}
public void setTestMessage(String TestMessage) {
this.TestMessage = TestMessage;
}
}
}
}
5,对应的适配器。适配器中根据对应data中isShow参数显示或者隐藏view,这还要多注意一点,每个view都需要给他赋值,不然会发生数据没有更新等不正常现象。
package com.mumu.jsrecyclerview2;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
public class TestAdapter extends BaseQuickAdapter<TestEntity.ResultBean.ListBean, BaseViewHolder> {
/**
* 增加一个构造方法,便于没有数据时候初始化适配器
*/
public TestAdapter() {
super(R.layout.item_test);
}
/**
* 继承BaseQuickAdapter后需要重写的方法
*
* @param helper view持有者,为重用view而设计,减少每次创建view的内存消耗
* @param data 我们的列表数据
*/
@Override
protected void convert(BaseViewHolder helper, TestEntity.ResultBean.ListBean data) {
// TODO: 2019/1/10 需要详细说明的点:每一个子控件都要赋值操作,不然会造成数据混乱 ,如没有给箭头赋值,在点击
// TODO: 2019/1/10 展开隐藏的时候箭头的赋值会发生错乱(点击第一个,第十一个数据也会更改)
//将每一个需要赋值的id和对应的数据绑定,绑定title和message
helper.setText(R.id.tv_item_title, data.getTestTitle())
.setText(R.id.tv_item_message, data.getTestMessage());
//设置箭头切换
if(data.isShow()){
helper.setImageResource(R.id.iv_item_arrow, R.mipmap.rw_arrow_1);
}else {
helper.setImageResource(R.id.iv_item_arrow, R.mipmap.rw_arrow_2);
}
//设置详细消息布局隐藏
helper.setGone(R.id.ll_item_message, data.isShow());
//设置title的点击监听
helper.addOnClickListener(R.id.rl_item_title);
}
}
6,对应的适配器的每个子布局文件。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/rl_item_title"
android:layout_width="match_parent"
android:layout_height="48dp">
<TextView
android:id="@+id/tv_item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="16dp"
android:text="--"
android:textColor="#303030" />
<ImageView
android:id="@+id/iv_item_arrow"
android:layout_width="13.3dp"
android:layout_height="8dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical"
android:layout_marginRight="16dp"
android:src="@mipmap/rw_arrow_2" />
</RelativeLayout>
<LinearLayout
android:id="@+id/ll_item_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="15dp"
android:background="#EBEBEB" />
<TextView
android:id="@+id/tv_item_message"
android:layout_width="match_parent"
android:layout_height="60dp"
android:gravity="center_vertical"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="--"
android:textColor="#999999"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="12dp"
android:background="#F6F7F9" />
</LinearLayout>
7,对应github地址
demo地址:https://github.com/mamumu/jsRecyclerView2
如果有发现错误欢迎指正我及时修改,如果有好的建议欢迎留言。如果觉得对你有帮助欢迎给小星星,谢谢。