RecyclerView实现二级目录显示---item可伸展收缩的RecyclerVoew

来源:互联网 发布:php常用算法 编辑:程序博客网 时间:2024/06/05 07:02

布局文件代码:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"                xmlns:tools="http://schemas.android.com/tools"                android:layout_width="match_parent"                android:layout_height="match_parent">    <android.support.v7.widget.RecyclerView        android:id="@+id/recyclerView_main"        android:layout_width="match_parent"        android:layout_height="match_parent"/></RelativeLayout>

MainActivity代码

package com.steven.bk31_expandablerecyclerview;import android.content.Context;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.GridLayoutManager;import android.support.v7.widget.RecyclerView;import com.steven.bk31_expandablerecyclerview.adapter.MyAdapter;import com.steven.bk31_expandablerecyclerview.decoration.MyItemAnimator;import com.steven.bk31_expandablerecyclerview.decoration.SpacesItemDecoration;import com.steven.bk31_expandablerecyclerview.model.ChildModel;import com.steven.bk31_expandablerecyclerview.model.GroupModel;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity {    private Context mContext = this;    private RecyclerView recyclerView_main;    //private MyAdapter.ViewHolder holder;    private MyAdapter adapter = null;    private List<Object> totalList = new ArrayList<>();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initData();        initView();    }    /**     * /     */    private void initView() {        //初始化recyclerview        recyclerView_main = ((RecyclerView) findViewById(R.id.recyclerView_main));        // 如果可以确定每个item的高度是固定的,设置这个选项可以提高性能        recyclerView_main.setHasFixedSize(true);        //设置分割线或者分割空间        recyclerView_main.addItemDecoration(new SpacesItemDecoration(3));        //设置动画        MyItemAnimator animator = new MyItemAnimator();        //适配器创建        adapter = new MyAdapter(this, totalList, animator);        //设置布局管理器        GridLayoutManager layoutManager = new GridLayoutManager(this, 1);        //根据位置设置span        layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {            @Override            public int getSpanSize(int position) {                if (adapter.getItemViewType(position) == MyAdapter.GROUP) {                    return 1;                }                return 1;            }        });        //设置布局管理器        recyclerView_main.setLayoutManager(layoutManager);       //设置适配器        recyclerView_main.setAdapter(adapter);      //  recyclerView_main.setItemAnimator(animator);    }    private void initData() {        //编造假数据源        int numzero=0;        for (int i = 0; i < 10; i++) {            GroupModel group = new GroupModel(String.format("第%d组项目", i));            int radom =(int)(Math.random()*10);            if (radom<=0)            {                numzero=numzero+1;            }            if (radom>0)            {                totalList.add(group);                for (int j = 0; j <radom ; j++) {                    //组对象中放置各个组下的二级目录                    group.getEntries().add(new ChildModel(String.format("第%d条消息", j),i-numzero));                }            }        }    }}
JavaBean包含两个

GroupBean与ChildBean

两个代码

package com.steven.bk31_expandablerecyclerview.model;/** * Created by StevenWang on 16/6/13. */public class ChildModel {    private String text;    private int parent_position;    public int getParent_position() {        return parent_position;    }    public void setParent_position(int parent_position) {        this.parent_position = parent_position;    }    public String getText() {        return text;    }    public void setText(String text) {        this.text = text;    }    public ChildModel(String text,int position) {        this.text = text;        this.parent_position =position;    }    @Override    public String toString() {        return "ChildModel{" +                "text='" + text + '\'' +                '}';    }}
package com.steven.bk31_expandablerecyclerview.model;import java.util.ArrayList;import java.util.List;/** * Created by StevenWang on 16/6/13. */public class GroupModel  {    private String text;    //组下实体集合    private List<ChildModel> entries;    //是否展开    private boolean isExpand;    public GroupModel(String text) {        this.text = text;        entries = new ArrayList<>();    }    public String getText() {        return text;    }    public void setText(String text) {        this.text = text;    }    public boolean isExpand() {        return isExpand;    }    public void setIsExpand(boolean isExpand) {        this.isExpand = isExpand;    }    public List<ChildModel> getEntries() {        return entries;    }    public void setEntries(List<ChildModel> entries) {        this.entries = entries;    }    @Override    public String toString() {        return "GroupModel{" +                "text='" + text + '\'' +                ", entries=" + entries +                ", isExpand=" + isExpand +                '}';    }}
Adapter代码

package com.steven.bk31_expandablerecyclerview.adapter;import android.app.Dialog;import android.content.Context;import android.content.DialogInterface;import android.support.v7.app.AlertDialog;import android.support.v7.widget.RecyclerView;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import android.widget.Toast;import com.steven.bk31_expandablerecyclerview.R;import com.steven.bk31_expandablerecyclerview.decoration.MyItemAnimator;import com.steven.bk31_expandablerecyclerview.model.ChildModel;import com.steven.bk31_expandablerecyclerview.model.GroupModel;import java.util.ArrayList;import java.util.List;import java.util.concurrent.locks.LockSupport;public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {    private Context context;    public static final int GROUP = 0;    public static final int CHILD = 1;    private List<Object> list;    private MyItemAnimator animator;    private RecyclerView recyclerView;    private LayoutInflater mInflater = null;    private List<Boolean> flags =new ArrayList<>();    private int last_position=-1;    public MyAdapter(Context context, List<Object> list, MyItemAnimator animator) {        this.context = context;        this.list = list;        this.animator = animator;        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);        for (int i = 0; i < list.size(); i++) {            flags.add(false);        }    }    @Override    public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {        View view = null;        switch (viewType) {            case GROUP:                view = mInflater.inflate(R.layout.item_recyclerview_group, parent, false);                //增加点击监听器                view.setOnClickListener(new View.OnClickListener() {                    /**                     /* @param v                     */                    @Override                    public void onClick(View v) {                        //获取view在adpter中的位置                        int position = recyclerView.getChildAdapterPosition(v);                        //获取这个我位置上的数据源类型                        switch (getItemViewType(position)) {                            //组类型                            case GROUP:                                GroupModel group = (GroupModel) list.get(position);                                if (last_position==-1||last_position==position)                                {                                    //是否展开                                    if (group.isExpand()) {                                        group.setIsExpand(false);                                      // animator.setXY(v.getLeft(), v.getTop());                                        list.removeAll(group.getEntries());                                        last_position=-1;                                        notifyItemRangeRemoved(position + 1, group.getEntries().size());                                    } else {                                        group.setIsExpand(true);                                      //  animator.setXY(v.getLeft(), v.getTop());                                        list.addAll(position + 1, group.getEntries());                                        last_position=position;                                        notifyItemRangeInserted(position + 1, group.getEntries().size                                                ());                                    }                                }else {                                    if (position<last_position)                                    {                                        //删除掉上次特定位置数据                                        GroupModel groupModel= (GroupModel) list.get(last_position);                                        Log.i("删除组的大小和内容:",groupModel.getEntries().size()+":::"+groupModel.toString());                                        Log.i("集合大小",list.size()+":::"+list.toString());                                        list.removeAll(groupModel.getEntries());                                        Log.i("集合大小",list.size()+":::"+list.toString());                                        groupModel.setIsExpand(false);                                       // animator.setXY(v.getLeft(), v.getTop());                                       // notifyItemRangeRemoved(last_position+1, groupModel.getEntries().size());                                        GroupModel groupModel1 = (GroupModel) list.get(position);                                        groupModel1.setIsExpand(true);                                        //当前点击位置下面加入子目录数据                                        list.addAll(position + 1,(groupModel1.getEntries()));                                       // animator.setXY(v.getLeft(), v.getTop());                                        notifyDataSetChanged();                                        //改变上次点击位置                                        last_position=position;                                      // notifyItemRangeRemoved(position+1, groupModel1.getEntries().size());                                    }else if (position>last_position){                                        GroupModel groupModel = (GroupModel) list.get(last_position);                                        int size = groupModel.getEntries().size();                                        list.removeAll(groupModel.getEntries());                                        groupModel.setIsExpand(false);                                        GroupModel groupModel1 = (GroupModel) list.get(position-size);                                        groupModel1.setIsExpand(true);                                        groupModel1.setIsExpand(true);                                        list.addAll(position + 1-size,(groupModel1.getEntries()));                                      //  animator.setXY(v.getLeft(), v.getTop());                                        notifyDataSetChanged();                                        //改变上次点击位置                                        last_position=position-size;                                    }                                }                                break;                            case CHILD:                                break;                        }                    }                });                return new ViewHolderGroup(view);            case CHILD:                view = mInflater.inflate(R.layout.item_recyclerview_child, parent, false);                view.setOnLongClickListener(new View.OnLongClickListener() {                    @Override                    public boolean onLongClick(final View v) {                        //                        int position = recyclerView.getChildAdapterPosition(v);                        Toast.makeText(context,"点击位置"+position,Toast.LENGTH_LONG).show();                        AlertDialog.Builder dialog=new AlertDialog.Builder(context);                        dialog.setTitle("是否删除");                        dialog.setNegativeButton("取消", new DialogInterface.OnClickListener() {                            @Override                            public void onClick(DialogInterface dialog, int which) {                                dialog.dismiss();                            }                        });                        //子目录删除操作                        dialog.setPositiveButton("是", new DialogInterface.OnClickListener() {                            @Override                            public void onClick(DialogInterface dialog, int which) {                                int position = recyclerView.getChildAdapterPosition(v);                               ChildModel childModel = (ChildModel) list.get(position);                                int parent_position = childModel.getParent_position();                                Log.i("parent_position腹肌目录的位置:",parent_position+"");                                GroupModel groupModel = (GroupModel) list.get(parent_position);                               groupModel.getEntries().remove(position-parent_position-1);                                list.remove(position);                                 Log.i(":::::::::::::>>>",list.size()+"个数");                                int size = list.size();                                if ((position-1)>-1&&((position+1)<list.size()-1))                                {                                    if ((list.get(position-1) instanceof GroupModel)==true&&(list.get(position+1) instanceof GroupModel)==true)                                    {                                        //删除一个父级目录 后续的所有父级目录下面 的字目录的getParentositio()都得变化                                        list.remove(position-1);                                        for (int i = position-1; i < list.size(); i++) {                                            if (list.get(i) instanceof GroupModel)                                            {                                                GroupModel model = (GroupModel) list.get(i);                                                List<ChildModel> entries = model.getEntries();                                                for (int j = 0; j < entries.size(); j++) {                                                    entries.get(j).setParent_position(i-1);                                                }                                            }                                        }                                    }                                    Log.i(":::::",(list.get(position-1) instanceof GroupModel)+"");                                    Log.i(":::::",(list.get(position+1) instanceof GroupModel)+"");                                }                                else if (position==size&&(list.get(position-1) instanceof GroupModel)) {                                    Log.i("hhhhh::","////"+position);                                   // list.remove(position-1);                                }                                notifyDataSetChanged();//                                //有问题 当删除的子条目为父级目录中的最后一个时候 开始执行删除父级目录指令//                                if ((list.get(position-1) instanceof GroupModel)&&(list.get(position+1) instanceof GroupModel))//                                {//                                    list.remove(position);//                                }//                                GroupModel groupModel = (GroupModel) list.get(parent_position);//                                if (groupModel.getEntries().size()==0)//                                {//                                    list.remove(parent_position);//                                }                            }                        });                        dialog.create().show();                        return false;                    }                });                return new ViewHolderChild(view);        }        return new ViewHolderGroup(view);    }    @Override    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {        Object obj = list.get(position);        if (obj instanceof GroupModel) {            ViewHolderGroup viewHolderGroup = (ViewHolderGroup) holder;            viewHolderGroup.textView_item_group.setText(((GroupModel) obj).getText());        }        if (obj instanceof ChildModel) {            ViewHolderChild viewHolderChild = (ViewHolderChild) holder;            viewHolderChild.textView_item_child.setText(((ChildModel) obj).getText());        }    }    @Override    public int getItemCount() {        return list.size();    }    @Override    public int getItemViewType(int position) {        Object obj = list.get(position);        if (obj instanceof GroupModel) {            return GROUP;        }        if (obj instanceof ChildModel) {            return CHILD;        }        return super.getItemViewType(position);    }    @Override    public void onAttachedToRecyclerView(RecyclerView recyclerView) {        this.recyclerView = recyclerView;    }    //组对象ViewHolder    class ViewHolderGroup extends RecyclerView.ViewHolder {        private TextView textView_item_group;        public ViewHolderGroup(View itemView) {            super(itemView);            textView_item_group = (TextView) itemView.findViewById(R.id.textView_item_group);        }    }   //组对象下对应二级目录Viewholder    class ViewHolderChild extends RecyclerView.ViewHolder {        private TextView textView_item_child;        public ViewHolderChild(View itemView) {            super(itemView);            textView_item_child = ((TextView) itemView.findViewById(R.id.textView_item_child));        }    }}


Steven封装的分割线类所有代码 4种分割线

package com.steven.bk31_expandablerecyclerview.decoration;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Rect;import android.graphics.drawable.Drawable;import android.support.v7.widget.GridLayoutManager;import android.support.v7.widget.RecyclerView;import android.support.v7.widget.RecyclerView.LayoutManager;import android.support.v7.widget.RecyclerView.State;import android.support.v7.widget.StaggeredGridLayoutManager;import android.view.View;/** * 设置网格排列时item之间的分割线 */public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};    private Drawable mDivider;    public DividerGridItemDecoration(Context context) {        final TypedArray a = context.obtainStyledAttributes(ATTRS);        mDivider = a.getDrawable(0);        a.recycle();    }    @Override    public void onDraw(Canvas c, RecyclerView parent, State state) {        drawHorizontal(c, parent);        drawVertical(c, parent);    }    private int getSpanCount(RecyclerView parent) {        int spanCount = -1;        LayoutManager layoutManager = parent.getLayoutManager();        if (layoutManager instanceof GridLayoutManager) {            spanCount = ((GridLayoutManager) layoutManager).getSpanCount();        } else if (layoutManager instanceof StaggeredGridLayoutManager) {            spanCount = ((StaggeredGridLayoutManager) layoutManager)                    .getSpanCount();        }        return spanCount;    }    public void drawHorizontal(Canvas c, RecyclerView parent) {        int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            final View child = parent.getChildAt(i);            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child                    .getLayoutParams();            final int left = child.getLeft() - params.leftMargin;            final int right = child.getRight() + params.rightMargin                    + mDivider.getIntrinsicWidth();            final int top = child.getBottom() + params.bottomMargin;            final int bottom = top + mDivider.getIntrinsicHeight();            mDivider.setBounds(left, top, right, bottom);            mDivider.draw(c);        }    }    public void drawVertical(Canvas c, RecyclerView parent) {        final int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            final View child = parent.getChildAt(i);            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child                    .getLayoutParams();            final int top = child.getTop() - params.topMargin;            final int bottom = child.getBottom() + params.bottomMargin;            final int left = child.getRight() + params.rightMargin;            final int right = left + mDivider.getIntrinsicWidth();            mDivider.setBounds(left, top, right, bottom);            mDivider.draw(c);        }    }    private boolean isLastColum(RecyclerView parent, int pos, int spanCount,                                int childCount) {        LayoutManager layoutManager = parent.getLayoutManager();        if (layoutManager instanceof GridLayoutManager) {            if ((pos + 1) % spanCount == 0) {                return true;            }        } else if (layoutManager instanceof StaggeredGridLayoutManager) {            int orientation = ((StaggeredGridLayoutManager) layoutManager)                    .getOrientation();            if (orientation == StaggeredGridLayoutManager.VERTICAL) {                if ((pos + 1) % spanCount == 0) {                    return true;                }            } else {                childCount = childCount - childCount % spanCount;                if (pos >= childCount)                    return true;            }        }        return false;    }    private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,                              int childCount) {        LayoutManager layoutManager = parent.getLayoutManager();        if (layoutManager instanceof GridLayoutManager) {            childCount = childCount - childCount % spanCount;            if (pos >= childCount)                return true;        } else if (layoutManager instanceof StaggeredGridLayoutManager) {            int orientation = ((StaggeredGridLayoutManager) layoutManager)                    .getOrientation();            // StaggeredGridLayoutManager            if (orientation == StaggeredGridLayoutManager.VERTICAL) {                childCount = childCount - childCount % spanCount;                if (pos >= childCount)                    return true;            } else            // StaggeredGridLayoutManager            {                if ((pos + 1) % spanCount == 0) {                    return true;                }            }        }        return false;    }    @Override    public void getItemOffsets(Rect outRect, int itemPosition,                               RecyclerView parent) {        int spanCount = getSpanCount(parent);        int childCount = parent.getAdapter().getItemCount();        if (isLastRaw(parent, itemPosition, spanCount, childCount))//        {            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);        } else if (isLastColum(parent, itemPosition, spanCount, childCount))//                {            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());        } else {            outRect.set(0, 0, mDivider.getIntrinsicWidth(),                    mDivider.getIntrinsicHeight());        }    }}

package com.steven.bk31_expandablerecyclerview.decoration;/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * limitations under the License. */import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Rect;import android.graphics.drawable.Drawable;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.util.Log;import android.view.View;/** * 设置线性排列时item之间的分割线 * This class is from the v7 samples of the Android SDK. * See the license above for details. */public class DividerItemDecoration extends RecyclerView.ItemDecoration {    private static final int[] ATTRS = new int[]{            android.R.attr.listDivider    };    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;    private Drawable mDivider;    private int mOrientation;    public DividerItemDecoration(Context context, int orientation) {        final TypedArray a = context.obtainStyledAttributes(ATTRS);        mDivider = a.getDrawable(0);        a.recycle();        setOrientation(orientation);    }    public void setOrientation(int orientation) {        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {            throw new IllegalArgumentException("invalid orientation");        }        mOrientation = orientation;    }    @Override    public void onDraw(Canvas c, RecyclerView parent) {        Log.v("recyclerview - itemdecoration", "onDraw()");        if (mOrientation == VERTICAL_LIST) {            drawVertical(c, parent);        } else {            drawHorizontal(c, parent);        }    }    public void drawVertical(Canvas c, RecyclerView parent) {        final int left = parent.getPaddingLeft();        final int right = parent.getWidth() - parent.getPaddingRight();        final int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            final View child = parent.getChildAt(i);            RecyclerView v = new RecyclerView(parent.getContext());            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child                    .getLayoutParams();            final int top = child.getBottom() + params.bottomMargin;            final int bottom = top + mDivider.getIntrinsicHeight();            mDivider.setBounds(left, top, right, bottom);            mDivider.draw(c);        }    }    public void drawHorizontal(Canvas c, RecyclerView parent) {        final int top = parent.getPaddingTop();        final int bottom = parent.getHeight() - parent.getPaddingBottom();        final int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            final View child = parent.getChildAt(i);            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child                    .getLayoutParams();            final int left = child.getRight() + params.rightMargin;            final int right = left + mDivider.getIntrinsicHeight();            mDivider.setBounds(left, top, right, bottom);            mDivider.draw(c);        }    }    @Override    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {        if (mOrientation == VERTICAL_LIST) {            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());        } else {            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);        }    }}

