FlingAnimation使用

本文内容

使用GestureDetector或者VelocityTracker获得速度

FlingAnimation的初始属性设置

FlingAnimation的动画配置

FlingAnimation的一些要点说明

本文示例对应的代码

FlingAnimation是甩动(Fling)效果动画, 即指尖离开屏幕后的惯性动画。设置一个初始属性值及速度值,就可以输出一个逐渐减小的属性值。最终以设置的最小值、最大值、最小可见值为停止条件。其中要注意的是速度单位是Pixel/second。添加库

implementation 'com.android.support:support-dynamic-animation:28.0.0'

使用GestureDetector或者VelocityTracker获得速度

首先,可以从利用GestureDetector的监测Fling动作, 在监听API中获得速度VelocityX和VelocityY,并且在这个onFling回调中使用FlingAnimation, 像这样

	public void init(){
    	GestureDetector  gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                //TODO FlingAnimation here
                //FlingAnimation fling = new FlingAnimation(target, DynamicAnimation.TRANSLATION_X);
                //fling.setStartVelocity(velocityX)
                //     .start();
                return false;
            }
        });
	}

    @Override
    public boolean onTouchEvent(MotionEvent event){
        gestureDetector.onTouchEvent(event);
        return super.onTouchEvent(event)
    }

或者从VelocityTracker获得速度, 像这样

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //滑动速度
        int index = event.getActionIndex();
        int action = event.getActionMasked();
        int pointerId = event.getPointerId(index);
        switch (action){
            case MotionEvent.ACTION_DOWN:
                if(velocityTracker==null){
                    velocityTracker = VelocityTracker.obtain();
                }else{
                    velocityTracker.clear();
                }
                velocityTracker.addMovement(event);
                break;
            case MotionEvent.ACTION_MOVE:
                velocityTracker.addMovement(event);
                velocityTracker.computeCurrentVelocity(1000);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                float velocityX = velocityTracker.getXVelocity(pointerId);
                float velocityY = velocityTracker.getYVelocity(pointerId);
                //TODO FlingAnimation here
                //FlingAnimation fling = new FlingAnimation(target, DynamicAnimation.TRANSLATION_X);
                //fling.setStartVelocity(velocityX)
                //      .start();
                velocityTracker.recycle();
                velocityTracker=null;
                break;
        }
        gestureDetectorCompat.onTouchEvent(event);
        return true;
    }

FlingAnimation的初始属性设置

Fling动画要设置初始速度值及初始属性,其他属性都有默认值的,可以不设置。

初始属性,FloatPropertyCompat的getValue为FlingAnimation获取初始属性值获取的地方,而setValue为FlingAnimation动画后设置属性值的回调,属性如下

               FlingAnimation fling = new FlingAnimation(target, new FloatPropertyCompat<View>(AStrName) {
                    @Override
                    public float getValue(View object) {
                        //这里返回要实现变化的
                        //比如可以设置为target.getTranslationX(),即当前target的X轴移动距离
                        return startValue;
                    }

                    @Override
                    public void setValue(View object, float value) {
                        //在startValue基础上的变化属性属性值
                        //如可以target.setTranslationX(value);
                    }
                });

其实,Google在DynamicAnimation中已经写好了很多常用的属性FloatProperty可以直接使用,例如

