|
|
@@ -1,1033 +0,0 @@
|
|
|
-package com.usai.redant.apexdrivers.update.xuanimageview;
|
|
|
-
|
|
|
-import android.content.Context;
|
|
|
-import android.content.res.TypedArray;
|
|
|
-import android.graphics.Matrix;
|
|
|
-import android.graphics.RectF;
|
|
|
-import android.graphics.drawable.Drawable;
|
|
|
-import android.util.AttributeSet;
|
|
|
-import android.util.Log;
|
|
|
-import android.view.GestureDetector;
|
|
|
-import android.view.MotionEvent;
|
|
|
-import android.view.ScaleGestureDetector;
|
|
|
-
|
|
|
-import com.usai.redant.apexdrivers.R;
|
|
|
-
|
|
|
-/**
|
|
|
- * Created by xuanyihuang on 8/30/16.
|
|
|
- */
|
|
|
-
|
|
|
-public class XuanImageView extends android.support.v7.widget.AppCompatImageView{
|
|
|
- private int XuanImageViewWidth;
|
|
|
- private int XuanImageViewHeight;
|
|
|
- private int XuanImageViewCenterX;
|
|
|
- private int XuanImageViewCenterY;
|
|
|
- private int ImageCenterX;
|
|
|
- private int ImageCenterY;
|
|
|
- private int mOrientation; //1 for portrait, 2 for landscape
|
|
|
- private int mAutoRotateCategory;
|
|
|
- private float mInitScale; //for landscape
|
|
|
- private float mInitPortraitScale;
|
|
|
- private float mTempInitPortraitScale;
|
|
|
- private float mMaxScale;
|
|
|
- private float mPortraitMaxScale;
|
|
|
- private float mTempPortraitMaxScale;
|
|
|
- private boolean mRotationToggle;
|
|
|
- private float mMaxScaleMultiple;
|
|
|
- private float mDoubleTabScaleMultiple;
|
|
|
- private float mDoubleTabScale;
|
|
|
- private float mPortraitDoubleTabScale;
|
|
|
- private float mTempPortraitDoubleTabScale;
|
|
|
- private Matrix mScaleMatrix;
|
|
|
- private ScaleGestureDetector mScaleGestureDetector;
|
|
|
- private GestureDetector mGestureDetector;
|
|
|
- private float mLastScaleFocusX;
|
|
|
- private float mLastScaleFocusY;
|
|
|
- private boolean isAutoScale;
|
|
|
- private float mSpringBackGradientScaleUpLevel;
|
|
|
- private float mSpringBackGradientScaleDownLevel;
|
|
|
- private float mDoubleTapGradientScaleUpLevel;
|
|
|
- private float mDoubleTapGradientScaleDownLevel;
|
|
|
- private int mLastPointerCount;
|
|
|
- private float mLastX;
|
|
|
- private float mLastY;
|
|
|
- private float mAngle;
|
|
|
- private float mPreviousAngle;
|
|
|
- private RotationGestureDetector mRotateGestureDetector;
|
|
|
- private boolean isAutoRotated;
|
|
|
- private double allowableFloatError;
|
|
|
- private double allowablePortraitFloatError;
|
|
|
- private float currentScaleLevel;
|
|
|
- private float currentAbsScaleLevel;
|
|
|
- private float autoRotationTrigger;
|
|
|
- private int autoRotationRunnableDelay;
|
|
|
- private int autoRotationRunnableTimes;
|
|
|
- private int doubleTabScaleRunnableDelay;
|
|
|
- private int springBackRunnableDelay;
|
|
|
- private boolean hasDrawable;
|
|
|
- private boolean knowViewSize;
|
|
|
-
|
|
|
- public XuanImageView(Context context) {
|
|
|
- this(context, null);
|
|
|
- }
|
|
|
-
|
|
|
- public XuanImageView(Context context, AttributeSet attrs) {
|
|
|
- this(context, attrs, 0);
|
|
|
- }
|
|
|
-
|
|
|
- public XuanImageView(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
|
- super(context, attrs, defStyleAttr);
|
|
|
- initialize(context, attrs);
|
|
|
- }
|
|
|
-
|
|
|
- private void initialize(Context context, AttributeSet attrs) {
|
|
|
- setScaleType(ScaleType.MATRIX);
|
|
|
- mScaleMatrix = new Matrix();
|
|
|
-
|
|
|
- mScaleGestureDetector = new ScaleGestureDetector(context, constructOnScaleGestureListener());
|
|
|
- mGestureDetector = new GestureDetector(context, constructOnGestureListener());
|
|
|
-
|
|
|
- initCustomAttrs(context, attrs);
|
|
|
-
|
|
|
- mOrientation = XuanImageViewSettings.ORIENTATION_LANDSCAPE; //1 for portrait, 2 for landscape
|
|
|
- isAutoScale = false;
|
|
|
- mLastPointerCount = 0;
|
|
|
- mAngle = 0;
|
|
|
- mPreviousAngle = 0;
|
|
|
- currentScaleLevel = 1;
|
|
|
- }
|
|
|
-
|
|
|
- private ScaleGestureDetector.OnScaleGestureListener constructOnScaleGestureListener() {
|
|
|
- return new ScaleGestureDetector.OnScaleGestureListener() {
|
|
|
- @Override
|
|
|
- public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
|
|
|
- float scaleFactor = scaleGestureDetector.getScaleFactor();
|
|
|
-
|
|
|
- currentScaleLevel = getCurrentScaleLevel();
|
|
|
- currentAbsScaleLevel = Math.abs(currentScaleLevel);
|
|
|
- Log.d("CurrentAbsScaleLevel", "" + currentAbsScaleLevel);
|
|
|
-
|
|
|
- boolean isRotating = false;
|
|
|
- boolean justScale = false;
|
|
|
-
|
|
|
- if(mRotateGestureDetector.IsRotated()){
|
|
|
- // is rotating
|
|
|
- isRotating = true;
|
|
|
- }
|
|
|
- else{
|
|
|
- // not rotating, just scaling
|
|
|
- if(mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION){
|
|
|
- if((currentScaleLevel <= mMaxScale && scaleFactor > 1.0f) || (currentScaleLevel >= mInitScale && scaleFactor < 1.0f))
|
|
|
- justScale = true;
|
|
|
-
|
|
|
- }
|
|
|
- else if(mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM){
|
|
|
- if (mOrientation == XuanImageViewSettings.ORIENTATION_LANDSCAPE) {
|
|
|
- if ((currentAbsScaleLevel <= mMaxScale && scaleFactor > 1.0f) || (currentAbsScaleLevel >= mInitScale && scaleFactor < 1.0f))
|
|
|
- justScale = true;
|
|
|
- } else if (mOrientation == XuanImageViewSettings.ORIENTATION_PORTRAIT) {
|
|
|
- if ((currentAbsScaleLevel <= mTempPortraitMaxScale && scaleFactor > 1.0f) || (currentAbsScaleLevel >= mTempInitPortraitScale && scaleFactor < 1.0f))
|
|
|
- justScale = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- if(isRotating) {
|
|
|
- mScaleMatrix.postScale(scaleFactor, scaleFactor, mRotateGestureDetector.getPivotX(), mRotateGestureDetector.getPivotY());
|
|
|
- }
|
|
|
- else if(justScale)
|
|
|
- {
|
|
|
- mScaleMatrix.postScale(scaleFactor, scaleFactor, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY());
|
|
|
- checkBorderAndCenterWhenScale();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- setImageMatrix(mScaleMatrix);
|
|
|
- mLastScaleFocusX = scaleGestureDetector.getFocusX();
|
|
|
- mLastScaleFocusY = scaleGestureDetector.getFocusY();
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
|
|
|
- Log.d("onScaleBegin-->", "");
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
|
|
|
- Log.d("onScaleEnd-->", "");
|
|
|
- }
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- private GestureDetector.OnGestureListener constructOnGestureListener() {
|
|
|
- return new GestureDetector.SimpleOnGestureListener() {
|
|
|
- @Override
|
|
|
- public boolean onDoubleTap(MotionEvent e) {
|
|
|
- currentScaleLevel = getCurrentScaleLevel();
|
|
|
- currentAbsScaleLevel = Math.abs(currentScaleLevel);
|
|
|
- float x = e.getX();
|
|
|
- float y = e.getY();
|
|
|
-
|
|
|
- if (isAutoScale)
|
|
|
- return true;
|
|
|
-
|
|
|
-
|
|
|
- if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION) {
|
|
|
- if (currentScaleLevel < mDoubleTabScale) {
|
|
|
- postDelayed(new AutoScaleRunnable(mDoubleTabScale, x, y, mDoubleTapGradientScaleUpLevel, mDoubleTapGradientScaleDownLevel), doubleTabScaleRunnableDelay);
|
|
|
- isAutoScale = true;
|
|
|
- } else {
|
|
|
- postDelayed(new AutoScaleRunnable(mInitScale, x, y, mDoubleTapGradientScaleUpLevel, mDoubleTapGradientScaleDownLevel), doubleTabScaleRunnableDelay);
|
|
|
- isAutoScale = true;
|
|
|
- }
|
|
|
- } else if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM) {
|
|
|
- if (mOrientation == XuanImageViewSettings.ORIENTATION_LANDSCAPE) {
|
|
|
- if (currentAbsScaleLevel < mDoubleTabScale) {
|
|
|
- postDelayed(new AutoScaleRunnable(mDoubleTabScale * (currentScaleLevel / currentAbsScaleLevel), x, y, mDoubleTapGradientScaleUpLevel, mDoubleTapGradientScaleDownLevel), doubleTabScaleRunnableDelay);
|
|
|
- isAutoScale = true;
|
|
|
- } else {
|
|
|
- postDelayed(new AutoScaleRunnable(mInitScale * (currentScaleLevel / currentAbsScaleLevel), x, y, mDoubleTapGradientScaleUpLevel, mDoubleTapGradientScaleDownLevel), doubleTabScaleRunnableDelay);
|
|
|
- isAutoScale = true;
|
|
|
- }
|
|
|
- } else if (mOrientation == XuanImageViewSettings.ORIENTATION_PORTRAIT) {
|
|
|
- if (currentAbsScaleLevel < mTempPortraitDoubleTabScale) {
|
|
|
- postDelayed(new AutoScaleRunnable(mTempPortraitDoubleTabScale * (currentScaleLevel / currentAbsScaleLevel), x, y, mDoubleTapGradientScaleUpLevel, mDoubleTapGradientScaleDownLevel), doubleTabScaleRunnableDelay);
|
|
|
- isAutoScale = true;
|
|
|
- } else {
|
|
|
- postDelayed(new AutoScaleRunnable(mTempInitPortraitScale * (currentScaleLevel / currentAbsScaleLevel), x, y, mDoubleTapGradientScaleUpLevel, mDoubleTapGradientScaleDownLevel), doubleTabScaleRunnableDelay);
|
|
|
- isAutoScale = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private RotationGestureDetector.OnRotationGestureListener constructOnRotationGestureListener(){
|
|
|
- return new RotationGestureDetector.OnRotationGestureListener() {
|
|
|
- @Override
|
|
|
- public boolean OnRotate(RotationGestureDetector rotationGestureDetector) {
|
|
|
- mAngle = rotationGestureDetector.getAngle();
|
|
|
- mPreviousAngle = rotationGestureDetector.getPreviousAngle();
|
|
|
- mScaleMatrix.postRotate(mAngle - mPreviousAngle, rotationGestureDetector.getPivotX(), rotationGestureDetector.getPivotY());
|
|
|
-
|
|
|
- setImageMatrix(mScaleMatrix);
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean StopRotate(RotationGestureDetector rotationGestureDetector) {
|
|
|
- if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION)
|
|
|
- AutoRotateRestoration(rotationGestureDetector);
|
|
|
- else if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM)
|
|
|
- AutoRotateMagnetism(rotationGestureDetector);
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- private void initCustomAttrs(Context context, AttributeSet attrs){
|
|
|
-
|
|
|
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.xuanimageview);
|
|
|
- mRotationToggle = a.getBoolean(R.styleable.xuanimageview_RotationToggle, true);
|
|
|
- mAutoRotateCategory = a.getInteger(R.styleable.xuanimageview_AutoRotateCategory, XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION);
|
|
|
- mMaxScaleMultiple = a.getFloat(R.styleable.xuanimageview_MaxScaleMultiple, 4);
|
|
|
- mDoubleTabScaleMultiple = a.getFloat(R.styleable.xuanimageview_DoubleTabScaleMultiple, 2);
|
|
|
- mSpringBackGradientScaleUpLevel = a.getFloat(R.styleable.xuanimageview_SpringBackGradientScaleUpLevel, 1.01f);
|
|
|
- mSpringBackGradientScaleDownLevel = a.getFloat(R.styleable.xuanimageview_SpringBackGradientScaleDownLevel, 0.99f);
|
|
|
- mDoubleTapGradientScaleUpLevel = a.getFloat(R.styleable.xuanimageview_DoubleTapGradientScaleUpLevel, 1.05f);
|
|
|
- mDoubleTapGradientScaleDownLevel = a.getFloat(R.styleable.xuanimageview_DoubleTapGradientScaleDownLevel, 0.95f);
|
|
|
- autoRotationTrigger = a.getFloat(R.styleable.xuanimageview_AutoRotationTrigger, 60);
|
|
|
- springBackRunnableDelay = a.getInteger(R.styleable.xuanimageview_SpringBackRunnableDelay, 10);
|
|
|
- doubleTabScaleRunnableDelay = a.getInteger(R.styleable.xuanimageview_DoubleTapScaleRunnableDelay, 10);
|
|
|
- autoRotationRunnableDelay = a.getInteger(R.styleable.xuanimageview_AutoRotationRunnableDelay, 5);
|
|
|
- autoRotationRunnableTimes = a.getInteger(R.styleable.xuanimageview_AutoRotationRunnableTimes, 10);
|
|
|
- try {
|
|
|
- allowableFloatError = Double.parseDouble(a.getString(R.styleable.xuanimageview_AllowableFloatError));
|
|
|
- }catch (Exception e){
|
|
|
- allowableFloatError = 1E-6; // for normal display aspect ratio
|
|
|
-// allowableFloatError = 3E-3; // for Galaxy S8
|
|
|
- }
|
|
|
- try {
|
|
|
- allowablePortraitFloatError = Double.parseDouble(a.getString(R.styleable.xuanimageview_AllowablePortraitFloatError));
|
|
|
- }catch (Exception e){
|
|
|
- allowablePortraitFloatError = 1E-12; // for normal display aspect ratio
|
|
|
-// allowablePortraitFloatError = 5E-8; //for Galaxy S8
|
|
|
- }
|
|
|
- a.recycle();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void setImageDrawable(Drawable drawable) {
|
|
|
- super.setImageDrawable(drawable);
|
|
|
-
|
|
|
- if (drawable == null) {
|
|
|
- hasDrawable = false;
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (!drawableHasSize(drawable))
|
|
|
- return;
|
|
|
-
|
|
|
- hasDrawable = true;
|
|
|
- initDrawableMatrix();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void setImageResource(int resId) {
|
|
|
- Drawable drawable = null;
|
|
|
- try {
|
|
|
- drawable = getResources().getDrawable(resId);
|
|
|
- } catch (Exception e) {
|
|
|
- e.printStackTrace();
|
|
|
- }
|
|
|
- setImageDrawable(drawable);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
|
|
- super.onSizeChanged(w, h, oldw, oldh);
|
|
|
-
|
|
|
- knowViewSize = true;
|
|
|
- initDrawableMatrix();
|
|
|
- }
|
|
|
-
|
|
|
- private boolean drawableHasSize(Drawable drawable) {
|
|
|
- if ((drawable.getIntrinsicHeight() <= 0 || drawable.getIntrinsicWidth() <= 0)
|
|
|
- && (drawable.getMinimumHeight() <= 0 || drawable.getMinimumWidth() <= 0)
|
|
|
- && (drawable.getBounds().height() <= 0 || drawable.getBounds().width() <= 0))
|
|
|
- return false;
|
|
|
- else
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- private void initDrawableMatrix() {
|
|
|
- if (!hasDrawable)
|
|
|
- return;
|
|
|
- if (!knowViewSize)
|
|
|
- return;
|
|
|
-
|
|
|
- if (mScaleMatrix == null)
|
|
|
- mScaleMatrix = new Matrix();
|
|
|
- else
|
|
|
- mScaleMatrix.reset();
|
|
|
-
|
|
|
- // get width and height of XuanImageView
|
|
|
- XuanImageViewWidth = getWidth();
|
|
|
- XuanImageViewHeight = getHeight();
|
|
|
-
|
|
|
- //get the center point of XuanImageView
|
|
|
- XuanImageViewCenterX = XuanImageViewWidth / 2;
|
|
|
- XuanImageViewCenterY = XuanImageViewHeight / 2;
|
|
|
-
|
|
|
- // instantiate mRotationGestureDetector after dimension of XuanImageView is got.
|
|
|
- mRotateGestureDetector = new RotationGestureDetector(constructOnRotationGestureListener(), XuanImageViewWidth);
|
|
|
-
|
|
|
- //get width and height of the image
|
|
|
- Drawable imageDrawable = getDrawable();
|
|
|
- if (imageDrawable == null)
|
|
|
- return;
|
|
|
- int imageWidth = imageDrawable.getIntrinsicWidth();
|
|
|
- int imageHeight = imageDrawable.getIntrinsicHeight();
|
|
|
-
|
|
|
- //image is scaled to fit the size of XuanImageView at the very beginning.
|
|
|
- float scale = Math.min(XuanImageViewWidth * 1.0f / imageWidth, XuanImageViewHeight * 1.0f / imageHeight);
|
|
|
- float portraitscale = Math.min(XuanImageViewWidth * 1.0f / imageHeight, XuanImageViewHeight * 1.0f / imageWidth);
|
|
|
-
|
|
|
- mInitScale = scale;
|
|
|
- mInitPortraitScale = portraitscale;
|
|
|
- mMaxScale = mMaxScaleMultiple * scale;
|
|
|
- mPortraitMaxScale = mMaxScaleMultiple * portraitscale;
|
|
|
- mDoubleTabScale = mDoubleTabScaleMultiple * scale;
|
|
|
- mPortraitDoubleTabScale = mDoubleTabScaleMultiple * portraitscale;
|
|
|
-
|
|
|
- //center of image overlaps with that of XuanImageView
|
|
|
- int deltaX = XuanImageViewWidth / 2 - imageWidth / 2;
|
|
|
- int deltaY = XuanImageViewHeight / 2 - imageHeight / 2;
|
|
|
-
|
|
|
- mScaleMatrix.postTranslate(deltaX, deltaY);
|
|
|
- mScaleMatrix.postScale(scale, scale, XuanImageViewWidth / 2, XuanImageViewHeight / 2);
|
|
|
- setImageMatrix(mScaleMatrix);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean onTouchEvent(MotionEvent motionEvent) {
|
|
|
- if (mRotateGestureDetector == null)
|
|
|
- return true;
|
|
|
-
|
|
|
- boolean parentDisallowInterceptTouchEventFlag = true;
|
|
|
-
|
|
|
- // for DoubleTap gesture
|
|
|
- mGestureDetector.onTouchEvent(motionEvent);
|
|
|
-
|
|
|
- // for Scale gesture
|
|
|
- mScaleGestureDetector.onTouchEvent(motionEvent);
|
|
|
-
|
|
|
-
|
|
|
- currentScaleLevel = getCurrentScaleLevel();
|
|
|
- currentAbsScaleLevel = Math.abs(currentScaleLevel);
|
|
|
- if (mRotationToggle) {
|
|
|
- if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION) {
|
|
|
- if (mRotateGestureDetector.IsRotated() || Math.abs(currentScaleLevel - mInitScale) < allowableFloatError
|
|
|
- || ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP)
|
|
|
- || ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP)) {
|
|
|
- if (!mRotateGestureDetector.IsRotated())
|
|
|
- parentDisallowInterceptTouchEventFlag = false;
|
|
|
-
|
|
|
- // for Rotation gesture
|
|
|
- mRotateGestureDetector.onTouchEvent(motionEvent);
|
|
|
- }
|
|
|
- } else if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM) {
|
|
|
- if (mOrientation == XuanImageViewSettings.ORIENTATION_LANDSCAPE) {
|
|
|
- if (mRotateGestureDetector.IsRotated() || Math.abs(currentAbsScaleLevel - mInitScale) < allowableFloatError
|
|
|
- || ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP)
|
|
|
- || ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP)) {
|
|
|
- if (!mRotateGestureDetector.IsRotated())
|
|
|
- parentDisallowInterceptTouchEventFlag = false;
|
|
|
-
|
|
|
- // for Rotation gesture
|
|
|
- mRotateGestureDetector.onTouchEvent(motionEvent);
|
|
|
-
|
|
|
- }
|
|
|
- } else if (mOrientation == XuanImageViewSettings.ORIENTATION_PORTRAIT) {
|
|
|
- if (mRotateGestureDetector.IsRotated() || Math.abs(currentAbsScaleLevel - mTempInitPortraitScale) < allowablePortraitFloatError
|
|
|
- || ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP)
|
|
|
- || ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP)) {
|
|
|
- if (!mRotateGestureDetector.IsRotated())
|
|
|
- parentDisallowInterceptTouchEventFlag = false;
|
|
|
-
|
|
|
- // for Rotation gesture
|
|
|
- mRotateGestureDetector.onTouchEvent(motionEvent);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //pointerCount won't be 0
|
|
|
- int pointerCount = motionEvent.getPointerCount();
|
|
|
- float pivotX = 0;
|
|
|
- float pivotY = 0;
|
|
|
- for (int i = 0; i < pointerCount; i++) {
|
|
|
- pivotX += motionEvent.getX(i);
|
|
|
- pivotY += motionEvent.getY(i);
|
|
|
- }
|
|
|
- pivotX /= pointerCount; //get integer result of the division
|
|
|
- pivotY /= pointerCount;
|
|
|
-
|
|
|
- // when image is being dragged, generally, pointCount == mLastCount holds, so old state is not saved.
|
|
|
- if (pointerCount != mLastPointerCount) {
|
|
|
- mLastX = pivotX;
|
|
|
- mLastY = pivotY;
|
|
|
- mLastPointerCount = pointerCount;
|
|
|
- }
|
|
|
-
|
|
|
- RectF rectF = getMatrixRectF();
|
|
|
-
|
|
|
- switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
|
|
|
- case MotionEvent.ACTION_DOWN:
|
|
|
- if (getParent() != null)
|
|
|
- getParent().requestDisallowInterceptTouchEvent(parentDisallowInterceptTouchEventFlag);
|
|
|
- break;
|
|
|
- case MotionEvent.ACTION_MOVE:
|
|
|
- if (getParent() != null)
|
|
|
- getParent().requestDisallowInterceptTouchEvent(parentDisallowInterceptTouchEventFlag);
|
|
|
- float deltaX = pivotX - mLastX;
|
|
|
- float deltaY = pivotY - mLastY;
|
|
|
-
|
|
|
- if (getDrawable() != null) {
|
|
|
- if (!mRotateGestureDetector.IsRotated()) {
|
|
|
- if (rectF.width() <= XuanImageViewWidth)
|
|
|
- deltaX = 0;
|
|
|
- if (rectF.height() <= XuanImageViewHeight)
|
|
|
- deltaY = 0;
|
|
|
- }
|
|
|
- mScaleMatrix.postTranslate(deltaX, deltaY);
|
|
|
-
|
|
|
- if (!mRotateGestureDetector.IsRotated())
|
|
|
- checkBorderAndCenterWhenTranslate();
|
|
|
- setImageMatrix(mScaleMatrix);
|
|
|
- }
|
|
|
- mLastX = pivotX;
|
|
|
- mLastY = pivotY;
|
|
|
- break;
|
|
|
- case MotionEvent.ACTION_POINTER_UP:
|
|
|
- //one finger loosening (multi-touch)
|
|
|
- if (pointerCount - 1 < 2) {
|
|
|
- if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION) {
|
|
|
- //spring back to mInitScale or mMaxScale
|
|
|
- if ((currentScaleLevel < mInitScale) && !isAutoRotated) {
|
|
|
- postDelayed(new AutoScaleRunnable(mInitScale, mLastScaleFocusX, mLastScaleFocusY, mSpringBackGradientScaleUpLevel, mSpringBackGradientScaleDownLevel), springBackRunnableDelay);
|
|
|
- isAutoScale = true;
|
|
|
- } else if ((currentScaleLevel > mMaxScale) && !isAutoRotated) {
|
|
|
- postDelayed(new AutoScaleRunnable(mMaxScale, mLastScaleFocusX, mLastScaleFocusY, mSpringBackGradientScaleUpLevel, mSpringBackGradientScaleDownLevel), springBackRunnableDelay);
|
|
|
- isAutoScale = true;
|
|
|
- }
|
|
|
- } else if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM) {
|
|
|
- if (mOrientation == XuanImageViewSettings.ORIENTATION_LANDSCAPE) {
|
|
|
- if (currentAbsScaleLevel < mInitScale && !isAutoRotated) {
|
|
|
- postDelayed(new AutoScaleRunnable(mInitScale * (currentScaleLevel / currentAbsScaleLevel), mLastScaleFocusX, mLastScaleFocusY, mSpringBackGradientScaleUpLevel, mSpringBackGradientScaleDownLevel), springBackRunnableDelay);
|
|
|
- isAutoScale = true;
|
|
|
- } else if ((currentAbsScaleLevel > mMaxScale) && !isAutoRotated) {
|
|
|
- postDelayed(new AutoScaleRunnable(mMaxScale * (currentScaleLevel / currentAbsScaleLevel), mLastScaleFocusX, mLastScaleFocusY, mSpringBackGradientScaleUpLevel, mSpringBackGradientScaleDownLevel), springBackRunnableDelay);
|
|
|
- isAutoScale = true;
|
|
|
- }
|
|
|
- } else if (mOrientation == XuanImageViewSettings.ORIENTATION_PORTRAIT) {
|
|
|
- if (currentAbsScaleLevel < mTempInitPortraitScale && !isAutoRotated) {
|
|
|
- postDelayed(new AutoScaleRunnable(mTempInitPortraitScale * (currentScaleLevel / currentAbsScaleLevel), mLastScaleFocusX, mLastScaleFocusY, mSpringBackGradientScaleUpLevel, mSpringBackGradientScaleDownLevel), springBackRunnableDelay);
|
|
|
- isAutoScale = true;
|
|
|
- } else if ((currentAbsScaleLevel > mTempPortraitMaxScale) && !isAutoRotated) {
|
|
|
- postDelayed(new AutoScaleRunnable(mTempPortraitMaxScale * (currentScaleLevel / currentAbsScaleLevel), mLastScaleFocusX, mLastScaleFocusY, mSpringBackGradientScaleUpLevel, mSpringBackGradientScaleDownLevel), springBackRunnableDelay);
|
|
|
- isAutoScale = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- break;
|
|
|
- case MotionEvent.ACTION_UP:
|
|
|
- mLastPointerCount = 0;
|
|
|
- break;
|
|
|
- case MotionEvent.ACTION_CANCEL:
|
|
|
- mLastPointerCount = 0;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- private void checkBorderAndCenterWhenScale() {
|
|
|
-
|
|
|
- RectF rectF = getMatrixRectF();
|
|
|
- float deltaX = 0;
|
|
|
- float deltaY = 0;
|
|
|
-
|
|
|
- /**
|
|
|
- * If width or height of image is bigger than that of XuanImageView,
|
|
|
- * should prevent image's edge being far away from XuanImageView's edge.
|
|
|
- */
|
|
|
- if (rectF.width() >= XuanImageViewWidth) {
|
|
|
- if (rectF.left > 0)
|
|
|
- deltaX = -rectF.left;
|
|
|
- if (rectF.right < XuanImageViewWidth)
|
|
|
- deltaX = XuanImageViewWidth - rectF.right;
|
|
|
-
|
|
|
- }
|
|
|
- if (rectF.height() >= XuanImageViewHeight) {
|
|
|
- if (rectF.top > 0)
|
|
|
- deltaY = -rectF.top;
|
|
|
- if (rectF.bottom < XuanImageViewHeight)
|
|
|
- deltaY = XuanImageViewHeight - rectF.bottom;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * If width or height of image is smaller than that of XuanImageView,
|
|
|
- * make sure the image, in width dimension or height dimension, is centered.
|
|
|
- */
|
|
|
- if (rectF.width() < XuanImageViewWidth) {
|
|
|
- deltaX = XuanImageViewWidth / 2.0f - rectF.left - rectF.width() / 2.0f;
|
|
|
- }
|
|
|
- if (rectF.height() < XuanImageViewHeight) {
|
|
|
- deltaY = XuanImageViewHeight / 2.0f - rectF.top - rectF.height() / 2.0f;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- mScaleMatrix.postTranslate(deltaX, deltaY);
|
|
|
- }
|
|
|
-
|
|
|
- private void checkBorderAndCenterWhenTranslate() {
|
|
|
- /**
|
|
|
- * No need to check Center here because it has been handled before checkBorderAndCenterWhenTranslate() is invoked.
|
|
|
- * See "case MotionEvent.ACTION_MOVE: " in onTouch(View view, MotionEvent motionEvent).
|
|
|
- */
|
|
|
- RectF rectF = getMatrixRectF();
|
|
|
-
|
|
|
- float deltaX = 0;
|
|
|
- float deltaY = 0;
|
|
|
-
|
|
|
- /**
|
|
|
- * If width or height of image is bigger than that of XuanImageView,
|
|
|
- * should prevent image's edge being far away from XuanImageView's edge.
|
|
|
- */
|
|
|
- if (rectF.width() >= XuanImageViewWidth) {
|
|
|
- if (rectF.left > 0)
|
|
|
- deltaX = -rectF.left;
|
|
|
- if (rectF.right < XuanImageViewWidth)
|
|
|
- deltaX = XuanImageViewWidth - rectF.right;
|
|
|
-
|
|
|
- }
|
|
|
- if (rectF.height() >= XuanImageViewHeight) {
|
|
|
- if (rectF.top > 0)
|
|
|
- deltaY = -rectF.top;
|
|
|
- if (rectF.bottom < XuanImageViewHeight)
|
|
|
- deltaY = XuanImageViewHeight - rectF.bottom;
|
|
|
- }
|
|
|
-
|
|
|
- mScaleMatrix.postTranslate(deltaX, deltaY);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private float getCurrentScaleLevel() {
|
|
|
- float matrixArray[] = new float[9];
|
|
|
- mScaleMatrix.getValues(matrixArray);
|
|
|
-
|
|
|
- return matrixArray[Matrix.MSCALE_X];
|
|
|
- }
|
|
|
-
|
|
|
- private RectF getMatrixRectF() {
|
|
|
- Matrix matrix = mScaleMatrix;
|
|
|
- RectF rectF = new RectF();
|
|
|
- Drawable image = getDrawable();
|
|
|
- if (image != null) {
|
|
|
- rectF.set(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight());
|
|
|
- matrix.mapRect(rectF);
|
|
|
- }
|
|
|
-
|
|
|
- return rectF;
|
|
|
- }
|
|
|
-
|
|
|
- private boolean calculateImageCenterCoordinates() {
|
|
|
- RectF rectF = getMatrixRectF();
|
|
|
- ImageCenterX = (int) ((rectF.left + rectF.right) / 2);
|
|
|
- ImageCenterY = (int) ((rectF.top + rectF.bottom) / 2);
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- public void AutoRotateMagnetism(RotationGestureDetector rotationGestureDetector) {
|
|
|
- mAngle = rotationGestureDetector.getAngle();//mAngle's range: [-180 degress, 180 degress]
|
|
|
- float autoRotateAngle;
|
|
|
- int quotient;
|
|
|
- float remainder;
|
|
|
-
|
|
|
- quotient = ((int) mAngle) / 90;
|
|
|
- remainder = mAngle % 90;
|
|
|
-
|
|
|
- if (remainder >= autoRotationTrigger) {
|
|
|
- autoRotateAngle = 90 - remainder;
|
|
|
- if ((quotient + 1) % 2 == 0)
|
|
|
- mOrientation = XuanImageViewSettings.ORIENTATION_LANDSCAPE;
|
|
|
- else
|
|
|
- mOrientation = XuanImageViewSettings.ORIENTATION_PORTRAIT;
|
|
|
- } else if (remainder <= -autoRotationTrigger) {
|
|
|
- autoRotateAngle = -90 - remainder;
|
|
|
- if ((quotient - 1) % 2 == 0)
|
|
|
- mOrientation = XuanImageViewSettings.ORIENTATION_LANDSCAPE;
|
|
|
- else
|
|
|
- mOrientation = XuanImageViewSettings.ORIENTATION_PORTRAIT;
|
|
|
- } else {
|
|
|
- autoRotateAngle = -remainder;
|
|
|
- if (quotient % 2 == 0)
|
|
|
- mOrientation = XuanImageViewSettings.ORIENTATION_LANDSCAPE;
|
|
|
- else
|
|
|
- mOrientation = XuanImageViewSettings.ORIENTATION_PORTRAIT;
|
|
|
- }
|
|
|
-
|
|
|
- postDelayed(new AutoRotateRunnable(autoRotateAngle, getCurrentScaleLevel() / (float) Math.cos(Math.toRadians(mAngle)), autoRotationRunnableTimes), autoRotationRunnableDelay);
|
|
|
- isAutoRotated = true;
|
|
|
-
|
|
|
- rotationGestureDetector.setAngle(mAngle + autoRotateAngle);
|
|
|
- rotationGestureDetector.setPreviousAngle(mAngle + autoRotateAngle);
|
|
|
- }
|
|
|
-
|
|
|
- public void AutoRotateRestoration(RotationGestureDetector rotationGestureDetector) {
|
|
|
- mAngle = rotationGestureDetector.getAngle();//mAngle's range: [-180 degress, 180 degress]
|
|
|
- float autoRotateAngle;
|
|
|
-
|
|
|
- if (mAngle >= autoRotationTrigger)
|
|
|
- autoRotateAngle = 360 - mAngle;
|
|
|
- else if (mAngle <= -autoRotationTrigger)
|
|
|
- autoRotateAngle = -360 - mAngle;
|
|
|
- else
|
|
|
- autoRotateAngle = -mAngle;
|
|
|
-
|
|
|
- postDelayed(new AutoRotateRunnable(autoRotateAngle, getCurrentScaleLevel() / (float) Math.cos(Math.toRadians(mAngle)), autoRotationRunnableTimes), autoRotationRunnableDelay);
|
|
|
- isAutoRotated = true;
|
|
|
-
|
|
|
- rotationGestureDetector.setAngle(0.0f);
|
|
|
- rotationGestureDetector.setPreviousAngle(0.0f);
|
|
|
- }
|
|
|
-
|
|
|
- private class AutoScaleRunnable implements Runnable {
|
|
|
- float targetScale;
|
|
|
- float targetAbsScale;
|
|
|
- float FocusX;
|
|
|
- float FocusY;
|
|
|
- float scaleFactor;
|
|
|
-
|
|
|
- AutoScaleRunnable(float targetScale, float FocusX, float FocusY, float GradientScaleUp, float GradientScaleDown) {
|
|
|
- this.targetScale = targetScale;
|
|
|
- this.targetAbsScale = Math.abs(targetScale);
|
|
|
- this.FocusX = FocusX;
|
|
|
- this.FocusY = FocusY;
|
|
|
- currentScaleLevel = getCurrentScaleLevel();
|
|
|
- currentAbsScaleLevel = Math.abs(currentScaleLevel);
|
|
|
- if (currentAbsScaleLevel < targetAbsScale)
|
|
|
- scaleFactor = GradientScaleUp;
|
|
|
- else if (currentAbsScaleLevel > targetAbsScale)
|
|
|
- scaleFactor = GradientScaleDown;
|
|
|
- else if (currentAbsScaleLevel == targetAbsScale)
|
|
|
- scaleFactor = 1.0f;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void run() {
|
|
|
- mScaleMatrix.postScale(scaleFactor, scaleFactor, FocusX, FocusY);
|
|
|
- checkBorderAndCenterWhenScale();
|
|
|
- setImageMatrix(mScaleMatrix);
|
|
|
-
|
|
|
- currentScaleLevel = getCurrentScaleLevel();
|
|
|
- currentAbsScaleLevel = Math.abs(currentScaleLevel);
|
|
|
- if ((scaleFactor < 1.0f && currentAbsScaleLevel > targetAbsScale)
|
|
|
- || (scaleFactor > 1.0f && currentAbsScaleLevel < targetAbsScale)) {
|
|
|
- postDelayed(this, springBackRunnableDelay);
|
|
|
- } else {
|
|
|
- scaleFactor = targetScale / currentScaleLevel;
|
|
|
-
|
|
|
- mScaleMatrix.postScale(scaleFactor, scaleFactor, FocusX, FocusY);
|
|
|
- checkBorderAndCenterWhenScale();
|
|
|
- setImageMatrix(mScaleMatrix);
|
|
|
-
|
|
|
- isAutoScale = false;
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private class AutoRotateRunnable implements Runnable {
|
|
|
- float targetRotateAngle;
|
|
|
- long TotalRotateTimes;
|
|
|
- float AnglePerTime;
|
|
|
- float AccumulativeRotateTimes;
|
|
|
- float AccumulativeRotateAngles;
|
|
|
- float initScaleLevel;
|
|
|
- double ScalePerTime;
|
|
|
-
|
|
|
- AutoRotateRunnable(float targetRotateAngle, float initScaleLevel, long TotalRotateTimes) {
|
|
|
- this.targetRotateAngle = targetRotateAngle;
|
|
|
- this.TotalRotateTimes = TotalRotateTimes;
|
|
|
- AnglePerTime = targetRotateAngle / this.TotalRotateTimes;
|
|
|
- AccumulativeRotateTimes = 0;
|
|
|
- AccumulativeRotateAngles = 0.0f;
|
|
|
- this.initScaleLevel = initScaleLevel;
|
|
|
- if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION) {
|
|
|
- ScalePerTime = Math.pow(mInitScale / initScaleLevel, 1.0 / TotalRotateTimes);
|
|
|
- } else if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM) {
|
|
|
- if (mOrientation == XuanImageViewSettings.ORIENTATION_LANDSCAPE)
|
|
|
- ScalePerTime = Math.pow(mInitScale / initScaleLevel, 1.0 / TotalRotateTimes);
|
|
|
- else if (mOrientation == XuanImageViewSettings.ORIENTATION_PORTRAIT)
|
|
|
- ScalePerTime = Math.pow(mInitPortraitScale / initScaleLevel, 1.0 / TotalRotateTimes);
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void run() {
|
|
|
- calculateImageCenterCoordinates();
|
|
|
-
|
|
|
- mScaleMatrix.postRotate(AnglePerTime, ImageCenterX, ImageCenterY);
|
|
|
- mScaleMatrix.postScale((float) ScalePerTime, (float) ScalePerTime, ImageCenterX, ImageCenterY);
|
|
|
- mScaleMatrix.postTranslate((XuanImageViewCenterX - ImageCenterX) / (TotalRotateTimes - AccumulativeRotateTimes), (XuanImageViewCenterY - ImageCenterY) / (TotalRotateTimes - AccumulativeRotateTimes));
|
|
|
- setImageMatrix(mScaleMatrix);
|
|
|
- AccumulativeRotateTimes++;
|
|
|
- AccumulativeRotateAngles += AnglePerTime;
|
|
|
-
|
|
|
-
|
|
|
- if (AccumulativeRotateTimes < TotalRotateTimes) {
|
|
|
- postDelayed(this, autoRotationRunnableDelay);
|
|
|
- } else {
|
|
|
- currentScaleLevel = getCurrentScaleLevel();
|
|
|
- currentAbsScaleLevel = Math.abs(currentScaleLevel);
|
|
|
- calculateImageCenterCoordinates();
|
|
|
- mScaleMatrix.postRotate(targetRotateAngle - AccumulativeRotateAngles, ImageCenterX, ImageCenterY);
|
|
|
- if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION) {
|
|
|
- mScaleMatrix.postScale(Math.abs(mInitScale / currentScaleLevel), Math.abs(mInitScale / currentScaleLevel), ImageCenterX, ImageCenterY);
|
|
|
- } else if (mAutoRotateCategory == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM) {
|
|
|
- if (mOrientation == XuanImageViewSettings.ORIENTATION_LANDSCAPE) {
|
|
|
- mScaleMatrix.postScale(Math.abs(mInitScale / currentScaleLevel), Math.abs(mInitScale / currentScaleLevel), ImageCenterX, ImageCenterY);
|
|
|
- } else if (mOrientation == XuanImageViewSettings.ORIENTATION_PORTRAIT) {
|
|
|
-// mScaleMatrix.postScale(Math.abs(mInitPortraitScale / getCurrentScaleLevel()), Math.abs(mInitPortraitScale / getCurrentScaleLevel()), ImageCenterX, ImageCenterY);
|
|
|
- mTempInitPortraitScale = Math.abs(currentScaleLevel);
|
|
|
- mTempPortraitMaxScale = mTempInitPortraitScale * mMaxScaleMultiple;
|
|
|
- mTempPortraitDoubleTabScale = mTempInitPortraitScale * mDoubleTabScaleMultiple;
|
|
|
- }
|
|
|
- }
|
|
|
- mScaleMatrix.postTranslate(XuanImageViewCenterX - ImageCenterX, XuanImageViewCenterY - ImageCenterY);
|
|
|
-
|
|
|
- checkBorderAndCenterWhenScale();
|
|
|
-
|
|
|
- setImageMatrix(mScaleMatrix);
|
|
|
- isAutoRotated = false;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Set a boolean value to determine whether rotation function is turned on.
|
|
|
- *
|
|
|
- * @param toggle determine whether rotation function is turned on
|
|
|
- */
|
|
|
- public void setRotationToggle(boolean toggle) {
|
|
|
- mRotationToggle = toggle;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return current RotationToggle
|
|
|
- */
|
|
|
- public boolean getRotationToggle() {
|
|
|
- return mRotationToggle;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Set AutoRotateCategory, there are two alternative values of it : XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION, XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM.
|
|
|
- *
|
|
|
- * @param category
|
|
|
- */
|
|
|
- public void setAutoRotateCategory(int category) {
|
|
|
- if (category == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION || category == XuanImageViewSettings.AUTO_ROTATE_CATEGORY_MAGNETISM)
|
|
|
- mAutoRotateCategory = category;
|
|
|
- else
|
|
|
- mAutoRotateCategory = XuanImageViewSettings.AUTO_ROTATE_CATEGORY_RESTORATION;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return current AutoRotateCategory
|
|
|
- */
|
|
|
- public int getAutoRotateCategory() {
|
|
|
- return mAutoRotateCategory;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * An image is scaled to an InitScale to fit the size of XuanImageView at the very beginning.
|
|
|
- * MaxScale = MaxScaleMultiple * InitScale holds.
|
|
|
- *
|
|
|
- * @param maxScaleMultiple
|
|
|
- */
|
|
|
- public void setMaxScaleMultiple(float maxScaleMultiple) {
|
|
|
- mMaxScaleMultiple = maxScaleMultiple;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return current MaxScaleMultiple
|
|
|
- */
|
|
|
- public float getMaxScaleMultiple() {
|
|
|
- return mMaxScaleMultiple;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * When image's current scale level is smaller than DoubleTabScale, the image will scale up to DoubleTapScale if an double-tap gesture is detected.
|
|
|
- * DoubleTapScale = DoubleTabScaleMultiple * InitScale holds.
|
|
|
- *
|
|
|
- * @param doubleTabScaleMultiple
|
|
|
- */
|
|
|
- public void setDoubleTabScaleMultiple(float doubleTabScaleMultiple) {
|
|
|
- mDoubleTabScaleMultiple = doubleTabScaleMultiple;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return current DoubleTabScaleMultiple
|
|
|
- */
|
|
|
- public float getDoubleTabScaleMultiple() {
|
|
|
- return mDoubleTabScaleMultiple;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * If current scale level is smaller than InitScale and image is not in rotation state,
|
|
|
- * the image will scale up to InitScale with SpringBackGradientScaleUpLevel step by step.
|
|
|
- * Default springBackGradientScaleUpLevel is 1.01f.
|
|
|
- *
|
|
|
- * @param springBackGradientScaleUpLevel
|
|
|
- */
|
|
|
- public void setSpringBackGradientScaleUpLevel(float springBackGradientScaleUpLevel) {
|
|
|
- mSpringBackGradientScaleUpLevel = springBackGradientScaleUpLevel;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return current SpringBackGradientScaleUpLevel
|
|
|
- */
|
|
|
- public float getSpringBackGradientScaleUpLevel() {
|
|
|
- return mSpringBackGradientScaleUpLevel;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * If current scale level is bigger than MaxScale and image is not in rotation state,
|
|
|
- * the image will scale down to MaxScale with SpringBackGradientScaleDownLevel step by step.
|
|
|
- * Default springBackGradientScaleDownLevel is 0.99f.
|
|
|
- *
|
|
|
- * @param springBackGradientScaleDownLevel
|
|
|
- */
|
|
|
- public void setSpringBackGradientScaleDownLevel(float springBackGradientScaleDownLevel) {
|
|
|
- mSpringBackGradientScaleDownLevel = springBackGradientScaleDownLevel;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return current SpringBackGradientScaleDownLevel
|
|
|
- */
|
|
|
- public float getSpringBackGradientScaleDownLevel() {
|
|
|
- return mSpringBackGradientScaleDownLevel;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * When image's current scale level is smaller than DoubleTabScale,
|
|
|
- * the image will scale up to DoubleTapScale with DoubleTapGradientScaleUpLevel step by step if a double-tap gesture is detected.
|
|
|
- * Default doubleTalGradientScaleUpLevel is 1.05f.
|
|
|
- *
|
|
|
- * @param doubleTapGradientScaleUpLevel
|
|
|
- */
|
|
|
- public void setDoubleTapGradientScaleUpLevel(float doubleTapGradientScaleUpLevel) {
|
|
|
- mDoubleTapGradientScaleUpLevel = doubleTapGradientScaleUpLevel;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return current DoubleTapGradientScaleUpLevel
|
|
|
- */
|
|
|
- public float getDoubleTapGradientScaleUpLevel() {
|
|
|
- return mDoubleTapGradientScaleUpLevel;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * When image's current scale level is bigger than DoubleTabScale,
|
|
|
- * the image will scale down to InitScale with DoubleTapGradientScaleDownLevel step by step if a double-tap gesture is detected.
|
|
|
- * Default doubleTabGradientScaleDownLevel is 0.95f.
|
|
|
- *
|
|
|
- * @param doubleTapGradientScaleDownLevel
|
|
|
- */
|
|
|
- public void setDoubleTabGradientScaleDownLevel(float doubleTapGradientScaleDownLevel) {
|
|
|
- mDoubleTapGradientScaleDownLevel = doubleTapGradientScaleDownLevel;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return current DoubleTapGradientScaleDownLevel
|
|
|
- */
|
|
|
- public float getDoubleTapGradientScaleDownLevel() {
|
|
|
- return mDoubleTapGradientScaleDownLevel;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * When image's current rotation angle is bigger than AutoRotationTrigger, the image will rotate in the same direction and scale back to it's initial state if rotation gesture is released.
|
|
|
- * When image's current rotation angle is smaller than AutoRotationTrigger, the image will rotate in the opposite direction and scale back to it's initial state if rotation gesture is released.
|
|
|
- * Default AutoRotationTrigger is 60 (degrees).
|
|
|
- *
|
|
|
- * @param autoRotationTrigger
|
|
|
- */
|
|
|
- public void setAutoRotationTrigger(float autoRotationTrigger) {
|
|
|
- this.autoRotationTrigger = autoRotationTrigger;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return current AutoRotationTrigger
|
|
|
- */
|
|
|
- public float getAutoRotationTrigger() {
|
|
|
- return autoRotationTrigger;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Default SpringBackRunnableDelay is 10 (milliseconds).
|
|
|
- *
|
|
|
- * @param delay
|
|
|
- */
|
|
|
- public void setSpringBackRunnableDelay(int delay) {
|
|
|
- springBackRunnableDelay = delay;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return current SpringBackRunnableDelay
|
|
|
- */
|
|
|
- public int getSpringBackRunnableDelay() {
|
|
|
- return springBackRunnableDelay;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Default DoubleTapRunnableDelay is 10 (milliseconds).
|
|
|
- *
|
|
|
- * @param delay
|
|
|
- */
|
|
|
- public void setDoubleTapScaleRunnableDelay(int delay) {
|
|
|
- doubleTabScaleRunnableDelay = delay;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return current DoubleTabScaleRunnableDelay
|
|
|
- */
|
|
|
- public int getDoubleTabScaleRunnableDelay() {
|
|
|
- return doubleTabScaleRunnableDelay;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Default AutoRotationRunnableDelay is 5 (milliseconds).
|
|
|
- *
|
|
|
- * @param delay
|
|
|
- */
|
|
|
- public void setAutoRotationRunnableDelay(int delay) {
|
|
|
- autoRotationRunnableDelay = delay;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return current AutoRotationRunnableDelay
|
|
|
- */
|
|
|
- public int getAutoRotationRunnalbleDelay() {
|
|
|
- return autoRotationRunnableDelay;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Default AutoRotationRunnableTimes is 10 (times).
|
|
|
- *
|
|
|
- * @param times
|
|
|
- */
|
|
|
- public void setAutoRotationRunnableTimes(int times) {
|
|
|
- autoRotationRunnableTimes = times;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return current AutoRotationRunnableTimes
|
|
|
- */
|
|
|
- public int getAutoRotationRunnableTimes() {
|
|
|
- return autoRotationRunnableTimes;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * Notice the Image can only start to be rotated when it's in initial state. But the image may be scaled up or down a little bit by ScaleGestureDetector
|
|
|
- * in advance when you try to rotate it, hence, currentScaleLevel is not precisely equal to initScaleLevel. Here, an AllowableFloatError is existed to handle
|
|
|
- * this situation. When Math.abs(currentScaleLevel - initScaleLevel) is smaller than allowableFloatError, RotateGestureDetector.onTouchEvent() can be invoked.
|
|
|
- * Default allowableFloatError is 1E-6, it should be compatible with most of devices. For devices whose display resolution and aspect ratio is not normal, allowableFloatError may
|
|
|
- * need to be tuned. eg., for Galaxy S8, 3E-3 works well. Of course, 3E-3 also works for most of devices because 1E-6 is smaller than 3E-3.
|
|
|
- * @param allowableFloatError
|
|
|
- */
|
|
|
- public void setAllowableFloatError(double allowableFloatError){
|
|
|
- this.allowableFloatError = allowableFloatError;
|
|
|
- }
|
|
|
-
|
|
|
- public double getAllowableFloatError(){
|
|
|
- return allowableFloatError;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * In AUTO_ROTATE_CATEGORY_MAGNETISM mode, the image may be showed under a fixed rotation angle like 90 degrees, 270 degrees,
|
|
|
- * 450 degrees, ect., then allowablePortraitFloatError should handle the situation when currentPortraitScaleLevel is not precisely
|
|
|
- * equal to initPortraitScaleLevel.
|
|
|
- * Default allowablePortraitFloatError is 1E-12, it should be compatible with most of devices. For devices whose display resolution and aspect ratio is not normal, allowablePortraitFloatError may
|
|
|
- * need to be tuned. eg., for Galaxy S8, 5E-8 works well. Of course, 5E-8 also works for most of devices because 1E-12 is smaller than 5E-8.
|
|
|
- * @see XuanImageView#setAllowableFloatError(double)
|
|
|
- *
|
|
|
- * @param allowablePortraitFloatError
|
|
|
- */
|
|
|
- public void setAllowablePortraitFloatError(double allowablePortraitFloatError){
|
|
|
- this.allowablePortraitFloatError = allowablePortraitFloatError;
|
|
|
- }
|
|
|
-
|
|
|
- public double getAllowablePortraitFloatError(){
|
|
|
- return allowablePortraitFloatError;
|
|
|
- }
|
|
|
-
|
|
|
-}
|