package com.steven.bk31_expandablerecyclerview.decoration;import android.support.annotation.NonNull;import android.support.v4.animation.AnimatorCompatHelper;import android.support.v4.view.ViewCompat;import android.support.v4.view.ViewPropertyAnimatorCompat;import android.support.v4.view.ViewPropertyAnimatorListener;import android.support.v7.widget.RecyclerView;import android.support.v7.widget.SimpleItemAnimator;import android.view.View;import java.util.ArrayList;import java.util.List;/** * Created by StevenWang on 16/6/13. */public class MyDefaultItemAnimator extends SimpleItemAnimator {/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */    /**     * This implementation of {@link RecyclerView.ItemAnimator} provides basic     * animations on remove, add, and move events that happen to the items in     * a RecyclerView. RecyclerView uses a DefaultItemAnimator by default.     *     * @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator)     */    private static final boolean DEBUG = false;    private ArrayList<RecyclerView.ViewHolder> mPendingRemovals = new ArrayList<>();    private ArrayList<RecyclerView.ViewHolder> mPendingAdditions = new ArrayList<>();    private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();    private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();    private ArrayList<ArrayList<RecyclerView.ViewHolder>> mAdditionsList = new ArrayList<>();    private ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();    private ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();    private ArrayList<RecyclerView.ViewHolder> mAddAnimations = new ArrayList<>();    private ArrayList<RecyclerView.ViewHolder> mMoveAnimations = new ArrayList<>();    private ArrayList<RecyclerView.ViewHolder> mRemoveAnimations = new ArrayList<>();    private ArrayList<RecyclerView.ViewHolder> mChangeAnimations = new ArrayList<>();    private static class MoveInfo {        public RecyclerView.ViewHolder holder;        public int fromX, fromY, toX, toY;        private MoveInfo(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {            this.holder = holder;            this.fromX = fromX;            this.fromY = fromY;            this.toX = toX;            this.toY = toY;        }    }    private static class ChangeInfo {        public RecyclerView.ViewHolder oldHolder, newHolder;        public int fromX, fromY, toX, toY;        private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder) {            this.oldHolder = oldHolder;            this.newHolder = newHolder;        }        private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,                           int fromX, int fromY, int toX, int toY) {            this(oldHolder, newHolder);            this.fromX = fromX;            this.fromY = fromY;            this.toX = toX;            this.toY = toY;        }        @Override        public String toString() {            return "ChangeInfo{" +                    "oldHolder=" + oldHolder +                    ", newHolder=" + newHolder +                    ", fromX=" + fromX +                    ", fromY=" + fromY +                    ", toX=" + toX +                    ", toY=" + toY +                    '}';        }    }    @Override    public void runPendingAnimations() {        boolean removalsPending = !mPendingRemovals.isEmpty();        boolean movesPending = !mPendingMoves.isEmpty();        boolean changesPending = !mPendingChanges.isEmpty();        boolean additionsPending = !mPendingAdditions.isEmpty();        if (!removalsPending && !movesPending && !additionsPending && !changesPending) {            // nothing to animate            return;        }        // First, remove stuff        for (RecyclerView.ViewHolder holder : mPendingRemovals) {            animateRemoveImpl(holder);        }        mPendingRemovals.clear();        // Next, move stuff        if (movesPending) {            final ArrayList<MoveInfo> moves = new ArrayList<>();            moves.addAll(mPendingMoves);            mMovesList.add(moves);            mPendingMoves.clear();            Runnable mover = new Runnable() {                @Override                public void run() {                    for (MoveInfo moveInfo : moves) {                        animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,                                moveInfo.toX, moveInfo.toY);                    }                    moves.clear();                    mMovesList.remove(moves);                }            };            if (removalsPending) {                View view = moves.get(0).holder.itemView;                ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());            } else {                mover.run();            }        }        // Next, change stuff, to run in parallel with move animations        if (changesPending) {            final ArrayList<ChangeInfo> changes = new ArrayList<>();            changes.addAll(mPendingChanges);            mChangesList.add(changes);            mPendingChanges.clear();            Runnable changer = new Runnable() {                @Override                public void run() {                    for (ChangeInfo change : changes) {                        animateChangeImpl(change);                    }                    changes.clear();                    mChangesList.remove(changes);                }            };            if (removalsPending) {                RecyclerView.ViewHolder holder = changes.get(0).oldHolder;                ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());            } else {                changer.run();            }        }        // Next, add stuff        if (additionsPending) {            final ArrayList<RecyclerView.ViewHolder> additions = new ArrayList<>();            additions.addAll(mPendingAdditions);            mAdditionsList.add(additions);            mPendingAdditions.clear();            Runnable adder = new Runnable() {                public void run() {                    for (RecyclerView.ViewHolder holder : additions) {                        animateAddImpl(holder);                    }                    additions.clear();                    mAdditionsList.remove(additions);                }            };            if (removalsPending || movesPending || changesPending) {                long removeDuration = removalsPending ? getRemoveDuration() : 0;                long moveDuration = movesPending ? getMoveDuration() : 0;                long changeDuration = changesPending ? getChangeDuration() : 0;                long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);                View view = additions.get(0).itemView;                ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);            } else {                adder.run();            }        }    }    @Override    public boolean animateRemove(final RecyclerView.ViewHolder holder) {        endAnimation(holder);        mPendingRemovals.add(holder);        return true;    }    private void animateRemoveImpl(final RecyclerView.ViewHolder holder) {        final View view = holder.itemView;        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);        mRemoveAnimations.add(holder);        animation.setDuration(getRemoveDuration())                .alpha(0).setListener(new VpaListenerAdapter() {            @Override            public void onAnimationStart(View view) {                dispatchRemoveStarting(holder);            }            @Override            public void onAnimationEnd(View view) {                animation.setListener(null);                ViewCompat.setAlpha(view, 1);                dispatchRemoveFinished(holder);                mRemoveAnimations.remove(holder);                dispatchFinishedWhenDone();            }        }).start();    }    @Override    public boolean animateAdd(final RecyclerView.ViewHolder holder) {        endAnimation(holder);        ViewCompat.setAlpha(holder.itemView, 0);        mPendingAdditions.add(holder);        return true;    }    private void animateAddImpl(final RecyclerView.ViewHolder holder) {        final View view = holder.itemView;        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);        mAddAnimations.add(holder);        animation.alpha(1).setDuration(getAddDuration()).                setListener(new VpaListenerAdapter() {                    @Override                    public void onAnimationStart(View view) {                        dispatchAddStarting(holder);                    }                    @Override                    public void onAnimationCancel(View view) {                        ViewCompat.setAlpha(view, 1);                    }                    @Override                    public void onAnimationEnd(View view) {                        animation.setListener(null);                        dispatchAddFinished(holder);                        mAddAnimations.remove(holder);                        dispatchFinishedWhenDone();                    }                }).start();    }    @Override    public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY,                               int toX, int toY) {        final View view = holder.itemView;        fromX += ViewCompat.getTranslationX(holder.itemView);        fromY += ViewCompat.getTranslationY(holder.itemView);        resetAnimation(holder);        int deltaX = toX - fromX;        int deltaY = toY - fromY;        if (deltaX == 0 && deltaY == 0) {            dispatchMoveFinished(holder);            return false;        }        if (deltaX != 0) {            ViewCompat.setTranslationX(view, -deltaX);        }        if (deltaY != 0) {            ViewCompat.setTranslationY(view, -deltaY);        }        mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));        return true;    }    private void animateMoveImpl(final RecyclerView.ViewHolder holder, int fromX, int fromY, int            toX, int toY) {        final View view = holder.itemView;        final int deltaX = toX - fromX;        final int deltaY = toY - fromY;        if (deltaX != 0) {            ViewCompat.animate(view).translationX(0);        }        if (deltaY != 0) {            ViewCompat.animate(view).translationY(0);        }        // TODO: make EndActions end listeners instead, since end actions aren't called when        // vpas are canceled (and can't end them. why?)        // need listener functionality in VPACompat for this. Ick.        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);        mMoveAnimations.add(holder);        animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {            @Override            public void onAnimationStart(View view) {                dispatchMoveStarting(holder);            }            @Override            public void onAnimationCancel(View view) {                if (deltaX != 0) {                    ViewCompat.setTranslationX(view, 0);                }                if (deltaY != 0) {                    ViewCompat.setTranslationY(view, 0);                }            }            @Override            public void onAnimationEnd(View view) {                animation.setListener(null);                dispatchMoveFinished(holder);                mMoveAnimations.remove(holder);                dispatchFinishedWhenDone();            }        }).start();    }    @Override    public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder            newHolder,                                 int fromX, int fromY, int toX, int toY) {        if (oldHolder == newHolder) {            // Don't know how to run change animations when the same view holder is re-used.            // run a move animation to handle position changes.            return animateMove(oldHolder, fromX, fromY, toX, toY);        }        final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);        final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);        final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);        resetAnimation(oldHolder);        int deltaX = (int) (toX - fromX - prevTranslationX);        int deltaY = (int) (toY - fromY - prevTranslationY);        // recover prev translation state after ending animation        ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);        ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);        ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);        if (newHolder != null) {            // carry over translation values            resetAnimation(newHolder);            ViewCompat.setTranslationX(newHolder.itemView, -deltaX);            ViewCompat.setTranslationY(newHolder.itemView, -deltaY);            ViewCompat.setAlpha(newHolder.itemView, 0);        }        mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));        return true;    }    private void animateChangeImpl(final ChangeInfo changeInfo) {        final RecyclerView.ViewHolder holder = changeInfo.oldHolder;        final View view = holder == null ? null : holder.itemView;        final RecyclerView.ViewHolder newHolder = changeInfo.newHolder;        final View newView = newHolder != null ? newHolder.itemView : null;        if (view != null) {            final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(                    getChangeDuration());            mChangeAnimations.add(changeInfo.oldHolder);            oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);            oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);            oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {                @Override                public void onAnimationStart(View view) {                    dispatchChangeStarting(changeInfo.oldHolder, true);                }                @Override                public void onAnimationEnd(View view) {                    oldViewAnim.setListener(null);                    ViewCompat.setAlpha(view, 1);                    ViewCompat.setTranslationX(view, 0);                    ViewCompat.setTranslationY(view, 0);                    dispatchChangeFinished(changeInfo.oldHolder, true);                    mChangeAnimations.remove(changeInfo.oldHolder);                    dispatchFinishedWhenDone();                }            }).start();        }        if (newView != null) {            final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);            mChangeAnimations.add(changeInfo.newHolder);            newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).                    alpha(1).setListener(new VpaListenerAdapter() {                @Override                public void onAnimationStart(View view) {                    dispatchChangeStarting(changeInfo.newHolder, false);                }                @Override                public void onAnimationEnd(View view) {                    newViewAnimation.setListener(null);                    ViewCompat.setAlpha(newView, 1);                    ViewCompat.setTranslationX(newView, 0);                    ViewCompat.setTranslationY(newView, 0);                    dispatchChangeFinished(changeInfo.newHolder, false);                    mChangeAnimations.remove(changeInfo.newHolder);                    dispatchFinishedWhenDone();                }            }).start();        }    }    private void endChangeAnimation(List<ChangeInfo> infoList, RecyclerView.ViewHolder item) {        for (int i = infoList.size() - 1; i >= 0; i--) {            ChangeInfo changeInfo = infoList.get(i);            if (endChangeAnimationIfNecessary(changeInfo, item)) {                if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {                    infoList.remove(changeInfo);                }            }        }    }    private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {        if (changeInfo.oldHolder != null) {            endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);        }        if (changeInfo.newHolder != null) {            endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);        }    }    private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, RecyclerView.ViewHolder            item) {        boolean oldItem = false;        if (changeInfo.newHolder == item) {            changeInfo.newHolder = null;        } else if (changeInfo.oldHolder == item) {            changeInfo.oldHolder = null;            oldItem = true;        } else {            return false;        }        ViewCompat.setAlpha(item.itemView, 1);        ViewCompat.setTranslationX(item.itemView, 0);        ViewCompat.setTranslationY(item.itemView, 0);        dispatchChangeFinished(item, oldItem);        return true;    }    @Override    public void endAnimation(RecyclerView.ViewHolder item) {        final View view = item.itemView;        // this will trigger end callback which should set properties to their target values.        ViewCompat.animate(view).cancel();        // TODO if some other animations are chained to end, how do we cancel them as well?        for (int i = mPendingMoves.size() - 1; i >= 0; i--) {            MoveInfo moveInfo = mPendingMoves.get(i);            if (moveInfo.holder == item) {                ViewCompat.setTranslationY(view, 0);                ViewCompat.setTranslationX(view, 0);                dispatchMoveFinished(item);                mPendingMoves.remove(i);            }        }        endChangeAnimation(mPendingChanges, item);        if (mPendingRemovals.remove(item)) {            ViewCompat.setAlpha(view, 1);            dispatchRemoveFinished(item);        }        if (mPendingAdditions.remove(item)) {            ViewCompat.setAlpha(view, 1);            dispatchAddFinished(item);        }        for (int i = mChangesList.size() - 1; i >= 0; i--) {            ArrayList<ChangeInfo> changes = mChangesList.get(i);            endChangeAnimation(changes, item);            if (changes.isEmpty()) {                mChangesList.remove(i);            }        }        for (int i = mMovesList.size() - 1; i >= 0; i--) {            ArrayList<MoveInfo> moves = mMovesList.get(i);            for (int j = moves.size() - 1; j >= 0; j--) {                MoveInfo moveInfo = moves.get(j);                if (moveInfo.holder == item) {                    ViewCompat.setTranslationY(view, 0);                    ViewCompat.setTranslationX(view, 0);                    dispatchMoveFinished(item);                    moves.remove(j);                    if (moves.isEmpty()) {                        mMovesList.remove(i);                    }                    break;                }            }        }        for (int i = mAdditionsList.size() - 1; i >= 0; i--) {            ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);            if (additions.remove(item)) {                ViewCompat.setAlpha(view, 1);                dispatchAddFinished(item);                if (additions.isEmpty()) {                    mAdditionsList.remove(i);                }            }        }        // animations should be ended by the cancel above.        //noinspection PointlessBooleanExpression,ConstantConditions        if (mRemoveAnimations.remove(item) && DEBUG) {            throw new IllegalStateException("after animation is cancelled, item should not be in "                    + "mRemoveAnimations list");        }        //noinspection PointlessBooleanExpression,ConstantConditions        if (mAddAnimations.remove(item) && DEBUG) {            throw new IllegalStateException("after animation is cancelled, item should not be in "                    + "mAddAnimations list");        }        //noinspection PointlessBooleanExpression,ConstantConditions        if (mChangeAnimations.remove(item) && DEBUG) {            throw new IllegalStateException("after animation is cancelled, item should not be in "                    + "mChangeAnimations list");        }        //noinspection PointlessBooleanExpression,ConstantConditions        if (mMoveAnimations.remove(item) && DEBUG) {            throw new IllegalStateException("after animation is cancelled, item should not be in "                    + "mMoveAnimations list");        }        dispatchFinishedWhenDone();    }    private void resetAnimation(RecyclerView.ViewHolder holder) {        AnimatorCompatHelper.clearInterpolator(holder.itemView);        endAnimation(holder);    }    @Override    public boolean isRunning() {        return (!mPendingAdditions.isEmpty() ||                !mPendingChanges.isEmpty() ||                !mPendingMoves.isEmpty() ||                !mPendingRemovals.isEmpty() ||                !mMoveAnimations.isEmpty() ||                !mRemoveAnimations.isEmpty() ||                !mAddAnimations.isEmpty() ||                !mChangeAnimations.isEmpty() ||                !mMovesList.isEmpty() ||                !mAdditionsList.isEmpty() ||                !mChangesList.isEmpty());    }    /**     * Check the state of currently pending and running animations. If there are none     * pending/running, call {@link #dispatchAnimationsFinished()} to notify any     * listeners.     */    private void dispatchFinishedWhenDone() {        if (!isRunning()) {            dispatchAnimationsFinished();        }    }    @Override    public void endAnimations() {        int count = mPendingMoves.size();        for (int i = count - 1; i >= 0; i--) {            MoveInfo item = mPendingMoves.get(i);            View view = item.holder.itemView;            ViewCompat.setTranslationY(view, 0);            ViewCompat.setTranslationX(view, 0);            dispatchMoveFinished(item.holder);            mPendingMoves.remove(i);        }        count = mPendingRemovals.size();        for (int i = count - 1; i >= 0; i--) {            RecyclerView.ViewHolder item = mPendingRemovals.get(i);            dispatchRemoveFinished(item);            mPendingRemovals.remove(i);        }        count = mPendingAdditions.size();        for (int i = count - 1; i >= 0; i--) {            RecyclerView.ViewHolder item = mPendingAdditions.get(i);            View view = item.itemView;            ViewCompat.setAlpha(view, 1);            dispatchAddFinished(item);            mPendingAdditions.remove(i);        }        count = mPendingChanges.size();        for (int i = count - 1; i >= 0; i--) {            endChangeAnimationIfNecessary(mPendingChanges.get(i));        }        mPendingChanges.clear();        if (!isRunning()) {            return;        }        int listCount = mMovesList.size();        for (int i = listCount - 1; i >= 0; i--) {            ArrayList<MoveInfo> moves = mMovesList.get(i);            count = moves.size();            for (int j = count - 1; j >= 0; j--) {                MoveInfo moveInfo = moves.get(j);                RecyclerView.ViewHolder item = moveInfo.holder;                View view = item.itemView;                ViewCompat.setTranslationY(view, 0);                ViewCompat.setTranslationX(view, 0);                dispatchMoveFinished(moveInfo.holder);                moves.remove(j);                if (moves.isEmpty()) {                    mMovesList.remove(moves);                }            }        }        listCount = mAdditionsList.size();        for (int i = listCount - 1; i >= 0; i--) {            ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);            count = additions.size();            for (int j = count - 1; j >= 0; j--) {                RecyclerView.ViewHolder item = additions.get(j);                View view = item.itemView;                ViewCompat.setAlpha(view, 1);                dispatchAddFinished(item);                additions.remove(j);                if (additions.isEmpty()) {                    mAdditionsList.remove(additions);                }            }        }        listCount = mChangesList.size();        for (int i = listCount - 1; i >= 0; i--) {            ArrayList<ChangeInfo> changes = mChangesList.get(i);            count = changes.size();            for (int j = count - 1; j >= 0; j--) {                endChangeAnimationIfNecessary(changes.get(j));                if (changes.isEmpty()) {                    mChangesList.remove(changes);                }            }        }        cancelAll(mRemoveAnimations);        cancelAll(mMoveAnimations);        cancelAll(mAddAnimations);        cancelAll(mChangeAnimations);        dispatchAnimationsFinished();    }    void cancelAll(List<RecyclerView.ViewHolder> viewHolders) {        for (int i = viewHolders.size() - 1; i >= 0; i--) {            ViewCompat.animate(viewHolders.get(i).itemView).cancel();        }    }    @Override    public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,                                             @NonNull List<Object> payloads) {        return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);    }    private static class VpaListenerAdapter implements ViewPropertyAnimatorListener {        @Override        public void onAnimationStart(View view) {        }        @Override        public void onAnimationEnd(View view) {        }        @Override        public void onAnimationCancel(View view) {        }    }    private int x;    private int y;    public void setXY(int x, int y) {        this.x = x;        this.y = y;    }}

/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.steven.bk31_expandablerecyclerview.decoration;import android.support.v4.view.ViewCompat;import android.support.v4.view.ViewPropertyAnimatorCompat;import android.support.v4.view.ViewPropertyAnimatorListener;import android.support.v7.widget.RecyclerView;import android.support.v7.widget.RecyclerView.ViewHolder;import android.support.v7.widget.SimpleItemAnimator;import android.view.View;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.List;/** * This implementation of {@link RecyclerView.ItemAnimator} provides basic * animations on remove, add, and move events that happen to the items in * a RecyclerView. RecyclerView uses a DefaultItemAnimator by default. * * @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator) */public class MyItemAnimator extends SimpleItemAnimator {    private static final boolean DEBUG = false;    private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<ViewHolder>();    private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<ViewHolder>();    private ArrayList<MoveInfo> mPendingMoves = new ArrayList<MoveInfo>();    private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<ChangeInfo>();    private ArrayList<ArrayList<ViewHolder>> mAdditionsList =            new ArrayList<ArrayList<ViewHolder>>();    private ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<ArrayList<MoveInfo>>();    private ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<ArrayList<ChangeInfo>>();    private ArrayList<ViewHolder> mAddAnimations = new ArrayList<ViewHolder>();    private ArrayList<ViewHolder> mMoveAnimations = new ArrayList<ViewHolder>();    private ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<ViewHolder>();    private ArrayList<ViewHolder> mChangeAnimations = new ArrayList<ViewHolder>();    private int x;    private int y;    private static class MoveInfo {        public ViewHolder holder;        public int fromX, fromY, toX, toY;        private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) {            this.holder = holder;            this.fromX = fromX;            this.fromY = fromY;            this.toX = toX;            this.toY = toY;        }    }    private static class ChangeInfo {        public ViewHolder oldHolder, newHolder;        public int fromX, fromY, toX, toY;        private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {            this.oldHolder = oldHolder;            this.newHolder = newHolder;        }        private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,                           int fromX, int fromY, int toX, int toY) {            this(oldHolder, newHolder);            this.fromX = fromX;            this.fromY = fromY;            this.toX = toX;            this.toY = toY;        }        @Override        public String toString() {            return "ChangeInfo{" +                    "oldHolder=" + oldHolder +                    ", newHolder=" + newHolder +                    ", fromX=" + fromX +                    ", fromY=" + fromY +                    ", toX=" + toX +                    ", toY=" + toY +                    '}';        }    }    @Override    public void runPendingAnimations() {        boolean removalsPending = !mPendingRemovals.isEmpty();        boolean movesPending = !mPendingMoves.isEmpty();        boolean changesPending = !mPendingChanges.isEmpty();        boolean additionsPending = !mPendingAdditions.isEmpty();        if (!removalsPending && !movesPending && !additionsPending && !changesPending) {            // nothing to animate            return;        }        // First, remove stuff        Collections.sort(mPendingRemovals, new Comparator<ViewHolder>() {            @Override            public int compare(ViewHolder lhs, ViewHolder rhs) {                return rhs.getAdapterPosition() - lhs.getAdapterPosition();            }        });        int i = 0;        for (ViewHolder holder : mPendingRemovals) {            animateRemoveImpl(holder, i++);        }        mPendingRemovals.clear();        // Next, move stuff        if (movesPending) {            final ArrayList<MoveInfo> moves = new ArrayList<MoveInfo>();            moves.addAll(mPendingMoves);            mMovesList.add(moves);            mPendingMoves.clear();            Runnable mover = new Runnable() {                @Override                public void run() {                    for (MoveInfo moveInfo : moves) {                        animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,                                moveInfo.toX, moveInfo.toY);                    }                    moves.clear();                    mMovesList.remove(moves);                }            };            if (removalsPending) {                View view = moves.get(0).holder.itemView;                ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration() + (i - 1) * 100);            } else {                mover.run();            }        }        // Next, change stuff, to run in parallel with move animations        if (changesPending) {            final ArrayList<ChangeInfo> changes = new ArrayList<ChangeInfo>();            changes.addAll(mPendingChanges);            mChangesList.add(changes);            mPendingChanges.clear();            Runnable changer = new Runnable() {                @Override                public void run() {                    for (ChangeInfo change : changes) {                        animateChangeImpl(change);                    }                    changes.clear();                    mChangesList.remove(changes);                }            };            if (removalsPending) {                ViewHolder holder = changes.get(0).oldHolder;                ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());            } else {                changer.run();            }        }        // Next, add stuff        if (additionsPending) {            final ArrayList<ViewHolder> additions = new ArrayList<ViewHolder>();            additions.addAll(mPendingAdditions);            mAdditionsList.add(additions);            mPendingAdditions.clear();            Runnable adder = new Runnable() {                public void run() {                    int i = 0;                    Collections.sort(additions, new Comparator<ViewHolder>() {                        @Override                        public int compare(ViewHolder lhs, ViewHolder rhs) {                            return lhs.getAdapterPosition() - rhs.getAdapterPosition();                        }                    });                    for (ViewHolder holder : additions) {                        animateAddImpl(holder, i++);                    }                    additions.clear();                    mAdditionsList.remove(additions);                }            };            if (removalsPending || movesPending || changesPending) {                long removeDuration = removalsPending ? getRemoveDuration() : 0;                long moveDuration = movesPending ? getMoveDuration() : 0;                long changeDuration = changesPending ? getChangeDuration() : 0;                long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);                View view = additions.get(0).itemView;                ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);            } else {                adder.run();            }        }    }    /**     * 准备删除     * @param holder     * @return     */    @Override    public boolean animateRemove(final ViewHolder holder) {        endAnimation(holder);        mPendingRemovals.add(holder);        return true;    }    /**     * 执行删除动画     * @param holder     */    private void animateRemoveImpl(final ViewHolder holder, int i) {        final View view = holder.itemView;        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);        mRemoveAnimations.add(holder);        animation.setStartDelay(i * 100).setDuration(getRemoveDuration())                .translationX(x - view.getLeft())                .translationY(y - view.getTop())//                .rotationXBy(90)                .setListener(new VpaListenerAdapter() {            @Override            public void onAnimationStart(View view) {                dispatchRemoveStarting(holder);            }            @Override            public void onAnimationEnd(View view) {                animation.setListener(null);                ViewCompat.setTranslationX(view, 0);                ViewCompat.setTranslationY(view, 0);//                ViewCompat.setRotationX(view, 0);                dispatchRemoveFinished(holder);                mRemoveAnimations.remove(holder);                dispatchFinishedWhenDone();            }        }).start();    }    /**     * 插入前的准备     * @param holder     * @return     */    @Override    public boolean animateAdd(final ViewHolder holder) {        endAnimation(holder);        ViewCompat.setTranslationX(holder.itemView, x - holder.itemView.getLeft());        ViewCompat.setTranslationY(holder.itemView, y - holder.itemView.getTop());        holder.itemView.setVisibility(View.INVISIBLE);        mPendingAdditions.add(holder);        return true;    }    /**     * 执行插入动画     * @param holder     */    private void animateAddImpl(final ViewHolder holder, int i) {        final View view = holder.itemView;        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);        mAddAnimations.add(holder);        animation.setStartDelay(i * 100)                .translationX(0)                .translationY(0)                .setDuration(getAddDuration()).                setListener(new VpaListenerAdapter() {                    @Override                    public void onAnimationStart(View view) {                        dispatchAddStarting(holder);                        view.setVisibility(View.VISIBLE);                    }                    @Override                    public void onAnimationCancel(View view) {                        ViewCompat.setTranslationX(view, 0);                        ViewCompat.setTranslationY(view, 0);                    }                    @Override                    public void onAnimationEnd(View view) {                        animation.setListener(null);                        dispatchAddFinished(holder);                        mAddAnimations.remove(holder);                        dispatchFinishedWhenDone();                    }                }).start();    }    @Override    public boolean animateMove(final ViewHolder holder, int fromX, int fromY,                               int toX, int toY) {        final View view = holder.itemView;        fromX += ViewCompat.getTranslationX(holder.itemView);        fromY += ViewCompat.getTranslationY(holder.itemView);        endAnimation(holder);        int deltaX = toX - fromX;        int deltaY = toY - fromY;        if (deltaX == 0 && deltaY == 0) {            dispatchMoveFinished(holder);            return false;        }        if (deltaX != 0) {            ViewCompat.setTranslationX(view, -deltaX);        }        if (deltaY != 0) {            ViewCompat.setTranslationY(view, -deltaY);        }        mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));        return true;    }    private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {        final View view = holder.itemView;        final int deltaX = toX - fromX;        final int deltaY = toY - fromY;        if (deltaX != 0) {            ViewCompat.animate(view).translationX(0);        }        if (deltaY != 0) {            ViewCompat.animate(view).translationY(0);        }        // TODO: make EndActions end listeners instead, since end actions aren't called when        // vpas are canceled (and can't end them. why?)        // need listener functionality in VPACompat for this. Ick.        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);        mMoveAnimations.add(holder);        animation.setStartDelay(0).setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {            @Override            public void onAnimationStart(View view) {                dispatchMoveStarting(holder);            }            @Override            public void onAnimationCancel(View view) {                if (deltaX != 0) {                    ViewCompat.setTranslationX(view, 0);                }                if (deltaY != 0) {                    ViewCompat.setTranslationY(view, 0);                }            }            @Override            public void onAnimationEnd(View view) {                animation.setListener(null);                dispatchMoveFinished(holder);                mMoveAnimations.remove(holder);                dispatchFinishedWhenDone();            }        }).start();    }    @Override    public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,                                 int fromX, int fromY, int toX, int toY) {        final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);        final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);        final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);        endAnimation(oldHolder);        int deltaX = (int) (toX - fromX - prevTranslationX);        int deltaY = (int) (toY - fromY - prevTranslationY);        // recover prev translation state after ending animation        ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);        ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);        ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);        if (newHolder != null && newHolder.itemView != null) {            // carry over translation values            endAnimation(newHolder);            ViewCompat.setTranslationX(newHolder.itemView, -deltaX);            ViewCompat.setTranslationY(newHolder.itemView, -deltaY);            ViewCompat.setAlpha(newHolder.itemView, 0);        }        mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));        return true;    }    private void animateChangeImpl(final ChangeInfo changeInfo) {        final ViewHolder holder = changeInfo.oldHolder;        final View view = holder == null ? null : holder.itemView;        final ViewHolder newHolder = changeInfo.newHolder;        final View newView = newHolder != null ? newHolder.itemView : null;        if (view != null) {            final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(                    getChangeDuration());            mChangeAnimations.add(changeInfo.oldHolder);            oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);            oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);            oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {                @Override                public void onAnimationStart(View view) {                    dispatchChangeStarting(changeInfo.oldHolder, true);                }                @Override                public void onAnimationEnd(View view) {                    oldViewAnim.setListener(null);                    ViewCompat.setAlpha(view, 1);                    ViewCompat.setTranslationX(view, 0);                    ViewCompat.setTranslationY(view, 0);                    dispatchChangeFinished(changeInfo.oldHolder, true);                    mChangeAnimations.remove(changeInfo.oldHolder);                    dispatchFinishedWhenDone();                }            }).start();        }        if (newView != null) {            final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);            mChangeAnimations.add(changeInfo.newHolder);            newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).                    alpha(1).setListener(new VpaListenerAdapter() {                @Override                public void onAnimationStart(View view) {                    dispatchChangeStarting(changeInfo.newHolder, false);                }                @Override                public void onAnimationEnd(View view) {                    newViewAnimation.setListener(null);                    ViewCompat.setAlpha(newView, 1);                    ViewCompat.setTranslationX(newView, 0);                    ViewCompat.setTranslationY(newView, 0);                    dispatchChangeFinished(changeInfo.newHolder, false);                    mChangeAnimations.remove(changeInfo.newHolder);                    dispatchFinishedWhenDone();                }            }).start();        }    }    private void endChangeAnimation(List<ChangeInfo> infoList, ViewHolder item) {        for (int i = infoList.size() - 1; i >= 0; i--) {            ChangeInfo changeInfo = infoList.get(i);            if (endChangeAnimationIfNecessary(changeInfo, item)) {                if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {                    infoList.remove(changeInfo);                }            }        }    }    private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {        if (changeInfo.oldHolder != null) {            endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);        }        if (changeInfo.newHolder != null) {            endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);        }    }    private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) {        boolean oldItem = false;        if (changeInfo.newHolder == item) {            changeInfo.newHolder = null;        } else if (changeInfo.oldHolder == item) {            changeInfo.oldHolder = null;            oldItem = true;        } else {            return false;        }        ViewCompat.setAlpha(item.itemView, 1);        ViewCompat.setTranslationX(item.itemView, 0);        ViewCompat.setTranslationY(item.itemView, 0);        dispatchChangeFinished(item, oldItem);        return true;    }    @Override    public void endAnimation(ViewHolder item) {        final View view = item.itemView;        // this will trigger end callback which should set properties to their target values.        ViewCompat.animate(view).cancel();        // TODO if some other animations are chained to end, how do we cancel them as well?        for (int i = mPendingMoves.size() - 1; i >= 0; i--) {            MoveInfo moveInfo = mPendingMoves.get(i);            if (moveInfo.holder == item) {                ViewCompat.setTranslationY(view, 0);                ViewCompat.setTranslationX(view, 0);                dispatchMoveFinished(item);                mPendingMoves.remove(i);            }        }        endChangeAnimation(mPendingChanges, item);        if (mPendingRemovals.remove(item)) {            ViewCompat.setAlpha(view, 1);            dispatchRemoveFinished(item);        }        if (mPendingAdditions.remove(item)) {            ViewCompat.setAlpha(view, 1);            dispatchAddFinished(item);        }        for (int i = mChangesList.size() - 1; i >= 0; i--) {            ArrayList<ChangeInfo> changes = mChangesList.get(i);            endChangeAnimation(changes, item);            if (changes.isEmpty()) {                mChangesList.remove(i);            }        }        for (int i = mMovesList.size() - 1; i >= 0; i--) {            ArrayList<MoveInfo> moves = mMovesList.get(i);            for (int j = moves.size() - 1; j >= 0; j--) {                MoveInfo moveInfo = moves.get(j);                if (moveInfo.holder == item) {                    ViewCompat.setTranslationY(view, 0);                    ViewCompat.setTranslationX(view, 0);                    dispatchMoveFinished(item);                    moves.remove(j);                    if (moves.isEmpty()) {                        mMovesList.remove(i);                    }                    break;                }            }        }        for (int i = mAdditionsList.size() - 1; i >= 0; i--) {            ArrayList<ViewHolder> additions = mAdditionsList.get(i);            if (additions.remove(item)) {                ViewCompat.setAlpha(view, 1);                dispatchAddFinished(item);                if (additions.isEmpty()) {                    mAdditionsList.remove(i);                }            }        }        // animations should be ended by the cancel above.        if (mRemoveAnimations.remove(item) && DEBUG) {            throw new IllegalStateException("after animation is cancelled, item should not be in "                    + "mRemoveAnimations list");        }        if (mAddAnimations.remove(item) && DEBUG) {            throw new IllegalStateException("after animation is cancelled, item should not be in "                    + "mAddAnimations list");        }        if (mChangeAnimations.remove(item) && DEBUG) {            throw new IllegalStateException("after animation is cancelled, item should not be in "                    + "mChangeAnimations list");        }        if (mMoveAnimations.remove(item) && DEBUG) {            throw new IllegalStateException("after animation is cancelled, item should not be in "                    + "mMoveAnimations list");        }        dispatchFinishedWhenDone();    }    @Override    public boolean isRunning() {        return (!mPendingAdditions.isEmpty() ||                !mPendingChanges.isEmpty() ||                !mPendingMoves.isEmpty() ||                !mPendingRemovals.isEmpty() ||                !mMoveAnimations.isEmpty() ||                !mRemoveAnimations.isEmpty() ||                !mAddAnimations.isEmpty() ||                !mChangeAnimations.isEmpty() ||                !mMovesList.isEmpty() ||                !mAdditionsList.isEmpty() ||                !mChangesList.isEmpty());    }    /**     * Check the state of currently pending and running animations. If there are none     * pending/running, call {@link #dispatchAnimationsFinished()} to notify any     * listeners.     */    private void dispatchFinishedWhenDone() {        if (!isRunning()) {            dispatchAnimationsFinished();        }    }    @Override    public void endAnimations() {        int count = mPendingMoves.size();        for (int i = count - 1; i >= 0; i--) {            MoveInfo item = mPendingMoves.get(i);            View view = item.holder.itemView;            ViewCompat.setTranslationY(view, 0);            ViewCompat.setTranslationX(view, 0);            dispatchMoveFinished(item.holder);            mPendingMoves.remove(i);        }        count = mPendingRemovals.size();        for (int i = count - 1; i >= 0; i--) {            ViewHolder item = mPendingRemovals.get(i);            dispatchRemoveFinished(item);            mPendingRemovals.remove(i);        }        count = mPendingAdditions.size();        for (int i = count - 1; i >= 0; i--) {            ViewHolder item = mPendingAdditions.get(i);            View view = item.itemView;            ViewCompat.setAlpha(view, 1);            dispatchAddFinished(item);            mPendingAdditions.remove(i);        }        count = mPendingChanges.size();        for (int i = count - 1; i >= 0; i--) {            endChangeAnimationIfNecessary(mPendingChanges.get(i));        }        mPendingChanges.clear();        if (!isRunning()) {            return;        }        int listCount = mMovesList.size();        for (int i = listCount - 1; i >= 0; i--) {            ArrayList<MoveInfo> moves = mMovesList.get(i);            count = moves.size();            for (int j = count - 1; j >= 0; j--) {                MoveInfo moveInfo = moves.get(j);                ViewHolder item = moveInfo.holder;                View view = item.itemView;                ViewCompat.setTranslationY(view, 0);                ViewCompat.setTranslationX(view, 0);                dispatchMoveFinished(moveInfo.holder);                moves.remove(j);                if (moves.isEmpty()) {                    mMovesList.remove(moves);                }            }        }        listCount = mAdditionsList.size();        for (int i = listCount - 1; i >= 0; i--) {            ArrayList<ViewHolder> additions = mAdditionsList.get(i);            count = additions.size();            for (int j = count - 1; j >= 0; j--) {                ViewHolder item = additions.get(j);                View view = item.itemView;                ViewCompat.setAlpha(view, 1);                dispatchAddFinished(item);                additions.remove(j);                if (additions.isEmpty()) {                    mAdditionsList.remove(additions);                }            }        }        listCount = mChangesList.size();        for (int i = listCount - 1; i >= 0; i--) {            ArrayList<ChangeInfo> changes = mChangesList.get(i);            count = changes.size();            for (int j = count - 1; j >= 0; j--) {                endChangeAnimationIfNecessary(changes.get(j));                if (changes.isEmpty()) {                    mChangesList.remove(changes);                }            }        }        cancelAll(mRemoveAnimations);        cancelAll(mMoveAnimations);        cancelAll(mAddAnimations);        cancelAll(mChangeAnimations);        dispatchAnimationsFinished();    }    void cancelAll(List<ViewHolder> viewHolders) {        for (int i = viewHolders.size() - 1; i >= 0; i--) {            ViewCompat.animate(viewHolders.get(i).itemView).cancel();        }    }    public void setXY(int x, int y){        this.x = x;        this.y = y;    }    private static class VpaListenerAdapter implements ViewPropertyAnimatorListener {        @Override        public void onAnimationStart(View view) {}        @Override        public void onAnimationEnd(View view) {}        @Override        public void onAnimationCancel(View view) {}    };}

package com.steven.bk31_expandablerecyclerview.decoration;import android.graphics.Rect;import android.support.v7.widget.RecyclerView;import android.view.View;/** * 设置item之间的分割空间 */public class SpacesItemDecoration extends RecyclerView.ItemDecoration {    private int space;    public SpacesItemDecoration(int space) {        this.space=space;    }    @Override    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {        outRect.left = space;        outRect.right = space;        outRect.bottom = space;        if(parent.getChildPosition(view) == 0 || parent.getChildPosition(view) == 1 || parent.getChildPosition(view) == 2 || parent.getChildPosition(view) == 3){            outRect.top = space;        }    }}

两种布局的XML文件

<?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:gravity="center"              android:orientation="horizontal"    android:background="#efe"    android:layout_marginBottom="1dp"    >    <ImageView        android:id="@+id/imageView_item_icon"        android:layout_width="50dp"        android:layout_height="50dp"        android:background="@drawable/ic_stub"/>    <TextView        android:id="@+id/textView_item_child"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginLeft="5dp"        android:textColor="#00f"        android:textSize="23sp"        android:text="New Text"/></LinearLayout>

<?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:background="#eee"    android:orientation="vertical">    <TextView        android:id="@+id/textView_item_group"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="#eee"        android:text="Large Text"        android:textSize="25sp" /></LinearLayout>



原创粉丝点击