Circular reveal transition for new activity - android

As per https://developer.android.com/training/material/animations.html
The ViewAnimationUtils.createCircularReveal() method enables you to
animate a clipping circle to reveal or hide a view.
To reveal a previously invisible view using this effect:
// previously invisible view
View myView = findViewById(R.id.my_view);
// get the center for the clipping circle
int cx = (myView.getLeft() + myView.getRight()) / 2;
int cy = (myView.getTop() + myView.getBottom()) / 2;
// get the final radius for the clipping circle
int finalRadius = Math.max(myView.getWidth(), myView.getHeight());
// create the animator for this view (the start radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
// make the view visible and start the animation
myView.setVisibility(View.VISIBLE);
anim.start();
This is meant to reveal a view. How can I use this to circularly reveal an entire activity, without any shared elements?
Specifically, I'd like my searchActivity to circularly reveal from the search action button in the toolbar.

After looking for a solution for half a day without a result, I came up with an own implementation. I'm using a transparent activity with a matching root layout.
The root layout is a view which can then be revealed with createCircularReveal().
My code looks like this:
Theme Definition in styles.xml
<style name="Theme.Transparent" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:statusBarColor">#android:color/transparent</item>
<item name="android:windowBackground">#android:color/transparent</item>
</style>
Activity Definition in AndroidManifest.xml
<activity
android:name=".ui.CircularRevealActivity"
android:theme="#style/Theme.Transparent"
android:launchMode="singleTask"
/>
then I declared a layout for my activity (I've chosen DrawerLayout, so that I can have a NavDrawer. Every layout should work here.)
<android.support.v4.widget.DrawerLayout
android:id="#+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<FrameLayout
android:id="#+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/honey_melon"
>
<!-- Insert your actual layout here -->
</FrameLayout>
</android.support.v4.widget.DrawerLayout>
Important is the FrameLayout with the id root_layout. This view will be revealed in the activity.
Finally I implemented CircularRevealActivity and overwrote onCreate():
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
overridePendingTransition(R.anim.do_not_move, R.anim.do_not_move);
setContentView(R.layout.activity_reveal_circular);
if (savedInstanceState == null) {
rootLayout.setVisibility(View.INVISIBLE);
ViewTreeObserver viewTreeObserver = rootLayout.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
circularRevealActivity();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
rootLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
}
}
}
It was important to put circularRevealActivity() into a OnGlobalLayoutListener, because the view needs to be drawn for the animation.
circularRevealActivity() looks like Ishaan's proposal:
private void circularRevealActivity() {
int cx = rootLayout.getWidth() / 2;
int cy = rootLayout.getHeight() / 2;
float finalRadius = Math.max(rootLayout.getWidth(), rootLayout.getHeight());
// create the animator for this view (the start radius is zero)
Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootLayout, cx, cy, 0, finalRadius);
circularReveal.setDuration(1000);
// make the view visible and start the animation
rootLayout.setVisibility(View.VISIBLE);
circularReveal.start();
}
Edit 1
The definition for R.anim.do_not_move was added. However, it should work without that line too, if your design does not specify default transitions for activities. Let me know
R.anim.do_not_move:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="0"
android:toYDelta="0"
android:duration="#android:integer/config_mediumAnimTime"
/>
</set>

If you want to reverse the circular reveal on leaving activity, use the following modification to onBackPressed().
#Override
public void onBackPressed() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int cx = rootLayout.getWidth();
int cy = 0;
float finalRadius = Math.max(rootLayout.getWidth(), rootLayout.getHeight());
Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootLayout, cx, cy, finalRadius, 0);
circularReveal.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) {
}
#Override
public void onAnimationEnd(Animator animator) {
rootLayout.setVisibility(View.INVISIBLE);
finish();
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
circularReveal.setDuration(400);
circularReveal.start();
}else{
super.onBackPressed();
}
}

