Android -- RecyclerView(超简单)实现可展开列表


转载地址:

http://blog.csdn.net/chengxu_hou/article/details/70344759



可展开列表,听到这几个字的时候也许你就会问了,为啥要用 RecyclerView,用Android提供的 ExpandableListView不是更好吗?是的,ExpandableListView是很轻松就可以实现,但是,我要告诉你,我就是喜欢用RecyclerView ……

                                                            

   好了,不再废话了,这里只是想用RecyclerView实现ExpandableListView的效果,提供一种实现方法和思路,其实很简单,就一句话,展开--插入一行、收起--删除一行。

    先上效果图,用的模拟器,有点卡,真机测试很流畅,为了便于区分,上了点色,样子有点丑

           

说一下实现思路:

* 列表组成:主要分成两个布局,默认展示的为父布局,点击父布局,在父布局的下方插入一行(子布局);

*展开效果:点击Item,插入一行数据的同时,该item下方的item向下滚动,也就是展开效果;

*动画效果:咳咳…… RecyclerView自带这种渐变的动画效果(知道我为啥喜欢RecyclerView了吧,哈哈),ListView自身是没有这种效果的。

以上也就是简单的实现思路,下面上码。

一大波代码来袭

首先是实体类,用来封装模拟数据

  1. /** 
  2.  * Created by hbh on 2017/4/20. 
  3.  * 实体类,模拟数据 
  4.  */  
  5.   
  6. public class DataBean {  
  7.   
  8.     public static final int PARENT_ITEM = 0;//父布局  
  9.     public static final int CHILD_ITEM = 1;//子布局  
  10.   
  11.     private int type;// 显示类型  
  12.     private boolean isExpand;// 是否展开  
  13.     private DataBean childBean;  
  14.   
  15.     private String ID;  
  16.     private String parentLeftTxt;  
  17.     private String parentRightTxt;  
  18.     private String childLeftTxt;  
  19.     private String childRightTxt;  
  20.   
  21.     public String getParentLeftTxt() {  
  22.         return parentLeftTxt;  
  23.     }  
  24.   
  25.     public void setParentLeftTxt(String parentLeftTxt) {  
  26.         this.parentLeftTxt = parentLeftTxt;  
  27.     }  
  28.   
  29.     public String getChildRightTxt() {  
  30.         return childRightTxt;  
  31.     }  
  32.   
  33.     public void setChildRightTxt(String childRightTxt) {  
  34.         this.childRightTxt = childRightTxt;  
  35.     }  
  36.   
  37.     public String getChildLeftTxt() {  
  38.         return childLeftTxt;  
  39.     }  
  40.   
  41.     public void setChildLeftTxt(String childLeftTxt) {  
  42.         this.childLeftTxt = childLeftTxt;  
  43.     }  
  44.   
  45.     public String getParentRightTxt() {  
  46.         return parentRightTxt;  
  47.     }  
  48.   
  49.     public void setParentRightTxt(String parentRightTxt) {  
  50.         this.parentRightTxt = parentRightTxt;  
  51.     }  
  52.   
  53.     public int getType() {  
  54.         return type;  
  55.     }  
  56.   
  57.     public void setType(int type) {  
  58.         this.type = type;  
  59.     }  
  60.   
  61.     public boolean isExpand() {  
  62.         return isExpand;  
  63.     }  
  64.   
  65.     public void setExpand(boolean expand) {  
  66.         isExpand = expand;  
  67.     }  
  68.   
  69.     public DataBean getChildBean() {  
  70.         return childBean;  
  71.     }  
  72.   
  73.     public void setChildBean(DataBean childBean) {  
  74.         this.childBean = childBean;  
  75.     }  
  76.   
  77.     public String getID() {  
  78.         return ID;  
  79.     }  
  80.   
  81.     public void setID(String ID) {  
  82.         this.ID = ID;  
  83.     }  
  84. }  


activity_main.xml文件中只有一个RecyclerView控件
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:tools="http://schemas.android.com/tools"  
  4.     android:id="@+id/activity_main"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.     tools:context="com.hbh.cl.expandrecyclerviewdemo.MainActivity">  
  8.   
  9.     <android.support.v7.widget.RecyclerView  
  10.         android:id="@+id/recycle_view"  
  11.         android:layout_width="match_parent"  
  12.         android:layout_height="wrap_content"  
  13.         android:scrollbars="vertical"  
  14.         android:background="@color/white"  
  15.         android:paddingTop="5dp">  
  16.     </android.support.v7.widget.RecyclerView>  
  17.   
  18. </RelativeLayout>  


