美文网首页Android 基础
Android 基础之动画和手势

Android 基础之动画和手势

作者: Kevin_小飞象 | 来源:发表于2019-02-12 14:51 被阅读3次

    Android中动画分为3种:

    1. Tween Animation(补间动画):通过对场景的对象不断做图像变换(平移、缩放、旋转)产生动画效果,即是一种渐变动画。
    2. Frame Animation(逐帧动画):顺序播放事先做好的图像,是一种画面转换动画。
    3. Property Animation(属性动画):通过动态地改变对象的属性从而达到动画效果,属性动画为 API 11 新特性。

    Tween Animation

    根据不同的动画效果,补间动画分为 4 种动画:

    • 平移动画(Translate)画面位置移动动画效果
    • 缩放动画(scale)渐变尺寸伸缩动画效果
    • 旋转动画(rotate)画面旋转动画效果
    • 透明度动画(alpha) 渐变透明度动画效果
      这四种动画实现方式都是通过 Animation 类和 AnimationUtils 配合实现。

    具体使用

    方法1.通过xml实现
    • 步骤1:在 res/anim 的文件夹里创建动画效果 rotate.xml 文件
    • 步骤2:根据不同动画效果的语法设置不同动画参数,从而实现动画效果。
      rotate.xml
    <?xml version="1.0" encoding="utf-8"?>
    <rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:fromDegrees="0" // 动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
        android:toDegrees="270" // 动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
        android:pivotX="50%" // 旋转轴点的x坐标
        android:pivotY="0" // 旋转轴点的y坐标
        /> 
    
    • 步骤3:在 Java 代码中创建 Animation 对象并播放动画
      MainActivity.java
    public class MainActivity extends AppCompatActivity {
        private Button mButton;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mButton = findViewById(R.id.button);
    
            Animation rotateAnimation = AnimationUtils.loadAnimation(this, R.anim.rotate);
    
            mButton.startAnimation(rotateAnimation);
    
        }
    }
    
    方法2:代码设置
            Button mButton = (Button) findViewById(R.id.Button);
              Animation rotateAnimation = new RotateAnimation(0,270,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
            rotateAnimation.setDuration(3000);
            mButton.startAnimation(rotateAnimation);
    

    参考:Android 动画:手把手教你使用 补间动画

    Frame Animation

    Frame Animation 是顺序播放事先做好的图像,跟电影类似。不同于animation package,Android SDK 提供了另外一个类 AnimationDrawable 来定义使用 Frame Animation。

    具体使用

    • 首先将动画资源(即每张图片资源)放到 drawable文件夹里
    • 方式1: XML 实现
      step1:在 res/drawable-hdpi 的文件夹里创建动画效果 frame.xml 文件:
      frame.xml
    <?xml version="1.0" encoding="utf-8"?>
     
    <animation-list
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:oneshot="true"
      >
           <item android:drawable="@drawable/p1" android:duration="1000"></item>
           <item android:drawable="@drawable/p2" android:duration="1000"></item>
           <item android:drawable="@drawable/p3" android:duration="1000"></item>
           <item android:drawable="@drawable/p4" android:duration="1000"></item>
           <item android:drawable="@drawable/p5" android:duration="1000"></item>
           <item android:drawable="@drawable/p6" android:duration="1000"></item>
    </animation-list>
    
    

    step2:在Java中实现
    FrameActivity.java

    public class FrameActivity extends AppCompatActivity {
        private Button btn_startFrame,btn_stopFrame;
        private ImageView iv;
        private AnimationDrawable animationDrawable;
    
            iv = (ImageView) findViewById(R.id.iv);
            btn_startFrame = (Button) findViewById(R.id.btn_startFrame);
            btn_stopFrame = (Button) findViewById(R.id.btn_stopFrame);
    
    
            <-- 开始动画 -->
            btn_startFrame.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    iv.setImageResource(R.drawable.frame);
                    // 1. 设置动画
                    animationDrawable = (AnimationDrawable) iv.getDrawable();
                    // 2. 获取动画对象
                    animationDrawable.start();
                    // 3. 启动动画
                }
            });
            //停止动画
            btn_stopFrame.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    
                    iv.setImageResource(R.drawable.frame);
                    // 1. 设置动画
                    animationDrawable = (AnimationDrawable) iv.getDrawable();
                    // 2. 获取动画对象
                    animationDrawable.stop();
                    // 3. 暂停动画
                }
            });
    
        }
    }
    
    • 方式2:在代码中实现
    <-- 直接从drawable文件夹获取动画资源(图片) -->
            animationDrawable = new AnimationDrawable();
            for (int i = 0; i <= 25; i++) {
                int id = getResources().getIdentifier("a" + i, "drawable", getPackageName());
                Drawable drawable = getResources().getDrawable(id);
                animationDrawable.addFrame(drawable, 100);
            }
    
            <-- 开始动画 -->
            btn_startFrame.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    animationDrawable.setOneShot(true);
                    iv.setImageDrawable(animationDrawable);
                    // 获取资源对象
                    animationDrawable.stop();
                     // 特别注意:在动画start()之前要先stop(),不然在第一次动画之后会停在最后一帧,这样动画就只会触发一次
                    animationDrawable.start();
                    // 启动动画
                   
                }
            });
    
             <-- 停止动画 -->
            btn_stopFrame.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    animationDrawable.setOneShot(true);
                    iv.setImageDrawable(animationDrawable);
                    animationDrawable.stop();
                }
            });
    

    参考:Android 逐帧动画:关于 逐帧动画 的使用都在这里了!

    Property Animation

    原理

    属性动画要求动画作用的对象提供该属性的 get 和 set 方法,属性动画根据你传递的该熟悉的初始值和最终值,以动画的效果多次去调用 set 方法,每次传递给 set 方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终值。总结一下,你对 object 的属性 xxx 做动画,如果想让动画生效,要同时满足两个条件:

    1. object 必须要提供 setXxx 方法,如果动画的时候没有传递初始值,那么还要提供 getXxx 方法,因为系统要去拿 xxx 属性的初始值(如果这条不满足,程序直接 Crash)

    2. object 的 setXxx 对属性 xxx 所做的改变必须能够通过某种方法反映出来,比如会带来 UI 的改变啥的(如果这条不满足,动画无效果但不会 Crash)

    以上条件缺一不可。

    具体使用

    1.ValueAnimator 类
    ValueAnimator类中有3个重要方法:

    1. ValueAnimator.ofInt(int values)作用:将初始值以整型数值的形式过渡到结束值
    2. ValueAnimator.ofFloat(float values)作用:将初始值以浮点型数值的形式过渡到结束值
    3. ValueAnimator.ofObject(int values) 作用:将初始值以对象的形式过渡到结束值

    实例:按钮的宽度从 150px 放大到 500px。

    public class MainActivity extends AppCompatActivity {
        private Button mButton;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mButton = findViewById(R.id.button);
    
            ValueAnimator valueAnimator = ValueAnimator.ofInt(mButton.getLayoutParams().width, 500);
    
            valueAnimator.setDuration(2000);
    
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animator) {
    
                    int currentValue = (Integer) animator.getAnimatedValue();
    
                    mButton.getLayoutParams().width = currentValue;
    
                    mButton.requestLayout();
    
                }
            });
            valueAnimator.start();
        }
    }
    
    

    效果图:


    效果图

    2.ObjectAnimator类

    旋转

    public class MainActivity extends AppCompatActivity {
        private Button mButton;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mButton = findViewById(R.id.button);
    
            ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "rotation", 0f, 360f);
            animator.setDuration(5000);
            animator.start();
        }
    }
    

    效果图


    旋转

    参考:Android 属性动画:这是一篇很详细的 属性动画 总结&攻略

    手势检测(GestureDetector)

    在开发 Android 手机应用过程中,可能需要对一些手势作出响应,如:单击、双击、长按、滑动、缩放等。这些都是很常用的手势。
    实例:轨迹球
    FailingBall.java

    package com.scarf.demo007.widget;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.graphics.RectF;
    import android.os.Handler;
    import android.support.annotation.Nullable;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.view.View;
    
    import com.scarf.demo007.R;
    
    /**
     * Created on 2019/2/12 15:09
     * 轨迹球
     * @author Scarf Gong
     */
    public class FailingBall extends View {
        private int mWidth;             // 宽度
        private int mHeight;            // 高度
    
        private float mStartX = 0;        // 小方块开始位置X
        private float mStartY = 0;        // 小方块开始位置Y
        private float mEdgeLength = 200;  // 边长
        private RectF mRect = new RectF(mStartX, mStartY, mStartX + mEdgeLength, mStartY + mEdgeLength);
    
        private float mFixedX = 0;  // 修正距离X
        private float mFixedY = 0;  // 修正距离Y
    
        private Paint mPaint;
    
        private GestureDetector mGestureDetector;
        private boolean mCanFail = false;   // 是否可以拖动
    
        private float mSpeedX = 0;
        private float mSpeedY = 0;
    
        private Boolean mXFixed = false;
        private Boolean mYFixed = false;
    
        private Bitmap mBitmap;
    
        public FailingBall(Context context) {
            this(context,null);
        }
    
        public FailingBall(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs,0);
        }
    
        public FailingBall(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
    
            mGestureDetector = new GestureDetector(context, mSimpleOnGestureListener);
    
            mPaint = new Paint();
            mPaint.setColor(Color.BLACK);
            mPaint.setAntiAlias(true);
    
            mBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.ball);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mWidth = w;
            mHeight = h;
            mStartX = (w - mEdgeLength) / 2;
            mStartY = (h - mEdgeLength) / 2;
            refreshRectByCurrentPoint();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawOval(mRect, mPaint);
            canvas.drawBitmap(mBitmap, new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight()),
                    mRect, mPaint);
        }
    
        // 每 100 ms 更新一次
        private Handler mHandler = new Handler();
        private Runnable mRunnable = new Runnable() {
            @Override
            public void run() {
                mStartX = mStartX + mSpeedX / 30;
                mStartY = mStartY + mSpeedY / 30;
                //mSpeedX = mSpeedX > 0 ? mSpeedX - 10 : mSpeedX + 10;
                //mSpeedY = mSpeedY > 0 ? mSpeedY - 10 : mSpeedY + 10;
                mSpeedX *= 0.97;
                mSpeedY *= 0.97;
                if (Math.abs(mSpeedX) < 10) {
                    mSpeedX = 0;
                }
                if (Math.abs(mSpeedY) < 10) {
                    mSpeedY = 0;
                }
                if (refreshRectByCurrentPoint()) {
                    // 转向
                    if (mXFixed) {
                        mSpeedX = -mSpeedX;
                    }
                    if (mYFixed) {
                        mSpeedY = -mSpeedY;
                    }
                }
                invalidate();
                if (mSpeedX == 0 && mSpeedY == 0) {
                    mHandler.removeCallbacks(this);
                    return;
                }
                mHandler.postDelayed(this, 33);
            }
        };
    
        private GestureDetector.SimpleOnGestureListener mSimpleOnGestureListener = new
                GestureDetector.SimpleOnGestureListener() {
                    @Override
                    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float
                            velocityY) {
                        Log.e("Failing", velocityX + " : " + velocityY);
                        if (!mCanFail) return false;
                        mSpeedX = velocityX;
                        mSpeedY = velocityY;
                        mHandler.removeCallbacks(mRunnable);
                        mHandler.postDelayed(mRunnable, 0);
                        return super.onFling(e1, e2, velocityX, velocityY);
                    }
                };
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            mGestureDetector.onTouchEvent(event);
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    if (contains(event.getX(), event.getY())) {
                        mCanFail = true;
                        mFixedX = event.getX() - mStartX;
                        mFixedY = event.getY() - mStartY;
                        mSpeedX = 0;
                        mSpeedY = 0;
                    } else {
                        mCanFail = false;
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (!mCanFail) {
                        break;
                    }
                    mStartX = event.getX() - mFixedX;
                    mStartY = event.getY() - mFixedY;
                    if (refreshRectByCurrentPoint()) {
                        mFixedX = event.getX() - mStartX;
                        mFixedY = event.getY() - mStartY;
                    }
                    invalidate();
                    break;
            }
            return true;
        }
    
        private Boolean contains(float x, float y) {
            float radius = mEdgeLength / 2;
            float centerX = mRect.left + radius;
            float centerY = mRect.top + radius;
            return Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2)) <= radius;
        }
    
        /**
         * 刷新方块位置
         *
         * @return true 表示修正过位置, false 表示没有修正过位置
         */
        private Boolean refreshRectByCurrentPoint() {
            Boolean fixed = false;
            mXFixed = false;
            mYFixed = false;
            // 修正坐标
            if (mStartX < 0) {
                mStartX = 0;
                fixed = true;
                mXFixed = true;
            }
            if (mStartY < 0) {
                mStartY = 0;
                fixed = true;
                mYFixed = true;
            }
            if (mStartX + mEdgeLength > mWidth) {
                mStartX = mWidth - mEdgeLength;
                fixed = true;
                mXFixed = true;
            }
            if (mStartY + mEdgeLength > mHeight) {
                mStartY = mHeight - mEdgeLength;
                fixed = true;
                mYFixed = true;
            }
            mRect.left = mStartX;
            mRect.top = mStartY;
            mRect.right = mStartX + mEdgeLength;
            mRect.bottom = mStartY + mEdgeLength;
            return fixed;
        }
    }
    
    

    布局文件:
    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 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"
    tools:context=".MainActivity">
    
        <com.scarf.demo007.widget.FailingBall
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </LinearLayout>
    

    效果图


    轨迹球

    缩放手势检测(ScaleGestureDecetor)

    缩放手势对于大部分 Android 工程师来说,需要用到的机会比较少,它最常见于以下的一些应用场景中,例如:图片浏览,图片编辑(贴图效果)、网页缩放、地图、文本阅读(通过缩放手势调整文字大小)等。

    代码:

    /**
     * Created on 2019/2/12 15:30
     *
     * @author Scarf Gong
     */
    public class GestureDemoView extends View {
        GestureDetector mGestureDetector;
        ScaleGestureDetector mScaleGestureDetector;
    
        // 画布当前的 Matrix, 用于获取当前画布的一些状态信息,例如缩放大小,平移距离等
        private Matrix mCanvasMatrix = new Matrix();
    
        // 将用户触摸的坐标转换为画布上坐标所需的 Matrix, 以便找到正确的缩放中心位置
        private Matrix mInvertMatrix = new Matrix();
    
        // 所有用户触发的缩放、平移等操作都通过下面的 Matrix 直接作用于画布上,
        // 将系统计算的一些初始缩放平移信息与用户操作的信息进行隔离,让操作更加直观
        private Matrix mUserMatrix = new Matrix();
    
        private Bitmap mBitmap;
    
        // 基础的缩放和平移信息,该信息与用户的手势操作无关
        private float mBaseScale;
        private float mBaseTranslateX;
        private float mBaseTranslateY;
    
        private Paint mPaint;
    
        public GestureDemoView(Context context) {
            super(context);
        }
    
        public GestureDemoView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            mPaint = new Paint();
            mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
            initGesture(context);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
    
            if (mBitmap.getWidth() * 1.0f / mBitmap.getHeight() > w * 1.0f / h) {
                mBaseScale = w * 1.0f / mBitmap.getWidth();
                mBaseTranslateX = 0;
                mBaseTranslateY = (h - mBitmap.getHeight() * mBaseScale) / 2;
            } else {
                mBaseScale = h * 1.0f / mBitmap.getHeight() * 1.0f;
                mBaseTranslateX = (w - mBitmap.getWidth() * mBaseScale) / 2;
                mBaseTranslateY = 0;
            }
    
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(6);
            canvas.translate(mBaseTranslateX, mBaseTranslateY);
            canvas.scale(mBaseScale, mBaseScale);
    
            canvas.save();
            canvas.concat(mUserMatrix);
    
            mCanvasMatrix = canvas.getMatrix();
            mCanvasMatrix.invert(mInvertMatrix);
    
            canvas.drawBitmap(mBitmap, 0, 0, mPaint);
            canvas.restore();
        }
    
    
        //--- 手势处理 ----------------------------------------------------------------------------------
    
        private void initGesture(Context context) {
            mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                    float scale = getMatrixValue(MSCALE_X, mCanvasMatrix);
                    mUserMatrix.preTranslate(-distanceX / scale, -distanceY / scale);
                    //fixTranslate();   // 在用户滚动时不进行修正,保证用户滚动时也有响应, 在用户抬起手指后进行修正
                    invalidate();
                    return true;
                }
    
                @Override
                public boolean onDoubleTap(MotionEvent e) {
                    if (!mUserMatrix.isIdentity()) {
                        mUserMatrix.reset();
                    } else {
                        float[] points = mapPoint(e.getX(), e.getY(), mInvertMatrix);
                        mUserMatrix.postScale(MAX_SCALE, MAX_SCALE, points[0], points[1]);
                    }
                    fixTranslate();
                    invalidate();
                    return true;
                }
            });
    
            mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
                @Override
                public boolean onScale(ScaleGestureDetector detector) {
                    float scaleFactor = detector.getScaleFactor();
                    float fx = detector.getFocusX();
                    float fy = detector.getFocusY();
                    float[] points = mapPoint(fx, fy, mInvertMatrix);
                    scaleFactor = getRealScaleFactor(scaleFactor);
                    mUserMatrix.preScale(scaleFactor, scaleFactor, points[0], points[1]);
                    fixTranslate();
                    invalidate();
                    return true;
                }
    
            });
        }
    
        // 修正缩放
        private void fixTranslate() {
            // 对 Matrix 进行预计算,并根据计算结果进行修正
            Matrix viewMatrix = getMatrix();    // 获取当前控件的Matrix
            viewMatrix.preTranslate(mBaseTranslateX, mBaseTranslateY);
            viewMatrix.preScale(mBaseScale, mBaseScale);
            viewMatrix.preConcat(mUserMatrix);
            Matrix invert = new Matrix();
            viewMatrix.invert(invert);
            Rect rect = new Rect();
            getGlobalVisibleRect(rect);
    
            float userScale = getMatrixValue(MSCALE_X, mUserMatrix);
            float scale = getMatrixValue(MSCALE_X, viewMatrix);
    
            float[] center = mapPoint(mBitmap.getWidth() / 2.0f, mBitmap.getHeight() / 2.0f, viewMatrix);
            float distanceX = center[0] - getWidth() / 2.0f;
            float distanceY = center[1] - getHeight() / 2.0f;
            float[] wh = mapVectors(mBitmap.getWidth(), mBitmap.getHeight(), viewMatrix);
    
            if (userScale <= 1.0f) {
                mUserMatrix.preTranslate(-distanceX / scale, -distanceY / scale);
            } else {
                float[] lefttop = mapPoint(0, 0, viewMatrix);
                float[] rightbottom = mapPoint(mBitmap.getWidth(), mBitmap.getHeight(), viewMatrix);
    
                // 如果宽度小于总宽度,则水平居中
                if (wh[0] < getWidth()) {
                    mUserMatrix.preTranslate(distanceX / scale, 0);
                } else {
                    if (lefttop[0] > 0) {
                        mUserMatrix.preTranslate(-lefttop[0] / scale, 0);
                    } else if (rightbottom[0] < getWidth()) {
                        mUserMatrix.preTranslate((getWidth() - rightbottom[0]) / scale, 0);
                    }
    
                }
                // 如果高度小于总高度,则垂直居中
                if (wh[1] < getHeight()) {
                    mUserMatrix.preTranslate(0, -distanceY / scale);
                } else {
                    if (lefttop[1] > 0) {
                        mUserMatrix.preTranslate(0, -lefttop[1] / scale);
                    } else if (rightbottom[1] < getHeight()) {
                        mUserMatrix.preTranslate(0, (getHeight() - rightbottom[1]) / scale);
                    }
                }
            }
            invalidate();
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            mGestureDetector.onTouchEvent(event);
            mScaleGestureDetector.onTouchEvent(event);
            if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                fixTranslate();
            }
            return true;
        }
    
    
        //--- Tools ------------------------------------------------------------------------------------
    
        //--- 将坐标转换为画布坐标 ---
        private float[] mapPoint(float x, float y, Matrix matrix) {
            float[] temp = new float[2];
            temp[0] = x;
            temp[1] = y;
            matrix.mapPoints(temp);
            return temp;
        }
    
        private float[] mapVectors(float x, float y, Matrix matrix) {
            float[] temp = new float[2];
            temp[0] = x;
            temp[1] = y;
            matrix.mapVectors(temp);
            return temp;
        }
    
    
        //--- 获取 Matrix 中的属性 ---
        private float[] matrixValues = new float[9];
        private static final int MSCALE_X = 0, MSKEW_X = 1, MTRANS_X = 2;
        private static final int MSKEW_Y = 3, MSCALE_Y = 4, MTRANS_Y = 5;
        private static final int MPERSP_0 = 6, MPERSP_1 = 7, MPERSP_2 = 8;
    
        @IntDef({MSCALE_X, MSKEW_X, MTRANS_X, MSKEW_Y, MSCALE_Y, MTRANS_Y, MPERSP_0, MPERSP_1, MPERSP_2})
        @Retention(RetentionPolicy.SOURCE)
        private @interface MatrixName {}
    
        private float getMatrixValue(@MatrixName int name, Matrix matrix) {
            matrix.getValues(matrixValues);
            return matrixValues[name];
        }
    
        //--- 限制缩放比例 ---
        private static final float MAX_SCALE = 4.0f;    //最大缩放比例
        private static final float MIN_SCALE = 0.5f;    // 最小缩放比例
    
        private float getRealScaleFactor(float currentScaleFactor) {
            float realScale = 1.0f;
            float userScale = getMatrixValue(MSCALE_X, mUserMatrix);    // 用户当前的缩放比例
            float theoryScale = userScale * currentScaleFactor;           // 理论缩放数值
    
            // 如果用户在执行放大操作并且理论缩放数据大于4.0
            if (currentScaleFactor > 1.0f && theoryScale > MAX_SCALE) {
                realScale = MAX_SCALE / userScale;
            } else if (currentScaleFactor < 1.0f && theoryScale < MIN_SCALE) {
                realScale = MIN_SCALE / userScale;
            } else {
                realScale = currentScaleFactor;
            }
            return realScale;
        }
    }
    
    

    相关文章

      网友评论

        本文标题:Android 基础之动画和手势

        本文链接:https://www.haomeiwen.com/subject/enbheqtx.html