public abstract class DynamicAnimation<T extends DynamicAnimation<T>>
        implements AnimationHandler.AnimationFrameCallback {
    public abstract static class ViewProperty extends FloatPropertyCompat<View> {
        private ViewProperty(String name) {
            super(name);
        }
    }
    public static final ViewProperty TRANSLATION_X = new ViewProperty("translationX") {
        @Override
        public void setValue(View view, float value) {
            view.setTranslationX(value);
        }

        @Override
        public float getValue(View view) {
            return view.getTranslationX();
        }
    };
    .......
    /**
     * View's translationX property.
     */
    public static final ViewProperty TRANSLATION_X = new ViewProperty("translationX") {
	
	...

    /**
     * View's translationY property.
     */
    public static final ViewProperty TRANSLATION_Y = new ViewProperty("translationY") {
	...

    /**
     * View's translationZ property.
     */
    public static final ViewProperty TRANSLATION_Z = new ViewProperty("translationZ") {
	...

    /**
     * View's scaleX property.
     */
    public static final ViewProperty SCALE_X = new ViewProperty("scaleX") {
	...

    /**
     * View's scaleY property.
     */
    public static final ViewProperty SCALE_Y = new ViewProperty("scaleY") {
	...
	
    /**
     * View's rotation property.
     */
    public static final ViewProperty ROTATION = new ViewProperty("rotation") {
	...

    /**
     * View's rotationX property.
     */
    public static final ViewProperty ROTATION_X = new ViewProperty("rotationX") {
	...

    /**
     * View's rotationY property.
     */
    public static final ViewProperty ROTATION_Y = new ViewProperty("rotationY") {
	...
	
    /**
     * View's x property.
     */
    public static final ViewProperty X = new ViewProperty("x") {

    /**
     * View's y property.
     */
    public static final ViewProperty Y = new ViewProperty("y") {
	...
	
    /**
     * View's z property.
     */
    public static final ViewProperty Z = new ViewProperty("z") {
	...

    /**
     * View's alpha property.
     */
    public static final ViewProperty ALPHA = new ViewProperty("alpha") {
	...

    // Properties below are not RenderThread compatible
    /**
     * View's scrollX property.
     */
    public static final ViewProperty SCROLL_X = new ViewProperty("scrollX") {
	...
	/**
     * View's scrollY property.
     */
    public static final ViewProperty SCROLL_Y = new ViewProperty("scrollY") {
	...

FlingAnimation的动画配置

flingAnimation.setStartVelocity(velocityX)//初始速度 
	   .setStartValue(target.getTranslationX())//初始值,这里设置会覆盖propety中的getValue值
	   .setMinValue(target.getTranslationX()-500)//value到达最小会停止动画
	   .setMaxValue(target.getTranslationX()+500)//value到达最大会停止动画
	   .setMinimumVisibleChange(100)//到达最小变化可见值,就是用于判断变化是否小到可以设置为静止状态了,然后停止动画
	   .setFriction(1f)//摩擦力,越大越快静止
	   .addEndListener(endListener)//动画状态更新
	   .addUpdateListener(updateListener)//动画值更新
	   .start();//启动

FlingAnimation的一些要点说明

  • flingAnimation.setStartValue(target.getTranslationX())//初始值,这里设置会覆盖FloatPropertyCompat中的getValue值
  • flingAnimation.addUpdateListener(updateListener)//动画值更新,这个更新会在FloatPropertyCompat中的setValue值执行后回调, 可以在DynamicAnimation的源码中看到
    /**DynamicAnimation
     * Updates the property value through the corresponding setter.
     */
    void setPropertyValue(float value) {
        mProperty.setValue(mTarget, value);
        for (int i = 0; i < mUpdateListeners.size(); i++) {
            if (mUpdateListeners.get(i) != null) {
                mUpdateListeners.get(i).onAnimationUpdate(this, mValue, mVelocity);
            }
        }
        removeNullEntries(mUpdateListeners);
    }

示例代码

package com.gyso.developerapplication.touchAndinput;

import androidx.appcompat.app.AppCompatActivity;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.FlingAnimation;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import android.graphics.PointF;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.TextView;
import com.gyso.developerapplication.databinding.ActivityFlingBinding;

public class FlingActivity extends AppCompatActivity {
    private static final String TAG = FlingActivity.class.getSimpleName();
    TextView target;
    PointF deltaMove = new PointF();
    VelocityTracker velocityTracker;
    private FlingAnimation flingX;
    private FlingAnimation flingY;
    private float minFlingVelocity;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityFlingBinding binding = ActivityFlingBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        target = binding.flingTestTv;
        ViewConfiguration vc = ViewConfiguration.get(this);
        minFlingVelocity = vc.getScaledMinimumFlingVelocity();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //滑动速度
        int index = event.getActionIndex();
        int action = event.getActionMasked();
        int pointerId = event.getPointerId(index);
        switch (action){
            case MotionEvent.ACTION_DOWN:
                if(flingX!=null){
                    flingX.cancel();
                }
                if(flingY!=null){
                    flingY.cancel();
                }
                if(velocityTracker==null){
                    velocityTracker = VelocityTracker.obtain();
                }else{
                    velocityTracker.clear();
                }
                velocityTracker.addMovement(event);
                break;
            case MotionEvent.ACTION_MOVE:
                //跟随手指移动
                float distanceX = event.getRawX() - deltaMove.x;
                float distanceY = event.getRawY() - deltaMove.y;
                target.setTranslationX(target.getTranslationX()+distanceX);
                target.setTranslationY(target.getTranslationY()+distanceY);

                //速度计算
                velocityTracker.addMovement(event);
                velocityTracker.computeCurrentVelocity(1000);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                float velocityX = velocityTracker.getXVelocity(pointerId);
                float velocityY = velocityTracker.getYVelocity(pointerId);
                //TODO FlingAnimation
                startFlingAnimation(velocityX,velocityY);
                //startSampleFlingAnimation(velocityX, velocityY)
                velocityTracker.recycle();
                velocityTracker=null;
                break;
        }
        deltaMove.set(event.getRawX(),event.getRawY());
        return true;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return this.onTouchEvent(ev);
    }

    /**
     * sample fling animation
     * @param velocityX vx
     * @param velocityY vy
     */
    public void startSampleFlingAnimation(float velocityX, float velocityY){
        new FlingAnimation(target, DynamicAnimation.TRANSLATION_X)
                .setStartVelocity(velocityX)
                .start();
        new FlingAnimation(target, DynamicAnimation.TRANSLATION_Y)
                .setStartVelocity(velocityY)
                .start();
    }

    /**
     * start fling animation
     * @param velocityX vx
     * @param velocityY vy
     */
    public void startFlingAnimation(float velocityX, float velocityY) {
        if(flingX!=null){
            flingX.cancel();
        }
        if(flingY!=null){
            flingY.cancel();
        }
        flingX = new FlingAnimation(target, new FloatPropertyCompat<View>("") {
            @Override
            public float getValue(View object) {
                return target.getTranslationX();
            }

            @Override
            public void setValue(View object, float value) {
                target.setTranslationX(value);
            }
        });
        flingX.setStartVelocity(velocityX)
               .addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {
                    @Override
                    public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) {
                        float leftNow = target.getLeft()+target.getTranslationX();
                        float right = target.getLeft()+target.getWidth()+target.getTranslationX();
                        boolean leftBounce = leftNow<0 && velocity<0 ;
                        boolean rightBounce = right >((View)target.getParent()).getRight() && velocity>0;
                        if(leftBounce || rightBounce){
                            flingX.setStartVelocity(-velocity);
                        }
                    }
                })
                .start();

        flingY = new FlingAnimation(target, new FloatPropertyCompat<View>("") {
            @Override
            public float getValue(View object) {
                return object.getTranslationY();
            }

            @Override
            public void setValue(View object, float value) {
                object.setTranslationY(value);
            }
        });
        flingY.setStartVelocity(velocityY)
              .addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {
                  @Override
                  public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) {
                      float topNow = target.getTop()+target.getTranslationY();
                      float bottomNow = target.getTop()+target.getHeight()+target.getTranslationY();
                      boolean topBounce = topNow<0 && velocity<0;
                      boolean bottomBounce = bottomNow >((View)target.getParent()).getBottom() && velocity>0;
                      if(topBounce || bottomBounce){
                          flingY.setStartVelocity(-velocity);
                      }
                  }
              })
              .start();
    }
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:textColor="@color/tran"
    android:background="@color/white_gray"
    tools:context=".touchAndinput.FlingActivity">

    <TextView
        android:id="@+id/fling_test_tv"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="滑块"
        android:textSize="30sp"
        android:textColor="@android:color/white"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值