MainActivity文件也是没啥东西,只有一点要注意的,就是添加滚动监听,滚动监听的接口在Adapter中定义
  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.     private RecyclerView mRecyclerView;  
  4.     private List<DataBean> dataBeanList;  
  5.     private DataBean dataBean;  
  6.     private RecyclerAdapter mAdapter;  
  7.   
  8.     @Override  
  9.     protected void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         setContentView(R.layout.activity_main);  
  12.         mRecyclerView = (RecyclerView) findViewById(R.id.recycle_view);  
  13.         initData();  
  14.     }  
  15.   
  16.     /** 
  17.      * 模拟数据 
  18.      */  
  19.     private void initData(){  
  20.         dataBeanList = new ArrayList<>();  
  21.         for (int i = 1; i <= 50; i++) {  
  22.             dataBean = new DataBean();  
  23.             dataBean.setID(i+"");  
  24.             dataBean.setType(0);  
  25.             dataBean.setParentLeftTxt("父--"+i);  
  26.             dataBean.setParentRightTxt("父内容--"+i);  
  27.             dataBean.setChildLeftTxt("子--"+i);  
  28.             dataBean.setChildRightTxt("子内容--"+i);  
  29.             dataBean.setChildBean(dataBean);  
  30.             dataBeanList.add(dataBean);  
  31.         }  
  32.         setData();  
  33.     }  
  34.   
  35.     private void setData(){  
  36.         mRecyclerView.setLayoutManager(new LinearLayoutManager(this));  
  37.         mAdapter = new RecyclerAdapter(this,dataBeanList);  
  38.         mRecyclerView.setAdapter(mAdapter);  
  39.         //滚动监听  
  40.         mAdapter.setOnScrollListener(new RecyclerAdapter.OnScrollListener() {  
  41.             @Override  
  42.             public void scrollTo(int pos) {  
  43.                 mRecyclerView.scrollToPosition(pos);  
  44.             }  
  45.         });  
  46.     }  
  47.   
  48. }  

再有就是自定义Item点击监听接口,一个展开,一个收起
  1. /** 
  2.  * Created by hbh on 2017/4/20. 
  3.  * 父布局Item点击监听接口 
  4.  */  
  5.   
  6. public interface ItemClickListener {  
  7.     /** 
  8.      * 展开子Item 
  9.      * @param bean 
  10.      */  
  11.     void onExpandChildren(DataBean bean);  
  12.   
  13.     /** 
  14.      * 隐藏子Item 
  15.      * @param bean 
  16.      */  
  17.     void onHideChildren(DataBean bean);  
  18. }  


