stackoverflow上看到這個問題,答主給了個demo
http://stackoverflow.com/questions/27446051/recyclerview-animate-item-resize
看懂了以後發個博,記錄一下,剛開始看別人代碼好難受,就這麼3個文件看了一夜。。java
效果以下
markdown
res文件
main_activity文件就是一個recyclerview
main_item是兩個textview 一個標題一個詳細信息ide
MainActivity就是加載了一個RecyclerView動畫
public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); final RecyclerView rv = (RecyclerView) findViewById(R.id.rv); final LinearLayoutManager layoutManager = new LinearLayoutManager(this); rv.setLayoutManager(layoutManager); final MainAdapter adapter = new MainAdapter(); rv.setAdapter(adapter); } }
MainAdapter中new了一個keepOne對象,點進去看這個類,有兩個方法:bind和toggle,其中的bind是在MainAdapter中的onBindViewHolder()方法中調用,而toggle是響應viewholder的點擊事件this
public static class KeepOneH<VH extends RecyclerView.ViewHolder & Expandable> { // opened爲-1表示全部item是關閉狀態,open爲pos值的表示pos位置的item爲展開的狀態 private int _opened = -1; public void bind(VH holder, int pos) { if (pos == _opened) // 3 // 直接顯示expandView 無動畫 ExpandableViewHoldersUtil.openH(holder, holder.getExpandView(), false); else // 直接關閉expandView 無動畫 ExpandableViewHoldersUtil.closeH(holder, holder.getExpandView(), false); }
@SuppressWarnings("unchecked") // 響應點擊事件的方法 public void toggle(VH holder) { // 若是點擊的就是開着的item,就關閉該item並把opened置-1 // ???TODO if (_opened == holder.getPosition()) { _opened = -1; // 關閉expandView 有動畫 ExpandableViewHoldersUtil.closeH(holder, holder.getExpandView(), true); } // 若是點擊其餘原本關閉着的item,則把opened值換成當前pos,把以前開的item給關掉 else { int previous = _opened; _opened = holder.getPosition(); // 展開expandView 有動畫 ExpandableViewHoldersUtil.openH(holder, holder.getExpandView(), true); // 用動畫關閉以前的item final VH oldHolder = (VH) ((RecyclerView) holder.itemView.getParent()).findViewHolderForPosition(previous); if (oldHolder != null) ExpandableViewHoldersUtil.closeH(oldHolder, oldHolder.getExpandView(), true); } } }
點進openH和closeH方法進去看spa
// 4 public static void openH(final RecyclerView.ViewHolder holder, final View expandView, final boolean animate) { // animate參數爲true,則有動畫效果 if (animate) { expandView.setVisibility(View.VISIBLE); // 5 // 改變高度的動畫,具體操做點進去看 final Animator animator = ViewHolderAnimator.ofItemViewHeight(holder); // 擴展的動畫結束後透明度動畫開始 animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { final ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(expandView, View.ALPHA, 1); alphaAnimator.addListener(new ViewHolderAnimator.ViewHolderAnimatorListener(holder)); alphaAnimator.start(); } }); animator.start(); } // animate參數爲false,則直接設置爲可見 else { expandView.setVisibility(View.VISIBLE); expandView.setAlpha(1); } }
openH方法接收3個參數,
第一個是viewholder.
第二個是展開部分的view,由holder.getExpandView()方法獲取。這裏定義了一個接口.net
public static interface Expandable { public View getExpandView(); }
在MainAdapter中傳入infos這個Textviewcode
@Override public View getExpandView() { return infos; }
第三個是一個標記,true時有動畫,false時直接設置其展開或者是關閉的狀態。因此在bind()方法中調用的openH()都是false,而toggle()中調用的設置爲true。對象
openH方法中 具體動畫的操做爲ViewHolderAnimator.ofItemViewHeight(holder)
blog
public static Animator ofItemViewHeight(RecyclerView.ViewHolder holder) { View parent = (View) holder.itemView.getParent(); if (parent == null) throw new IllegalStateException("Cannot animate the layout of a view that has no parent"); // 測量擴展動畫的起始高度和結束高度 int start = holder.itemView.getMeasuredHeight(); holder.itemView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); int end = holder.itemView.getMeasuredHeight(); // 6 final Animator animator = LayoutAnimator.ofHeight(holder.itemView, start, end); // 設定該item在動畫開始結束和取消時可否被recycle animator.addListener(new ViewHolderAnimatorListener(holder)); // 設定結束時這個item的寬高 animator.addListener(new LayoutParamsAnimatorListener(holder.itemView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); return animator; }
能夠看出 具體展開的動畫在LayoutAnimator.ofHeight(holder.itemView, start, end);
中,ViewHolderAnimator只是測量參數,設定監聽兩個監聽事件
1設定在動畫開始結束和取消狀態下是否能夠被回收
public ViewHolderAnimatorListener(RecyclerView.ViewHolder holder) { _holder = holder; } @Override public void onAnimationStart(Animator animation) { _holder.setIsRecyclable(false); } @Override public void onAnimationEnd(Animator animation) { _holder.setIsRecyclable(true); } @Override public void onAnimationCancel(Animator animation) { _holder.setIsRecyclable(true); } }
2.設定在動畫結束後view的高和寬分別爲warp_content,match_parent.
public static class LayoutParamsAnimatorListener extends AnimatorListenerAdapter { private final View _view; private final int _paramsWidth; private final int _paramsHeight; public LayoutParamsAnimatorListener(View view, int paramsWidth, int paramsHeight) { _view = view; _paramsWidth = paramsWidth; _paramsHeight = paramsHeight; } @Override public void onAnimationEnd(Animator animation) { final ViewGroup.LayoutParams params = _view.getLayoutParams(); params.width = _paramsWidth; params.height = _paramsHeight; _view.setLayoutParams(params); } }
再深刻一層看展開的動畫
public class LayoutAnimator { public static class LayoutHeightUpdateListener implements ValueAnimator.AnimatorUpdateListener { private final View _view; public LayoutHeightUpdateListener(View view) { _view = view; } @Override public void onAnimationUpdate(ValueAnimator animation) { final ViewGroup.LayoutParams lp = _view.getLayoutParams(); lp.height = (int) animation.getAnimatedValue(); _view.setLayoutParams(lp); } } public static Animator ofHeight(View view, int start, int end) { final ValueAnimator animator = ValueAnimator.ofInt(start, end); animator.addUpdateListener(new LayoutHeightUpdateListener(view)); return animator; } }
是用ValueAnimator.ofInt生成一系列高度值,而後監聽動畫的變化,不斷設定view的高度值
http://blog.csdn.net/gulumi_mmga/article/details/46683437