I think you can use ActivityOptionsCompat.makeClipRevealAnimation .
[https://developer.android.com/reference/android/support/v4/app/ActivityOptionsCompat.html#makeClipRevealAnimation(android.view.View, int, int, int, int)](https://developer.android.com/reference/android/support/v4/app/ActivityOptionsCompat.html#makeClipRevealAnimation(android.view.View, int, int, int, int))

To reverse the CircularReveal animation swap the startRadius and endRadius arguments. Also you will need to setup an AnimatorListenerand in the onAnimationEnd() callback method is where you can call finishAfterTransition(). This is for when you press the up navigation or click on the back button.

ou have to draw the circle view, and after that you should create an animation to it.
Creating the circle view:
public class Circle extends View {
private static final int START_ANGLE_POINT = 90;
private final Paint paint;
private final RectF rect;
private float angle;
public Circle(Context context, AttributeSet attrs) {
super(context, attrs);
final int strokeWidth = 40;
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(strokeWidth);
//Circle color
paint.setColor(Color.RED);
//size 200x200 example
rect = new RectF(strokeWidth, strokeWidth, 200 + strokeWidth, 200 + strokeWidth);
//Initial Angle (optional, it can be zero)
angle = 120;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawArc(rect, START_ANGLE_POINT, angle, false, paint);
}
public float getAngle() {
return angle;
}
public void setAngle(float angle) {
this.angle = angle;
}
}
Creating the animation class to set the new angle:
public class CircleAngleAnimation extends Animation {
private Circle circle;
private float oldAngle;
private float newAngle;
public CircleAngleAnimation(Circle circle, int newAngle) {
this.oldAngle = circle.getAngle();
this.newAngle = newAngle;
this.circle = circle;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation transformation) {
float angle = oldAngle + ((newAngle - oldAngle) * interpolatedTime);
circle.setAngle(angle);
circle.requestLayout();
}
}
Put circle into your layout:
<com.package.Circle
android:id="#+id/circle"
android:layout_width="300dp"
android:layout_height="300dp" />
And finally starting the animation:
Circle circle = (Circle) findViewById(R.id.circle);
CircleAngleAnimation animation = new CircleAngleAnimation(circle, 240);
animation.setDuration(1000);
circle.startAnimation(animation);

Related

Enlarge animation in android

I want to achieve the below animation in android I have tried scenes but scenes do not work with text as per docs it is confirmed :
"If you try to resize a TextView with an animation, the text will pop to a new location before the object has completely resized. To avoid this problem, do not animate the resizing of views that contain text."
Please any solution , the enlarged layout text can contain images too.
animation video
this thing worked some how ,but the animation is little jittery,I guess layout height final value is attained first and layoutWidth later, have to fix this. this is my enlarge/reduce animation :
public class EnlargeAnimation extends Animation {
private final int diffHeight;
private final int diffWidth;
private final int initialHeight;
private final int initialWidth;
private final View targetView;
public EnlargeAnimation(View targetView, float targetHeight, float targetWidth) {
this.targetView = targetView;
this.initialHeight = targetView.getMeasuredHeight();
this.initialWidth = targetView.getMeasuredWidth();
this.diffHeight = (int) (targetHeight-initialHeight);
this.diffWidth = (int) (targetWidth-initialWidth);
}
#Override
public boolean willChangeBounds() {
return true;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float newHeight = initialHeight + diffHeight * interpolatedTime;
float newWidth = initialWidth + diffWidth * interpolatedTime;
targetView.getLayoutParams().height = (int) newHeight;
targetView.getLayoutParams().width = (int) newWidth;
targetView.requestLayout();
}
}
this is when enlarge animation is called :
I am using viewpager so i have to make padding negative to enlarge the card size :
ValueAnimator paddingAnimator = ValueAnimator.ofInt(20, -10).setDuration(400);
paddingAnimator.setInterpolator(new LinearInterpolator());
paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
int padding = (int) animation.getAnimatedValue();
view.setPadding(padding,
(int) DeviceUtils.convertDpToPx(50, v.getContext()), padding,
(int) DeviceUtils.convertDpToPx(50, v.getContext()));
view.requestLayout();
}
});
viewPagerItemSizeListener.onEnlarged();
EnlargeAnimation
enlargeAnimation =
new EnlargeAnimation(cardView, screenHeight, screenWidth);
enlargeAnimation.setDuration(400);
enlargeAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
paddingAnimator.start();
seeExampleText.setVisibility(View.INVISIBLE);
}
#Override
public void onAnimationEnd(Animation animation) {
stage.setVisibility(View.GONE);
cardEnlargedWidth = cardView.getLayoutParams().width;
cardEnlargedHeight = cardView.getLayoutParams().height;
crossContianer.setVisibility(View.VISIBLE);
detailTextContianer.setVisibility(View.VISIBLE);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
view.startAnimation(enlargeAnimation);
I guess no one is reading it, but if something is confusing about variables let me know i will edit the answer.

android: how to visible portion of a view with animation?

I have a invisible View behind of another View. I want to make this view visible with a translate animation and show only a part of the right side view.
like this:
I don't want to use 9 patch image and resize that.
this animation named "peek in"" in MS-PowerPoint.
You could try something like this
1) create a drawable resource rectangle_curved.xml as shown below
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" android:padding="10dp">
<solid android:color="#FFFFFF"/>
<corners
android:bottomRightRadius="350dp"
android:bottomLeftRadius="0dp"
android:topLeftRadius="0dp"
android:topRightRadius="350dp"/>
<stroke android:color="#50000000" android:width="2dp"/>
</shape>
2) Set this as the background of the view which is expanding and collapsing. Here I have used a Framelayout which is expanding and collapsing
<FrameLayout
android:id="#+id/shape"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:background="#drawable/rectangle_curved"/>
3) Create a class to handle the expand/collapse animation as follows
public class ResizeAnimation extends Animation {
private int mWidth;
private int mInitialWidth;
private View mView;
public ResizeAnimation(View view, int width) {
mView = view;
mWidth = width;
mInitialWidth = view.getWidth();
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
int newWidth = mInitialWidth + (int) ((mWidth - mInitialWidth) * interpolatedTime);
mView.getLayoutParams().width = newWidth;
mView.requestLayout();
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
}
#Override
public boolean willChangeBounds() {
return true;
}
}
4) Finally, use it like this. Create a function to handle animation as follows
private void animate(View shapeView, boolean isExpand) {
int width = isExpand ? 500 : 0; // 500 is the max width in pixels , you ca change it
ResizeAnimation anim = new ResizeAnimation(shapeView, width);
anim.setDuration(500);
shape.startAnimation(anim);
}
and call this function as follows
for expand animation : animate(myBackgroundView, true)
for collapse animation : animate(myBackgroundView, false)
EDIT:
in this line of step 4: int width = isExpand ? 500 : 0; use 1 instead of 0.
0 not work properly. I don't know why.
private void animate(View view, boolean isExpand) {
int width = isExpand ? 200 : 1; // 200 is the max width in pixels.
//use a factor for same width on all screen size.
DisplayMetrics metrics = getResources().getDisplayMetrics();
int factor =(int) metrics.density;
ResizeAnimation anim = new ResizeAnimation(view, width * factor);
anim.setDuration(500);
view.startAnimation(anim);
}

