Android -- RecyclerView(超简单)实现可展开列表
转载地址:
http://blog.****.net/chengxu_hou/article/details/70344759
可展开列表,听到这几个字的时候也许你就会问了,为啥要用 RecyclerView,用Android提供的 ExpandableListView不是更好吗?是的,ExpandableListView是很轻松就可以实现,但是,我要告诉你,我就是喜欢用RecyclerView ……
好了,不再废话了,这里只是想用RecyclerView实现ExpandableListView的效果,提供一种实现方法和思路,其实很简单,就一句话,展开--插入一行、收起--删除一行。
先上效果图,用的模拟器,有点卡,真机测试很流畅,为了便于区分,上了点色,样子有点丑
说一下实现思路:
* 列表组成:主要分成两个布局,默认展示的为父布局,点击父布局,在父布局的下方插入一行(子布局);
*展开效果:点击Item,插入一行数据的同时,该item下方的item向下滚动,也就是展开效果;
*动画效果:咳咳…… RecyclerView自带这种渐变的动画效果(知道我为啥喜欢RecyclerView了吧,哈哈),ListView自身是没有这种效果的。
以上也就是简单的实现思路,下面上码。
一大波代码来袭
首先是实体类,用来封装模拟数据
- /**
- * Created by hbh on 2017/4/20.
- * 实体类,模拟数据
- */
- public class DataBean {
- public static final int PARENT_ITEM = 0;//父布局
- public static final int CHILD_ITEM = 1;//子布局
- private int type;// 显示类型
- private boolean isExpand;// 是否展开
- private DataBean childBean;
- private String ID;
- private String parentLeftTxt;
- private String parentRightTxt;
- private String childLeftTxt;
- private String childRightTxt;
- public String getParentLeftTxt() {
- return parentLeftTxt;
- }
- public void setParentLeftTxt(String parentLeftTxt) {
- this.parentLeftTxt = parentLeftTxt;
- }
- public String getChildRightTxt() {
- return childRightTxt;
- }
- public void setChildRightTxt(String childRightTxt) {
- this.childRightTxt = childRightTxt;
- }
- public String getChildLeftTxt() {
- return childLeftTxt;
- }
- public void setChildLeftTxt(String childLeftTxt) {
- this.childLeftTxt = childLeftTxt;
- }
- public String getParentRightTxt() {
- return parentRightTxt;
- }
- public void setParentRightTxt(String parentRightTxt) {
- this.parentRightTxt = parentRightTxt;
- }
- public int getType() {
- return type;
- }
- public void setType(int type) {
- this.type = type;
- }
- public boolean isExpand() {
- return isExpand;
- }
- public void setExpand(boolean expand) {
- isExpand = expand;
- }
- public DataBean getChildBean() {
- return childBean;
- }
- public void setChildBean(DataBean childBean) {
- this.childBean = childBean;
- }
- public String getID() {
- return ID;
- }
- public void setID(String ID) {
- this.ID = ID;
- }
- }
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/activity_main"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="com.hbh.cl.expandrecyclerviewdemo.MainActivity">
- <android.support.v7.widget.RecyclerView
- android:id="@+id/recycle_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:scrollbars="vertical"
- android:background="@color/white"
- android:paddingTop="5dp">
- </android.support.v7.widget.RecyclerView>
- </RelativeLayout>
- public class MainActivity extends AppCompatActivity {
- private RecyclerView mRecyclerView;
- private List<DataBean> dataBeanList;
- private DataBean dataBean;
- private RecyclerAdapter mAdapter;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mRecyclerView = (RecyclerView) findViewById(R.id.recycle_view);
- initData();
- }
- /**
- * 模拟数据
- */
- private void initData(){
- dataBeanList = new ArrayList<>();
- for (int i = 1; i <= 50; i++) {
- dataBean = new DataBean();
- dataBean.setID(i+"");
- dataBean.setType(0);
- dataBean.setParentLeftTxt("父--"+i);
- dataBean.setParentRightTxt("父内容--"+i);
- dataBean.setChildLeftTxt("子--"+i);
- dataBean.setChildRightTxt("子内容--"+i);
- dataBean.setChildBean(dataBean);
- dataBeanList.add(dataBean);
- }
- setData();
- }
- private void setData(){
- mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
- mAdapter = new RecyclerAdapter(this,dataBeanList);
- mRecyclerView.setAdapter(mAdapter);
- //滚动监听
- mAdapter.setOnScrollListener(new RecyclerAdapter.OnScrollListener() {
- @Override
- public void scrollTo(int pos) {
- mRecyclerView.scrollToPosition(pos);
- }
- });
- }
- }
再有就是自定义Item点击监听接口,一个展开,一个收起
- /**
- * Created by hbh on 2017/4/20.
- * 父布局Item点击监听接口
- */
- public interface ItemClickListener {
- /**
- * 展开子Item
- * @param bean
- */
- void onExpandChildren(DataBean bean);
- /**
- * 隐藏子Item
- * @param bean
- */
- void onHideChildren(DataBean bean);
- }
- /**
- * Created by hbh on 2017/4/20.
- * 适配器
- */
- public class RecyclerAdapter extends RecyclerView.Adapter<BaseViewHolder> {
- private Context context;
- private List<DataBean> dataBeanList;
- private LayoutInflater mInflater;
- private OnScrollListener mOnScrollListener;
- public RecyclerAdapter(Context context, List<DataBean> dataBeanList) {
- this.context = context;
- this.dataBeanList = dataBeanList;
- this.mInflater = LayoutInflater.from(context);
- }
- @Override
- public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- View view = null;
- switch (viewType){
- case DataBean.PARENT_ITEM:
- view = mInflater.inflate(R.layout.recycleview_item_parent, parent, false);
- return new ParentViewHolder(context, view);
- case DataBean.CHILD_ITEM:
- view = mInflater.inflate(R.layout.recycleview_item_child, parent, false);
- return new ChildViewHolder(context, view);
- default:
- view = mInflater.inflate(R.layout.recycleview_item_parent, parent, false);
- return new ParentViewHolder(context, view);
- }
- }
- /**
- * 根据不同的类型绑定View
- * @param holder
- * @param position
- */
- @Override
- public void onBindViewHolder(BaseViewHolder holder, int position) {
- switch (getItemViewType(position)){
- case DataBean.PARENT_ITEM:
- ParentViewHolder parentViewHolder = (ParentViewHolder) holder;
- parentViewHolder.bindView(dataBeanList.get(position), position, itemClickListener);
- break;
- case DataBean.CHILD_ITEM:
- ChildViewHolder childViewHolder = (ChildViewHolder) holder;
- childViewHolder.bindView(dataBeanList.get(position), position);
- break;
- }
- }
- @Override
- public int getItemCount() {
- return dataBeanList.size();
- }
- @Override
- public int getItemViewType(int position) {
- return dataBeanList.get(position).getType();
- }
- private ItemClickListener itemClickListener = new ItemClickListener() {
- @Override
- public void onExpandChildren(DataBean bean) {
- int position = getCurrentPosition(bean.getID());//确定当前点击的item位置
- DataBean children = getChildDataBean(bean);//获取要展示的子布局数据对象,注意区分onHideChildren方法中的getChildBean()。
- if (children == null) {
- return;
- }
- add(children, position + 1);//在当前的item下方插入
- if (position == dataBeanList.size() - 2 && mOnScrollListener != null) { //如果点击的item为最后一个
- mOnScrollListener.scrollTo(position + 1);//向下滚动,使子布局能够完全展示
- }
- }
- @Override
- public void onHideChildren(DataBean bean) {
- int position = getCurrentPosition(bean.getID());//确定当前点击的item位置
- DataBean children = bean.getChildBean();//获取子布局对象
- if (children == null) {
- return;
- }
- remove(position + 1);//删除
- if (mOnScrollListener != null) {
- mOnScrollListener.scrollTo(position);
- }
- }
- };
- /**
- * 在父布局下方插入一条数据
- * @param bean
- * @param position
- */
- public void add(DataBean bean, int position) {
- dataBeanList.add(position, bean);
- notifyItemInserted(position);
- }
- /**
- *移除子布局数据
- * @param position
- */
- protected void remove(int position) {
- dataBeanList.remove(position);
- notifyItemRemoved(position);
- }
- /**
- * 确定当前点击的item位置并返回
- * @param uuid
- * @return
- */
- protected int getCurrentPosition(String uuid) {
- for (int i = 0; i < dataBeanList.size(); i++) {
- if (uuid.equalsIgnoreCase(dataBeanList.get(i).getID())) {
- return i;
- }
- }
- return -1;
- }
- /**
- * 封装子布局数据对象并返回
- * 注意,此处只是重新封装一个DataBean对象,为了标注Type为子布局数据,进而展开,展示数据
- * 要和onHideChildren方法里的getChildBean()区分开来
- * @param bean
- * @return
- */
- private DataBean getChildDataBean(DataBean bean){
- DataBean child = new DataBean();
- child.setType(1);
- child.setParentLeftTxt(bean.getParentLeftTxt());
- child.setParentRightTxt(bean.getParentRightTxt());
- child.setChildLeftTxt(bean.getChildLeftTxt());
- child.setChildRightTxt(bean.getChildRightTxt());
- return child;
- }
- /**
- * 滚动监听接口
- */
- public interface OnScrollListener{
- void scrollTo(int pos);
- }
- public void setOnScrollListener(OnScrollListener onScrollListener){
- this.mOnScrollListener = onScrollListener;
- }
- }
还有两个对应的 ParentViewHolder 和 ChildViewHolder
- /**
- * Created by hbh on 2017/4/20.
- * 父布局ViewHolder
- */
- public class ParentViewHolder extends BaseViewHolder {
- private Context mContext;
- private View view;
- private RelativeLayout containerLayout;
- private TextView parentLeftView;
- private TextView parentRightView;
- private ImageView expand;
- private View parentDashedView;
- public ParentViewHolder(Context context, View itemView) {
- super(itemView);
- this.mContext = context;
- this.view = itemView;
- }
- public void bindView(final DataBean dataBean, final int pos, final ItemClickListener listener){
- containerLayout = (RelativeLayout) view.findViewById(R.id.container);
- parentLeftView = (TextView) view.findViewById(R.id.parent_left_text);
- parentRightView = (TextView) view.findViewById(R.id.parent_right_text);
- expand = (ImageView) view.findViewById(R.id.expend);
- parentDashedView = view.findViewById(R.id.parent_dashed_view);
- RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) expand
- .getLayoutParams();
- expand.setLayoutParams(params);
- parentLeftView.setText(dataBean.getParentLeftTxt());
- parentRightView.setText(dataBean.getParentRightTxt());
- if (dataBean.isExpand()) {
- expand.setRotation(90);
- parentDashedView.setVisibility(View.INVISIBLE);
- } else {
- expand.setRotation(0);
- parentDashedView.setVisibility(View.VISIBLE);
- }
- //父布局OnClick监听
- containerLayout.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (listener != null) {
- if (dataBean.isExpand()) {
- listener.onHideChildren(dataBean);
- parentDashedView.setVisibility(View.VISIBLE);
- dataBean.setExpand(false);
- rotationExpandIcon(90, 0);
- } else {
- listener.onExpandChildren(dataBean);
- parentDashedView.setVisibility(View.INVISIBLE);
- dataBean.setExpand(true);
- rotationExpandIcon(0, 90);
- }
- }
- }
- });
- }
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- private void rotationExpandIcon(float from, float to) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- ValueAnimator valueAnimator = ValueAnimator.ofFloat(from, to);//属性动画
- valueAnimator.setDuration(500);
- valueAnimator.setInterpolator(new DecelerateInterpolator());
- valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- expand.setRotation((Float) valueAnimator.getAnimatedValue());
- }
- });
- valueAnimator.start();
- }
- }
- }
- /**
- * Created by hbh on 2017/4/20.
- * 子布局ViewHolder
- */
- public class ChildViewHolder extends BaseViewHolder {
- private Context mContext;
- private View view;
- private TextView childLeftText;
- private TextView childRightText;
- public ChildViewHolder(Context context, View itemView) {
- super(itemView);
- this.mContext = context;
- this.view = itemView;
- }
- public void bindView(final DataBean dataBean, final int pos){
- childLeftText = (TextView) view.findViewById(R.id.child_left_text);
- childRightText = (TextView) view.findViewById(R.id.child_right_text);
- childLeftText.setText(dataBean.getChildLeftTxt());
- childRightText.setText(dataBean.getChildRightTxt());
- }
- }
OVER,以上就是主要的代码,大部分都贴上了,很简单。
总的来说就是 通过增加和删除 再加上动画效果 来模拟列表的展开和收起,在这里抛砖引玉,如有更好的实现方法或方式,还望各位猿友们不吝赐教,文章如有表述不当,还请指正。
Demo传送门