下面就是最主要的Adapter适配器了,主要代码都在这个里面,不过很简单,都有注释,一看就明白
  1. /** 
  2.  * Created by hbh on 2017/4/20. 
  3.  * 适配器 
  4.  */  
  5.   
  6. public class RecyclerAdapter extends RecyclerView.Adapter<BaseViewHolder> {  
  7.   
  8.     private Context context;  
  9.     private List<DataBean> dataBeanList;  
  10.     private LayoutInflater mInflater;  
  11.     private OnScrollListener mOnScrollListener;  
  12.   
  13.     public RecyclerAdapter(Context context, List<DataBean> dataBeanList) {  
  14.         this.context = context;  
  15.         this.dataBeanList = dataBeanList;  
  16.         this.mInflater = LayoutInflater.from(context);  
  17.     }  
  18.   
  19.     @Override  
  20.     public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  21.         View view = null;  
  22.         switch (viewType){  
  23.             case DataBean.PARENT_ITEM:  
  24.                 view = mInflater.inflate(R.layout.recycleview_item_parent, parent, false);  
  25.                 return new ParentViewHolder(context, view);  
  26.             case DataBean.CHILD_ITEM:  
  27.                 view = mInflater.inflate(R.layout.recycleview_item_child, parent, false);  
  28.                 return new ChildViewHolder(context, view);  
  29.             default:  
  30.                 view = mInflater.inflate(R.layout.recycleview_item_parent, parent, false);  
  31.                 return new ParentViewHolder(context, view);  
  32.         }  
  33.     }  
  34.   
  35.     /** 
  36.      * 根据不同的类型绑定View 
  37.      * @param holder 
  38.      * @param position 
  39.      */  
  40.     @Override  
  41.     public void onBindViewHolder(BaseViewHolder holder, int position) {  
  42.         switch (getItemViewType(position)){  
  43.             case DataBean.PARENT_ITEM:  
  44.                 ParentViewHolder parentViewHolder = (ParentViewHolder) holder;  
  45.                 parentViewHolder.bindView(dataBeanList.get(position), position, itemClickListener);  
  46.                 break;  
  47.             case DataBean.CHILD_ITEM:  
  48.                 ChildViewHolder childViewHolder = (ChildViewHolder) holder;  
  49.                 childViewHolder.bindView(dataBeanList.get(position), position);  
  50.                 break;  
  51.         }  
  52.     }  
  53.   
  54.     @Override  
  55.     public int getItemCount() {  
  56.         return dataBeanList.size();  
  57.     }  
  58.   
  59.     @Override  
  60.     public int getItemViewType(int position) {  
  61.         return dataBeanList.get(position).getType();  
  62.     }  
  63.   
  64.     private ItemClickListener itemClickListener = new ItemClickListener() {  
  65.         @Override  
  66.         public void onExpandChildren(DataBean bean) {  
  67.             int position = getCurrentPosition(bean.getID());//确定当前点击的item位置  
  68.             DataBean children = getChildDataBean(bean);//获取要展示的子布局数据对象,注意区分onHideChildren方法中的getChildBean()。  
  69.             if (children == null) {  
  70.                 return;  
  71.             }  
  72.             add(children, position + 1);//在当前的item下方插入  
  73.             if (position == dataBeanList.size() - 2 && mOnScrollListener != null) { //如果点击的item为最后一个  
  74.                 mOnScrollListener.scrollTo(position + 1);//向下滚动,使子布局能够完全展示  
  75.             }  
  76.         }  
  77.   
  78.         @Override  
  79.         public void onHideChildren(DataBean bean) {  
  80.             int position = getCurrentPosition(bean.getID());//确定当前点击的item位置  
  81.             DataBean children = bean.getChildBean();//获取子布局对象  
  82.             if (children == null) {  
  83.                 return;  
  84.             }  
  85.             remove(position + 1);//删除  
  86.             if (mOnScrollListener != null) {  
  87.                 mOnScrollListener.scrollTo(position);  
  88.             }  
  89.         }  
  90.     };  
  91.   
  92.     /** 
  93.      * 在父布局下方插入一条数据 
  94.      * @param bean 
  95.      * @param position 
  96.      */  
  97.     public void add(DataBean bean, int position) {  
  98.         dataBeanList.add(position, bean);  
  99.         notifyItemInserted(position);  
  100.     }  
  101.   
  102.     /** 
  103.      *移除子布局数据 
  104.      * @param position 
  105.      */  
  106.     protected void remove(int position) {  
  107.         dataBeanList.remove(position);  
  108.         notifyItemRemoved(position);  
  109.     }  
  110.   
  111.     /** 
  112.      * 确定当前点击的item位置并返回 
  113.      * @param uuid 
  114.      * @return 
  115.      */  
  116.     protected int getCurrentPosition(String uuid) {  
  117.         for (int i = 0; i < dataBeanList.size(); i++) {  
  118.             if (uuid.equalsIgnoreCase(dataBeanList.get(i).getID())) {  
  119.                 return i;  
  120.             }  
  121.         }  
  122.         return -1;  
  123.     }  
  124.   
  125.     /** 
  126.      * 封装子布局数据对象并返回 
  127.      * 注意,此处只是重新封装一个DataBean对象,为了标注Type为子布局数据,进而展开,展示数据 
  128.      * 要和onHideChildren方法里的getChildBean()区分开来 
  129.      * @param bean 
  130.      * @return 
  131.      */  
  132.     private DataBean getChildDataBean(DataBean bean){  
  133.         DataBean child = new DataBean();  
  134.         child.setType(1);  
  135.         child.setParentLeftTxt(bean.getParentLeftTxt());  
  136.         child.setParentRightTxt(bean.getParentRightTxt());  
  137.         child.setChildLeftTxt(bean.getChildLeftTxt());  
  138.         child.setChildRightTxt(bean.getChildRightTxt());  
  139.         return child;  
  140.     }  
  141.   
  142.     /** 
  143.      * 滚动监听接口 
  144.      */  
  145.     public interface OnScrollListener{  
  146.         void scrollTo(int pos);  
  147.     }  
  148.   
  149.     public void setOnScrollListener(OnScrollListener onScrollListener){  
  150.         this.mOnScrollListener = onScrollListener;  
  151.     }  
  152. }  