Scale transition animation

I want to create custom Transition for the Scale View elements in the ConstraintLayout. Inside the AutoTransition.java. We have Fade In, AdjustBounds and Fade Out. So performing simple Scale doesn't animate changes.
After some workaround I was able to Create custom Transition with Scale. And my point, it's to create Transition Set with Visiblity and Scale. With changes below this animation works well, despite Scale Pivot.
//..................... General method from Custom Scale Transition
#Nullable
#Override
public Animator createAnimator(#NonNull ViewGroup sceneRoot, #Nullable TransitionValues startValues,
#Nullable TransitionValues endValues) {
if (null == startValues || null == endValues) {
return null;
}
final View view = endValues.view;
final float startX = (float) startValues.values.get(PROPNAME_SCALE_X);
final float endX = (float) endValues.values.get(PROPNAME_SCALE_X);
final float startY = (float) startValues.values.get(PROPNAME_SCALE_Y);
final float endY = (float) endValues.values.get(PROPNAME_SCALE_Y);
final AnimatorSet set = new AnimatorSet();
final ValueAnimator animatorX = ValueAnimator.ofFloat(startX, endX);
animatorX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
final float value = (float) animation.getAnimatedValue();
view.setScaleX(value);
}
});
final ValueAnimator animatorY = ValueAnimator.ofFloat(startY, endY);
animatorY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
final float value = (float) animation.getAnimatedValue();
view.setScaleY(value);
}
});
set.playTogether(animatorX, animatorY);
return set;
}
//..................... Complete Set Transitions
public class ScaleVisibility extends TransitionSet {
public ScaleVisibility() {
setOrdering(ORDERING_TOGETHER);
addTransition(new Fade()).
addTransition(new Scale());
}
}
//..................... Code for starting Animation.
mConstraintSet.clone(mConstraintLayout);
Transition transition = new ScaleVisibility();
transition.setDuration(3000);
mConstraintSet.setScaleX(item.resource, item.scale);
mConstraintSet.setScaleY(item.resource, item.scale);
mConstraintSet.setTransformPivot(item.resource, 0.5f, 0.5f);
mConstraintSet.setVisibility(item.resource, item.visibility);
TransitionManager.beginDelayedTransition(mConstraintLayout, transition);
mConstraintSet.applyTo(mConstraintLayout);
//..................... Main XML. Trying to Animate input_send_button
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragment_input_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:id="#+id/input_background"
android:layout_width="match_parent"
android:layout_height="#dimen/chat_input_panel_height"
android:background="#color/white"
app:layout_constraintBottom_toBottomOf="parent"/>
<!--This View will change Visibility with Text Changes-->
<ImageButton
android:id="#+id/input_send_button"
android:layout_width="#dimen/chat_input_panel_height"
android:layout_height="0dp"
android:background="#android:color/transparent"
android:src="#drawable/chat_send"
android:tint="#color/primary"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="#+id/input_background"
app:layout_constraintRight_toRightOf="#+id/input_background"
app:layout_constraintTop_toTopOf="#+id/input_background"
tools:ignore="ContentDescription"
tools:visibility="visible"/>
</android.support.constraint.ConstraintLayout>
Main issue here, it's Scale animation Pivot, which is zero (top left corner of view). And I cannot change Transition pivot for Animated view. Neither with adding this property to the View.java nor ConstraintSet.javaparameters. So question it's how to change View Pivot for this Scale Transition. (As additional question, I was interested with changing Pivot for the default AdjustBounds Transition, where we changing view Height/Width).
Try resetting the pivot on each animation step. If you're looking for center pivot you can just slash through getWidth(), otherwise you need to capture another pivot value and use it.
Try this transition:
public class ScaleTransition extends Transition {
private final static String PROPNAME_SCALE_X = "PROPNAME_SCALE_X";
private final static String PROPNAME_SCALE_Y = "PROPNAME_SCALE_Y";
#Override
public void captureStartValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
#Override
public void captureEndValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
private void captureValues(TransitionValues values){
values.values.put(PROPNAME_SCALE_X, values.view.getScaleX());
values.values.put(PROPNAME_SCALE_Y, values.view.getScaleY());
}
#Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
if(endValues == null || startValues == null)
return null; // no values
float startX = (float) startValues.values.get(PROPNAME_SCALE_X);
float startY = (float) startValues.values.get(PROPNAME_SCALE_Y);
float endX = (float) endValues.values.get(PROPNAME_SCALE_X);
float endY = (float) endValues.values.get(PROPNAME_SCALE_Y);
if(startX == endX && startY == endY)
return null; // no scale to run
final View view = startValues.view;
PropertyValuesHolder propX = PropertyValuesHolder.ofFloat(PROPNAME_SCALE_X, startX, endX);
PropertyValuesHolder propY = PropertyValuesHolder.ofFloat(PROPNAME_SCALE_Y, startY, endY);
ValueAnimator valAnim = ValueAnimator.ofPropertyValuesHolder(propX, propY);
valAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
view.setPivotX(view.getWidth()/2f);
view.setPivotY(view.getHeight()/2f);
view.setScaleX((float) valueAnimator.getAnimatedValue(PROPNAME_SCALE_X));
view.setScaleY((float) valueAnimator.getAnimatedValue(PROPNAME_SCALE_Y));
}
});
return valAnim;
}
}
In order to create a custom transition(s) for TransitionManager you need to create a custom Transition or TransitionSet. For example:
TransitionSet transitionSet = new TransitionSet();
//your types of transitions
Transition first = new ChangeBounds();
Transition second = new Fade();
first.addTarget(firstView);
second.addTarget(secondView);
//Add your transitions to TransitionSet
transitionSet.addTransition(first).addTransition(second);
//Start transitions
TransitionManager.beginDelayedTransition(layout, transitionSet);
You can learn more about TransitionSet here and about Transition here

