目录
使用GestureDetector或者VelocityTracker获得速度
FlingAnimation是甩动(Fling)效果动画, 即指尖离开屏幕后的惯性动画。设置一个初始属性值及速度值,就可以输出一个逐渐减小的属性值。最终以设置的最小值、最大值、最小可见值为停止条件。其中要注意的是速度单位是Pixel/second。
使用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();
}
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END