还有两个对应的 ParentViewHolder  和  ChildViewHolder

  1. /** 
  2.  * Created by hbh on 2017/4/20. 
  3.  * 父布局ViewHolder 
  4.  */  
  5.   
  6. public class ParentViewHolder extends BaseViewHolder {  
  7.   
  8.     private Context mContext;  
  9.     private View view;  
  10.     private RelativeLayout containerLayout;  
  11.     private TextView parentLeftView;  
  12.     private TextView parentRightView;  
  13.     private ImageView expand;  
  14.     private View parentDashedView;  
  15.   
  16.     public ParentViewHolder(Context context, View itemView) {  
  17.         super(itemView);  
  18.         this.mContext = context;  
  19.         this.view = itemView;  
  20.     }  
  21.   
  22.     public void bindView(final DataBean dataBean, final int pos, final ItemClickListener listener){  
  23.   
  24.         containerLayout = (RelativeLayout) view.findViewById(R.id.container);  
  25.         parentLeftView = (TextView) view.findViewById(R.id.parent_left_text);  
  26.         parentRightView = (TextView) view.findViewById(R.id.parent_right_text);  
  27.         expand = (ImageView) view.findViewById(R.id.expend);  
  28.         parentDashedView = view.findViewById(R.id.parent_dashed_view);  
  29.         RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) expand  
  30.                 .getLayoutParams();  
  31.         expand.setLayoutParams(params);  
  32.         parentLeftView.setText(dataBean.getParentLeftTxt());  
  33.         parentRightView.setText(dataBean.getParentRightTxt());  
  34.   
  35.         if (dataBean.isExpand()) {  
  36.             expand.setRotation(90);  
  37.             parentDashedView.setVisibility(View.INVISIBLE);  
  38.         } else {  
  39.             expand.setRotation(0);  
  40.             parentDashedView.setVisibility(View.VISIBLE);  
  41.         }  
  42.   
  43.         //父布局OnClick监听  
  44.         containerLayout.setOnClickListener(new View.OnClickListener() {  
  45.             @Override  
  46.             public void onClick(View view) {  
  47.                 if (listener != null) {  
  48.                     if (dataBean.isExpand()) {  
  49.                         listener.onHideChildren(dataBean);  
  50.                         parentDashedView.setVisibility(View.VISIBLE);  
  51.                         dataBean.setExpand(false);  
  52.                         rotationExpandIcon(900);  
  53.                     } else {  
  54.                         listener.onExpandChildren(dataBean);  
  55.                         parentDashedView.setVisibility(View.INVISIBLE);  
  56.                         dataBean.setExpand(true);  
  57.                         rotationExpandIcon(090);  
  58.                     }  
  59.                 }  
  60.             }  
  61.         });  
  62.     }  
  63.   
  64.     @TargetApi(Build.VERSION_CODES.HONEYCOMB)  
  65.     private void rotationExpandIcon(float from, float to) {  
  66.         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {  
  67.             ValueAnimator valueAnimator = ValueAnimator.ofFloat(from, to);//属性动画  
  68.             valueAnimator.setDuration(500);  
  69.             valueAnimator.setInterpolator(new DecelerateInterpolator());  
  70.             valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
  71.   
  72.                 @Override  
  73.                 public void onAnimationUpdate(ValueAnimator valueAnimator) {  
  74.                     expand.setRotation((Float) valueAnimator.getAnimatedValue());  
  75.                 }  
  76.             });  
  77.             valueAnimator.start();  
  78.         }  
  79.     }  
  80. }  

  1. /** 
  2.  * Created by hbh on 2017/4/20. 
  3.  * 子布局ViewHolder 
  4.  */  
  5.   
  6. public class ChildViewHolder extends BaseViewHolder {  
  7.   
  8.     private Context mContext;  
  9.     private View view;  
  10.     private TextView childLeftText;  
  11.     private TextView childRightText;  
  12.   
  13.     public ChildViewHolder(Context context, View itemView) {  
  14.         super(itemView);  
  15.         this.mContext = context;  
  16.         this.view = itemView;  
  17.     }  
  18.   
  19.     public void bindView(final DataBean dataBean, final int pos){  
  20.   
  21.         childLeftText = (TextView) view.findViewById(R.id.child_left_text);  
  22.         childRightText = (TextView) view.findViewById(R.id.child_right_text);  
  23.         childLeftText.setText(dataBean.getChildLeftTxt());  
  24.         childRightText.setText(dataBean.getChildRightTxt());  
  25.   
  26.     }  
  27. }  

OVER,以上就是主要的代码,大部分都贴上了,很简单。

总的来说就是 通过增加和删除 再加上动画效果 来模拟列表的展开和收起,在这里抛砖引玉,如有更好的实现方法或方式,还望各位猿友们不吝赐教,文章如有表述不当,还请指正。

Demo传送门
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值