scale animation in DragShadowBuilder canvas during drag and drop

How can I create a custom DragShadowBuilder to animate the scaling of a view thats in the canvas instead of it just scaling to a set size?
For example I have an ImageView that is used for drag and drop and when I press on the ImageView the image needs to grow in the canvas thats used for drag and drop that you would see following your finger while you are dragging it to a drop zone.
I am able to get the image to scale to a set size using the following CustomDragShadowBuilder class found here but is there a way to animate the scaling?
public static class CustomDragShadowBuilder extends View.DragShadowBuilder {
private static final int SCALING_FACTOR = 4;
public CustomDragShadowBuilder(View view) {
super(view);
}
#Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
View v = getView();
final int width = v.getWidth() * SCALING_FACTOR;
final int height = v.getHeight() * SCALING_FACTOR;
shadowSize.set(width, height);
shadowTouchPoint.set(width / 2, height / 2);
}
#Override
public void onDrawShadow(Canvas canvas) {
canvas.scale(SCALING_FACTOR, SCALING_FACTOR);
getView().draw(canvas);
}
}
best solution I found is to animate the scale before start the dragging, something like that:
final View yourView;
ObjectAnimator scaleX = ObjectAnimator.ofFloat(yourView, "scaleX",4f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(yourView, "scaleY",4f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(scaleX, scaleY);
animSetXY.setDuration(duration);
final View.DragShadowBuilder shadow = new View.DragShadowBuilder(this);
animSetXY.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
v.startDrag(dragData, // the data to be dragged
new CustomDragShadowBuilder(yourView) , // the drag shadow builder
null,
0 // flags (not currently used, set to 0)
);
}
});
animSetXY.start();

