summaryrefslogtreecommitdiffstats
path: root/samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java
diff options
context:
space:
mode:
Diffstat (limited to 'samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java')
-rw-r--r--samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java286
1 files changed, 259 insertions, 27 deletions
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java
index af5c6532d..6714f6d02 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java
@@ -15,13 +15,20 @@
*/
package com.example.android.supportv7.widget;
-import android.support.v4.util.ArrayMap;
-import android.widget.CompoundButton;
import com.example.android.supportv7.R;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
+import android.os.Build;
import android.os.Bundle;
+import android.support.v4.util.ArrayMap;
import android.support.v4.view.MenuItemCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.ViewPropertyAnimatorListener;
+import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.util.TypedValue;
@@ -30,10 +37,10 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
+import android.widget.CompoundButton;
import android.widget.TextView;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
public class AnimatedRecyclerView extends Activity {
@@ -49,6 +56,7 @@ public class AnimatedRecyclerView extends Activity {
boolean mAnimationsEnabled = true;
boolean mPredictiveAnimationsEnabled = true;
RecyclerView.ItemAnimator mCachedAnimator = null;
+ boolean mEnableInPlaceChange = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -57,7 +65,9 @@ public class AnimatedRecyclerView extends Activity {
ViewGroup container = (ViewGroup) findViewById(R.id.container);
mRecyclerView = new RecyclerView(this);
- mCachedAnimator = mRecyclerView.getItemAnimator();
+ mCachedAnimator = createAnimator();
+ mCachedAnimator.setChangeDuration(2000);
+ mRecyclerView.setItemAnimator(mCachedAnimator);
mRecyclerView.setLayoutManager(new MyLayoutManager(this));
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
@@ -91,14 +101,241 @@ public class AnimatedRecyclerView extends Activity {
}
});
- CheckBox enableChangeAnimations =
- (CheckBox) findViewById(R.id.enableChangeAnimations);
- enableChangeAnimations.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ CheckBox enableInPlaceChange = (CheckBox) findViewById(R.id.enableInPlaceChange);
+ enableInPlaceChange.setChecked(mEnableInPlaceChange);
+ enableInPlaceChange.setOnCheckedChangeListener(
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ mEnableInPlaceChange = isChecked;
+ }
+ });
+ }
+
+ private RecyclerView.ItemAnimator createAnimator() {
+ return new DefaultItemAnimator() {
+ List<ItemChangeAnimator> mPendingChangeAnimations = new ArrayList<>();
+ ArrayMap<RecyclerView.ViewHolder, ItemChangeAnimator> mRunningAnimations
+ = new ArrayMap<>();
+ ArrayMap<MyViewHolder, Long> mPendingSettleList = new ArrayMap<>();
+
@Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- mCachedAnimator.setSupportsChangeAnimations(isChecked);
+ public void runPendingAnimations() {
+ super.runPendingAnimations();
+ for (ItemChangeAnimator anim : mPendingChangeAnimations) {
+ anim.start();
+ mRunningAnimations.put(anim.mViewHolder, anim);
+ }
+ mPendingChangeAnimations.clear();
+ for (int i = mPendingSettleList.size() - 1; i >=0; i--) {
+ final MyViewHolder vh = mPendingSettleList.keyAt(i);
+ final long duration = mPendingSettleList.valueAt(i);
+ ViewCompat.animate(vh.textView).translationX(0f).alpha(1f)
+ .setDuration(duration).setListener(
+ new ViewPropertyAnimatorListener() {
+ @Override
+ public void onAnimationStart(View view) {
+ dispatchAnimationStarted(vh);
+ }
+
+ @Override
+ public void onAnimationEnd(View view) {
+ ViewCompat.setTranslationX(vh.textView, 0f);
+ ViewCompat.setAlpha(vh.textView, 1f);
+ dispatchAnimationFinished(vh);
+ }
+
+ @Override
+ public void onAnimationCancel(View view) {
+
+ }
+ }).start();
+ }
+ mPendingSettleList.clear();
}
- });
+
+ @Override
+ public ItemHolderInfo recordPreLayoutInformation(RecyclerView.State state,
+ RecyclerView.ViewHolder viewHolder,
+ @AdapterChanges int changeFlags, List<Object> payloads) {
+ MyItemInfo info = (MyItemInfo) super
+ .recordPreLayoutInformation(state, viewHolder, changeFlags, payloads);
+ info.text = ((MyViewHolder) viewHolder).textView.getText();
+ return info;
+ }
+
+ @Override
+ public ItemHolderInfo recordPostLayoutInformation(RecyclerView.State state,
+ RecyclerView.ViewHolder viewHolder) {
+ MyItemInfo info = (MyItemInfo) super.recordPostLayoutInformation(state, viewHolder);
+ info.text = ((MyViewHolder) viewHolder).textView.getText();
+ return info;
+ }
+
+
+ @Override
+ public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {
+ return mEnableInPlaceChange;
+ }
+
+ @Override
+ public void endAnimation(RecyclerView.ViewHolder item) {
+ super.endAnimation(item);
+ for (int i = mPendingChangeAnimations.size() - 1; i >= 0; i--) {
+ ItemChangeAnimator anim = mPendingChangeAnimations.get(i);
+ if (anim.mViewHolder == item) {
+ mPendingChangeAnimations.remove(i);
+ anim.setFraction(1f);
+ dispatchChangeFinished(item, true);
+ }
+ }
+ for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+ ItemChangeAnimator animator = mRunningAnimations.get(item);
+ if (animator != null) {
+ animator.end();
+ mRunningAnimations.removeAt(i);
+ }
+ }
+ for (int i = mPendingSettleList.size() - 1; i >= 0; i--) {
+ final MyViewHolder vh = mPendingSettleList.keyAt(i);
+ if (vh == item) {
+ mPendingSettleList.removeAt(i);
+ dispatchChangeFinished(item, true);
+ }
+ }
+ }
+
+ @Override
+ public boolean animateChange(RecyclerView.ViewHolder oldHolder,
+ RecyclerView.ViewHolder newHolder, ItemHolderInfo preInfo,
+ ItemHolderInfo postInfo) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1
+ || oldHolder != newHolder) {
+ return super.animateChange(oldHolder, newHolder, preInfo, postInfo);
+ }
+ return animateChangeApiHoneycombMr1(oldHolder, newHolder, preInfo, postInfo);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
+ private boolean animateChangeApiHoneycombMr1(RecyclerView.ViewHolder oldHolder,
+ RecyclerView.ViewHolder newHolder,
+ ItemHolderInfo preInfo, ItemHolderInfo postInfo) {
+ endAnimation(oldHolder);
+ MyItemInfo pre = (MyItemInfo) preInfo;
+ MyItemInfo post = (MyItemInfo) postInfo;
+ MyViewHolder vh = (MyViewHolder) oldHolder;
+
+ CharSequence finalText = post.text;
+
+ if (pre.text.equals(post.text)) {
+ // same content. Just translate back to 0
+ final long duration = (long) (getChangeDuration()
+ * (ViewCompat.getTranslationX(vh.textView) / vh.textView.getWidth()));
+ mPendingSettleList.put(vh, duration);
+ // we set it here because previous endAnimation would set it to other value.
+ vh.textView.setText(finalText);
+ } else {
+ // different content, get out and come back.
+ vh.textView.setText(pre.text);
+ final ItemChangeAnimator anim = new ItemChangeAnimator(vh, finalText,
+ getChangeDuration()) {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ setFraction(1f);
+ dispatchChangeFinished(mViewHolder, true);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ dispatchChangeStarting(mViewHolder, true);
+ }
+ };
+ mPendingChangeAnimations.add(anim);
+ }
+ return true;
+ }
+
+ @Override
+ public ItemHolderInfo obtainHolderInfo() {
+ return new MyItemInfo();
+ }
+ };
+ }
+
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
+ abstract private static class ItemChangeAnimator implements
+ ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
+ CharSequence mFinalText;
+ ValueAnimator mValueAnimator;
+ MyViewHolder mViewHolder;
+ final float mMaxX;
+ final float mStartRatio;
+ public ItemChangeAnimator(MyViewHolder viewHolder, CharSequence finalText, long duration) {
+ mViewHolder = viewHolder;
+ mMaxX = mViewHolder.itemView.getWidth();
+ mStartRatio = ViewCompat.getTranslationX(mViewHolder.textView) / mMaxX;
+ mFinalText = finalText;
+ mValueAnimator = ValueAnimator.ofFloat(0f, 1f);
+ mValueAnimator.addUpdateListener(this);
+ mValueAnimator.addListener(this);
+ mValueAnimator.setDuration(duration);
+ mValueAnimator.setTarget(mViewHolder.itemView);
+ }
+
+ void setFraction(float fraction) {
+ fraction = mStartRatio + (1f - mStartRatio) * fraction;
+ if (fraction < .5f) {
+ ViewCompat.setTranslationX(mViewHolder.textView, fraction * mMaxX);
+ ViewCompat.setAlpha(mViewHolder.textView, 1f - fraction);
+ } else {
+ ViewCompat.setTranslationX(mViewHolder.textView, (1f - fraction) * mMaxX);
+ ViewCompat.setAlpha(mViewHolder.textView, fraction);
+ maybeSetFinalText();
+ }
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ setFraction(valueAnimator.getAnimatedFraction());
+ }
+
+ public void start() {
+ mValueAnimator.start();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ maybeSetFinalText();
+ ViewCompat.setAlpha(mViewHolder.textView, 1f);
+ }
+
+ public void maybeSetFinalText() {
+ if (mFinalText != null) {
+ mViewHolder.textView.setText(mFinalText);
+ mFinalText = null;
+ }
+ }
+
+ public void end() {
+ mValueAnimator.cancel();
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ }
+
+ private static class MyItemInfo extends DefaultItemAnimator.ItemHolderInfo {
+ CharSequence text;
}
@Override
@@ -114,6 +351,7 @@ public class AnimatedRecyclerView extends Activity {
return super.onOptionsItemSelected(item);
}
+ @SuppressWarnings("unused")
public void checkboxClicked(View view) {
ViewGroup parent = (ViewGroup) view.getParent();
boolean selected = ((CheckBox) view).isChecked();
@@ -121,6 +359,7 @@ public class AnimatedRecyclerView extends Activity {
mAdapter.selectItem(holder, selected);
}
+ @SuppressWarnings("unused")
public void itemClicked(View view) {
ViewGroup parent = (ViewGroup) view;
MyViewHolder holder = (MyViewHolder) mRecyclerView.getChildViewHolder(parent);
@@ -462,20 +701,19 @@ public class AnimatedRecyclerView extends Activity {
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
String itemText = mData.get(position);
- ((MyViewHolder) holder).textView.setText(itemText);
- ((MyViewHolder) holder).expandedText.setText("More text for the expanded version");
+ MyViewHolder myViewHolder = (MyViewHolder) holder;
+ myViewHolder.boundText = itemText;
+ myViewHolder.textView.setText(itemText);
boolean selected = false;
if (mSelected.get(itemText) != null) {
selected = mSelected.get(itemText);
}
- ((MyViewHolder) holder).checkBox.setChecked(selected);
+ myViewHolder.checkBox.setChecked(selected);
Boolean expanded = mExpanded.get(itemText);
- if (expanded != null && expanded) {
- ((MyViewHolder) holder).expandedText.setVisibility(View.VISIBLE);
- ((MyViewHolder) holder).textView.setVisibility(View.GONE);
+ if (Boolean.TRUE.equals(expanded)) {
+ myViewHolder.textView.setText("More text for the expanded version");
} else {
- ((MyViewHolder) holder).expandedText.setVisibility(View.GONE);
- ((MyViewHolder) holder).textView.setVisibility(View.VISIBLE);
+ myViewHolder.textView.setText(itemText);
}
}
@@ -484,28 +722,22 @@ public class AnimatedRecyclerView extends Activity {
return mData.size();
}
- public void selectItem(String itemText, boolean selected) {
- mSelected.put(itemText, selected);
- }
-
public void selectItem(MyViewHolder holder, boolean selected) {
- mSelected.put((String) holder.textView.getText().toString(), selected);
+ mSelected.put(holder.boundText, selected);
}
public void toggleExpanded(MyViewHolder holder) {
- String text = (String) holder.textView.getText();
- mExpanded.put(text, !mExpanded.get(text));
+ mExpanded.put(holder.boundText, !mExpanded.get(holder.boundText));
}
}
static class MyViewHolder extends RecyclerView.ViewHolder {
- public TextView expandedText;
public TextView textView;
public CheckBox checkBox;
+ public String boundText;
public MyViewHolder(View v) {
super(v);
- expandedText = (TextView) v.findViewById(R.id.expandedText);
textView = (TextView) v.findViewById(R.id.text);
checkBox = (CheckBox) v.findViewById(R.id.selected);
}