English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Custom View Implementation of Alipay Swipe Effect in Android

This article introduces how to implement the following effects by using a custom View and property animation

The implementation idea is quite simple:

  • Draw a semi-transparent circle
  • Implement two animation effects, expanding when clicked and retracting when not clicked
  • Combine the above two steps using threading

First, let's take a look at the part of drawing a semi-transparent circle

public class ClickCircleView extends View {
 private Bitmap bitmap;
 private Paint paint;
 private Canvas canvas;
 private boolean isSpreadFlag = false;//Mark whether the emission is completed
 public boolean isSpreadFlag() {
  return isSpreadFlag;
 }
 public void setIsSpreadFlag(boolean isSpreadFlag) {
  this.isSpreadFlag = isSpreadFlag;
 }
 public ClickCircleView(Context context, int width, int height, int screenWidth, int screenHeight) {
  super(context);
  bitmap = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.ARGB_8888); // Set the width and height of the bitmap
  canvas = new Canvas();
  canvas.setBitmap(bitmap);
  paint = new Paint(Paint.DITHER_FLAG);
  paint.setAntiAlias(true);
  paint.setColor(Color.WHITE);
  paint.setStyle(Paint.Style.FILL);
  paint.setAlpha(50);
  canvas.drawCircle(screenWidth / 2, screenHeight / 2, width / 2 + 10, paint);
  invalidate();
 }
 @Override
 protected void onDraw(Canvas canvas) {
  canvas.drawBitmap(bitmap, 0, 0, null);
 }
}

As can be seen, the relevant properties are set on the brush, then directly call the canvas's drawCircle() method to draw a semi-transparent circle, and finally call the invalidate() method to refresh the View
It is necessary to override the parent class's onDraw() method, otherwise the custom View cannot take effect
We set a flag isSpreadFlag, which is used to mark whether the expansion animation is completed

Then we implement two animation effects

Click expansion animation

<set xmlns:android="http://schemas.android.com/apk/res/android">
 <objectAnimator
  android:duration="1000"
  android:propertyName="scaleY"
  android:valueFrom="1.0"
  android:valueTo="1.8"
  android:valueType="floatType" />
 <objectAnimator
  android:duration="1000"
  android:propertyName="scaleX"
  android:valueFrom="1.0"
  android:valueTo="1.8"
  android:valueType="floatType" />
</set>

It's very simple, just change the scale value to increase to1.8times

Non-click expansion and contraction animation

<set xmlns:android="http://schemas.android.com/apk/res/android"
 android:ordering="together">
 <objectAnimator
  android:duration="1000"
  android:propertyName="scaleX"
  android:valueFrom="1.0"
  android:valueTo="1.2"
  android:valueType="floatType" />
 <objectAnimator
  android:duration="1000"
  android:propertyName="scaleY"
  android:valueFrom="1.0"
  android:valueTo="1.2"
  android:valueType="floatType" />
 <objectAnimator
  android:duration="1000"
  android:propertyName="scaleX"
  android:startOffset="1000"
  android:valueFrom="1.2"
  android:valueTo="1.0"
  android:valueType="floatType" />
 <objectAnimator
  android:duration="1000"
  android:propertyName="scaleY"
  android:startOffset="1000"
  android:valueFrom="1.2"
  android:valueTo="1.0"
  android:valueType="floatType" />
</set>

Similar to the previous animation, the startOffset parameter can be used to control the running order of the Animation, such as Android:startOffset=""10"00" indicates the animation delay of the property setting1Second execution

Then it is the part of executing animation and logic with threads

Animation part when not clicked

mXiuyixiuButton.post(new Runnable() {
   @Override
   public void run() {
    clickCircleView = new ClickCircleView(CustomView1.this, mXiuyixiuButton.getWidth()
      , mXiuyixiuButton.getHeight(), mXiuyixiuLayout.getMeasuredWidth(),
      mXiuyixiuLayout.getMeasuredHeight());
    clickCircleView.setVisibility(View.VISIBLE);
    mXiuyixiuLayout.addView(clickCircleView);
    mXiuyixiuLayout.postInvalidate();
    // Load animation
    final Animator anim = AnimatorInflater.loadAnimator(CustomView1.this,
      R.animator.circle_scale_animator);
    anim.addListener(new AnimatorListenerAdapter() {
     @Override
     public void onAnimationEnd(Animator animation) {
      if (anim != null) {
       anim.start();//Loop animation execution
      }
     }
    });
    anim.setTarget(clickCircleView);
    anim.start();
   }
  });

After initializing clickCircleView, add this view to the parent layout, then load the animation and set it to loop, and finally use postInvalidate() to refresh the view in the child thread

Animation part when clicked

mXiuyixiuButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    clickCircleView.setVisibility(View.GONE);//Launch the circle, hiding the loop animation View
    final ClickCircleView item = new ClickCircleView(CustomView1.this, mXiuyixiuButton.getWidth()
      , mXiuyixiuButton.getHeight(), mXiuyixiuLayout.getWidth(),
      mXiuyixiuLayout.getHeight());
    Animator spreadAnim = AnimatorInflater.loadAnimator(CustomView1.this,
      R.animator.circle_spread_animator);
    spreadAnim.addListener(new AnimatorListenerAdapter() {
     @Override
     public void onAnimationEnd(Animator animation) {
      item.setIsSpreadFlag(true);//Mark after the animation is completed
     }
    });
    spreadAnim.setTarget(item);
    spreadAnim.start();
    clickCircleViewList.add(item);
    mXiuyixiuLayout.addView(item);
    mXiuyixiuLayout.invalidate();
    handler.post(circleViewRunnable);
   }
  });


Hide the non-click animation, initialize the ClickCircleView, add the view to the List, and then add it to the parent layout. After that, load the animation, add the isSpreadFlag mark at the end of the animation, and finally call the invalidate() method to refresh the view and start the thread.

Thread part

private Runnable circleViewRunnable = new Runnable() {
  public void run() {
   for (int i = 0; i < clickCircleViewList.size(); i++) {
    if (clickCircleViewList.get(i).isSpreadFlag()) {
     mXiuyixiuLayout.removeView(clickCircleViewList.get(i));
     clickCircleViewList.remove(i);
     mXiuyixiuLayout.postInvalidate();
    }
   }
   if (clickCircleViewList.size() <= 0) {
    clickCircleView.setVisibility(View.VISIBLE);
   }
   handler.postDelayed(this, 100);
  }
 };

Traverse the list, remove the view with the isSpreadFlag marker from the list and the parent layout, and refresh the view. Finally, if the list is empty, the animation is displayed when not clicked

Remember to remove the thread in onDestroy() at the end

@Override
 protected void onDestroy() {
  super.onDestroy();
  handler.removeCallbacks(circleViewRunnable);
 }

Using custom View with property animation to achieve this effect has a high degree of coupling. However, compared with using custom View completely, this method is more smooth. Most of the code is implemented based on the code from other blogs, but if you just use it without summarizing, it will not become your own knowledge. Therefore, this blog was written.

Reference: Several ways to implement Alipay's Xiu Xiu with android

That's all for this article. I hope it will be helpful to everyone's learning and also hope everyone will support the Yelling Tutorial more.

Declaration: The content of this article is from the network, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, does not edit the content manually, and does not assume relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (When sending an email, please replace # with @ for reporting, and provide relevant evidence. Once verified, this site will immediately delete the suspected infringing content.)