Animating strike-through on a TextView

I have been searching a lot on how to animate the strike-through affect on a TextView to no results. Only thing I am getting on forums and StackOverflow is:
some_text_view.setPaintFlags(some_text_view.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG)
What I want to do is, animate the strike-through affect like in todo apps on Play Store e.g. Any.do has it on an item left-to-right swipe.
You have a couple of options:
Extend TextView and make a custom view which checks if the STRIKE_THRU_TEXT_FLAG is set and fires off an animation that will draw a small line on the text incrementing it's width on each frame of the animation.
Use an empty view and place it on your TextView (using RelativeLayout, FrameLayout etc). Make sure the dimensions of this view match exactly with your TextView. Then animate this view following the same strategy as before: Draw a horizontal line at the center of the view whose width is incremented at each frame of the animation.
If you want to know how to the animation itself, then you can look up Animator, AnimatorSet etc and their related guides.
I used this approach to make strikethrough animation:
private void animateStrikeThrough1(final TextView tv) {
final int ANIM_DURATION = 1000; //duration of animation in millis
final int length = tv.getText().length();
new CountDownTimer(ANIM_DURATION, ANIM_DURATION/length) {
Spannable span = new SpannableString(tv.getText());
StrikethroughSpan strikethroughSpan = new StrikethroughSpan();
#Override
public void onTick(long millisUntilFinished) {
//calculate end position of strikethrough in textview
int endPosition = (int) (((millisUntilFinished-ANIM_DURATION)*-1)/(ANIM_DURAT [ION/length));
endPosition = endPosition > length ?
length : endPosition;
span.setSpan(strikethroughSpan, 0, endPosition,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(span);
}
#Override
public void onFinish() {
}
}.start();
}
private fun TextView.startStrikeThroughAnimation(): ValueAnimator {
val span = SpannableString(text)
val strikeSpan = StrikethroughSpan()
val animator = ValueAnimator.ofInt(text.length)
animator.addUpdateListener {
span.setSpan(strikeSpan, 0, it.animatedValue as Int, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
text = span
invalidate()
}
animator.start()
return animator
}
private fun TextView.reverseStrikeThroughAnimation(): ValueAnimator {
val span = SpannableString(text.toString())
val strikeSpan = StrikethroughSpan()
val animator = ValueAnimator.ofInt(text.length, 0)
animator.addUpdateListener {
span.setSpan(strikeSpan, 0, it.animatedValue as Int, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
text = span
invalidate()
}
animator.start()
return animator
}
// Created by kot32 on 2017/10/26.
public class AnimationText extends TextView {
private boolean isAnimationStarted;
private float targetLength;
private float totalLength;
private Paint strikePaint;
private float startY;
//should always show Strike-Through
private boolean isDeleted;
public AnimationText(Context context, AttributeSet attrs) {
super(context, attrs);
strikePaint = new Paint();
strikePaint.setColor(Color.BLACK);
strikePaint.setAntiAlias(true);
strikePaint.setStyle(Paint.Style.FILL_AND_STROKE);
strikePaint.setStrokeWidth(5);
}
public AnimationText(Context context) {
super(context);
strikePaint = new Paint();
strikePaint.setColor(Color.BLACK);
strikePaint.setAntiAlias(true);
strikePaint.setStyle(Paint.Style.FILL_AND_STROKE);
strikePaint.setStrokeWidth(5);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isAnimationStarted) {
//画线
canvas.drawLine(0, startY, targetLength, startY, strikePaint);
}
if (isDeleted && !isAnimationStarted) {
canvas.drawLine(0, startY, totalLength, startY, strikePaint);
}
}
public void startStrikeThroughAnimation() {
totalLength = getWidth();
startY = (float) getHeight() / 2;
isAnimationStarted = true;
//利用动画逐渐画出一条删除线
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this, "targetLength", 0, totalLength);
objectAnimator.setInterpolator(new AccelerateInterpolator());
objectAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
isAnimationStarted = false;
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
objectAnimator.setDuration(300);
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
invalidate();
}
});
objectAnimator.start();
postInvalidate();
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
totalLength = getWidth();
}
public float getTargetLength() {
return targetLength;
}
public void setTargetLength(float targetLength) {
this.targetLength = targetLength;
}
}

Categories

Resources