Skip to content

Instantly share code, notes, and snippets.

@moxgeek
Last active June 8, 2020 11:47
Show Gist options
  • Save moxgeek/1f7906e9c44c1063fde07655c4c614e4 to your computer and use it in GitHub Desktop.
Save moxgeek/1f7906e9c44c1063fde07655c4c614e4 to your computer and use it in GitHub Desktop.
image zoom in/out , double tap, pinch , drag for gallery with RecyclerView. use setRecyclerView to freez your recycler while performing any kind of zooming gestures.
<moxgeek.lovely.package.MoxGeekImageView
android:id="@+id/image_preview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginBottom="15dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="15dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="@drawable/border_edittext"
android:scaleType="matrix" />
package com.nexy.nexinun.voipapp.utils;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.ImageView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class MoxGeekImageView extends ImageView {
// i was working on a gallery for images with zoom in and out , and double tap gesture
// i found alot of library to do that, but the problem it behave really bad with the rcyclerview.
// if you wanna zoom you need to be really carful so you not swipe to other image in your recycler gallery.
// PinchImageView.java (thanks to @boycy815) suffer from this problem too, it work fine until you wanna use your to fingers to zoom
// this gist is a solution for gallery cases by editing PinchImageView.
// you need to set the recycler to freez while using gestures, by calling imageViewPreview.setRecycler(recycler); in your declaration.
// i have been able to test it for several project, however please if you see any improvement just comment or contact me on moxgeek@gmail.com
public static final int SCALE_ANIMATOR_DURATION = 200;
public static final float FLING_DAMPING_FACTOR = 0.9f;
private static final float MAX_SCALE = 4f;
private OnClickListener mOnClickListener;
private OnLongClickListener mOnLongClickListener;
@Override
public void setOnClickListener(OnClickListener l) {
mOnClickListener = l;
}
@Override
public void setOnLongClickListener(OnLongClickListener l) {
mOnLongClickListener = l;
}
public static final int PINCH_MODE_FREE = 0;
public static final int PINCH_MODE_SCROLL = 1;
public static final int PINCH_MODE_SCALE = 2;
private Matrix mOuterMatrix = new Matrix();
private RectF mMask;
private int mPinchMode = PINCH_MODE_FREE;
public Matrix getOuterMatrix(Matrix matrix) {
if (matrix == null) {
matrix = new Matrix(mOuterMatrix);
} else {
matrix.set(mOuterMatrix);
}
return matrix;
}
public Matrix getInnerMatrix(Matrix matrix) {
if (matrix == null) {
matrix = new Matrix();
} else {
matrix.reset();
}
if (isReady()) {
RectF tempSrc = MathUtils.rectFTake(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
RectF tempDst = MathUtils.rectFTake(0, 0, getWidth(), getHeight());
matrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.CENTER);
MathUtils.rectFGiven(tempDst);
MathUtils.rectFGiven(tempSrc);
}
return matrix;
}
public Matrix getCurrentImageMatrix(Matrix matrix) {
matrix = getInnerMatrix(matrix);
matrix.postConcat(mOuterMatrix);
return matrix;
}
public RectF getImageBound(RectF rectF) {
if (rectF == null) {
rectF = new RectF();
} else {
rectF.setEmpty();
}
if (!isReady()) {
return rectF;
} else {
Matrix matrix = MathUtils.matrixTake();
getCurrentImageMatrix(matrix);
rectF.set(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
matrix.mapRect(rectF);
MathUtils.matrixGiven(matrix);
return rectF;
}
}
public RectF getMask() {
if (mMask != null) {
return new RectF(mMask);
} else {
return null;
}
}
public int getPinchMode() {
return mPinchMode;
}
@Override
public boolean canScrollHorizontally(int direction) {
if (mPinchMode == MoxGeekImageView.PINCH_MODE_SCALE) {
return true;
}
RectF bound = getImageBound(null);
if (bound == null) {
return false;
}
if (bound.isEmpty()) {
return false;
}
if (direction > 0) {
return bound.right > getWidth();
} else {
return bound.left < 0;
}
}
@Override
public boolean canScrollVertically(int direction) {
if (mPinchMode == MoxGeekImageView.PINCH_MODE_SCALE) {
return true;
}
RectF bound = getImageBound(null);
if (bound == null) {
return false;
}
if (bound.isEmpty()) {
return false;
}
if (direction > 0) {
return bound.bottom > getHeight();
} else {
return bound.top < 0;
}
}
public void outerMatrixTo(Matrix endMatrix, long duration) {
if (endMatrix == null) {
return;
}
mPinchMode = PINCH_MODE_FREE;
cancelAllAnimator();
if (duration <= 0) {
mOuterMatrix.set(endMatrix);
dispatchOuterMatrixChanged();
invalidate();
} else {
mScaleAnimator = new ScaleAnimator(mOuterMatrix, endMatrix, duration);
mScaleAnimator.start();
}
}
public void zoomMaskTo(RectF mask, long duration) {
if (mask == null) {
return;
}
if (mMaskAnimator != null) {
mMaskAnimator.cancel();
mMaskAnimator = null;
}
if (duration <= 0 || mMask == null) {
if (mMask == null) {
mMask = new RectF();
}
mMask.set(mask);
invalidate();
} else {
mMaskAnimator = new MaskAnimator(mMask, mask, duration);
mMaskAnimator.start();
}
}
public void reset() {
mOuterMatrix.reset();
dispatchOuterMatrixChanged();
mMask = null;
mPinchMode = PINCH_MODE_FREE;
mLastMovePoint.set(0, 0);
mScaleCenter.set(0, 0);
mScaleBase = 0;
if (mMaskAnimator != null) {
mMaskAnimator.cancel();
mMaskAnimator = null;
}
cancelAllAnimator();
invalidate();
}
RecyclerView recyclerView ;
boolean isZoomed;
public void setRecycler(RecyclerView recycler) {
recyclerView = recycler;
}
public interface OuterMatrixChangedListener {
void onOuterMatrixChanged(MoxGeekImageView MoxGeekImageView);
}
private List<OuterMatrixChangedListener> mOuterMatrixChangedListeners;
private List<OuterMatrixChangedListener> mOuterMatrixChangedListenersCopy;
private int mDispatchOuterMatrixChangedLock;
public void addOuterMatrixChangedListener(OuterMatrixChangedListener listener) {
if (listener == null) {
return;
}
if (mDispatchOuterMatrixChangedLock == 0) {
if (mOuterMatrixChangedListeners == null) {
mOuterMatrixChangedListeners = new ArrayList<OuterMatrixChangedListener>();
}
mOuterMatrixChangedListeners.add(listener);
} else {
if (mOuterMatrixChangedListenersCopy == null) {
if (mOuterMatrixChangedListeners != null) {
mOuterMatrixChangedListenersCopy = new ArrayList<OuterMatrixChangedListener>(mOuterMatrixChangedListeners);
} else {
mOuterMatrixChangedListenersCopy = new ArrayList<OuterMatrixChangedListener>();
}
}
mOuterMatrixChangedListenersCopy.add(listener);
}
}
public void removeOuterMatrixChangedListener(OuterMatrixChangedListener listener) {
if (listener == null) {
return;
}
if (mDispatchOuterMatrixChangedLock == 0) {
if (mOuterMatrixChangedListeners != null) {
mOuterMatrixChangedListeners.remove(listener);
}
} else {
if (mOuterMatrixChangedListenersCopy == null) {
if (mOuterMatrixChangedListeners != null) {
mOuterMatrixChangedListenersCopy = new ArrayList<OuterMatrixChangedListener>(mOuterMatrixChangedListeners);
}
}
if (mOuterMatrixChangedListenersCopy != null) {
mOuterMatrixChangedListenersCopy.remove(listener);
}
}
}
private void dispatchOuterMatrixChanged() {
if (mOuterMatrixChangedListeners == null) {
return;
}
mDispatchOuterMatrixChangedLock++;
for (OuterMatrixChangedListener listener : mOuterMatrixChangedListeners) {
listener.onOuterMatrixChanged(this);
}
mDispatchOuterMatrixChangedLock--;
if (mDispatchOuterMatrixChangedLock == 0) {
if (mOuterMatrixChangedListenersCopy != null) {
mOuterMatrixChangedListeners = mOuterMatrixChangedListenersCopy;
mOuterMatrixChangedListenersCopy = null;
}
}
}
protected float getMaxScale() {
return MAX_SCALE;
}
protected float calculateNextScale(float innerScale, float outerScale) {
float currentScale = innerScale * outerScale;
if (currentScale < MAX_SCALE) {
return MAX_SCALE;
} else {
return innerScale;
}
}
public MoxGeekImageView(Context context) {
super(context);
initView();
}
public MoxGeekImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public MoxGeekImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
private void initView() {
super.setScaleType(ScaleType.MATRIX);
}
@Override
public void setScaleType(ScaleType scaleType) {}
@Override
protected void onDraw(Canvas canvas) {
if (isReady()) {
Matrix matrix = MathUtils.matrixTake();
setImageMatrix(getCurrentImageMatrix(matrix));
MathUtils.matrixGiven(matrix);
}
if (mMask != null) {
canvas.save();
canvas.clipRect(mMask);
super.onDraw(canvas);
canvas.restore();
} else {
super.onDraw(canvas);
}
}
private boolean isReady() {
return getDrawable() != null && getDrawable().getIntrinsicWidth() > 0 && getDrawable().getIntrinsicHeight() > 0
&& getWidth() > 0 && getHeight() > 0;
}
private MaskAnimator mMaskAnimator;
private class MaskAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener {
private float[] mStart = new float[4];
private float[] mEnd = new float[4];
private float[] mResult = new float[4];
public MaskAnimator(RectF start, RectF end, long duration) {
super();
setFloatValues(0, 1f);
setDuration(duration);
addUpdateListener(this);
mStart[0] = start.left;
mStart[1] = start.top;
mStart[2] = start.right;
mStart[3] = start.bottom;
mEnd[0] = end.left;
mEnd[1] = end.top;
mEnd[2] = end.right;
mEnd[3] = end.bottom;
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
for (int i = 0; i < 4; i++) {
mResult[i] = mStart[i] + (mEnd[i] - mStart[i]) * value;
}
if (mMask == null) {
mMask = new RectF();
}
mMask.set(mResult[0], mResult[1], mResult[2], mResult[3]);
invalidate();
}
}
private PointF mLastMovePoint = new PointF();
private PointF mScaleCenter = new PointF();
private float mScaleBase = 0;
private ScaleAnimator mScaleAnimator;
private FlingAnimator mFlingAnimator;
private GestureDetector mGestureDetector = new GestureDetector(MoxGeekImageView.this.getContext(), new GestureDetector.SimpleOnGestureListener() {
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mPinchMode == PINCH_MODE_FREE && !(mScaleAnimator != null && mScaleAnimator.isRunning())) {
fling(velocityX, velocityY);
}
return true;
}
public void onLongPress(MotionEvent e) {
if (mOnLongClickListener != null) {
mOnLongClickListener.onLongClick(MoxGeekImageView.this);
}
}
public boolean onDoubleTap(MotionEvent e) {
if (mPinchMode == PINCH_MODE_SCROLL && !(mScaleAnimator != null && mScaleAnimator.isRunning())) {
doubleTap(e.getX(), e.getY());
}
return true;
}
public boolean onSingleTapConfirmed(MotionEvent e) {
if (mOnClickListener != null) {
mOnClickListener.onClick(MoxGeekImageView.this);
}
return true;
}
});
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
int action = event.getAction() & MotionEvent.ACTION_MASK;
if(action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
if (mPinchMode == PINCH_MODE_SCALE) {
scaleEnd();
}
mPinchMode = PINCH_MODE_FREE;
} else if (action == MotionEvent.ACTION_POINTER_UP) {
if (mPinchMode == PINCH_MODE_SCALE) {
if (event.getPointerCount() > 2) {
if (event.getAction() >> 8 == 0) {
saveScaleContext(event.getX(1), event.getY(1), event.getX(2), event.getY(2));
} else if (event.getAction() >> 8 == 1) {
saveScaleContext(event.getX(0), event.getY(0), event.getX(2), event.getY(2));
}
}
}
} else if (action == MotionEvent.ACTION_DOWN) {
if (!(mScaleAnimator != null && mScaleAnimator.isRunning())) {
cancelAllAnimator();
mPinchMode = PINCH_MODE_SCROLL;
mLastMovePoint.set(event.getX(), event.getY());
}
} else if (action == MotionEvent.ACTION_POINTER_DOWN) {
recyclerView.setLayoutFrozen(true);
cancelAllAnimator();
mPinchMode = PINCH_MODE_SCALE;
saveScaleContext(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
} else if (action == MotionEvent.ACTION_MOVE) {
if (!(mScaleAnimator != null && mScaleAnimator.isRunning())) {
if (mPinchMode == PINCH_MODE_SCROLL) {
scrollBy(event.getX() - mLastMovePoint.x, event.getY() - mLastMovePoint.y);
mLastMovePoint.set(event.getX(), event.getY());
} else if (mPinchMode == PINCH_MODE_SCALE && event.getPointerCount() > 1) {
float distance = MathUtils.getDistance(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
float[] lineCenter = MathUtils.getCenterPoint(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
mLastMovePoint.set(lineCenter[0], lineCenter[1]);
scale(mScaleCenter, mScaleBase, distance, mLastMovePoint);
}
}
}
mGestureDetector.onTouchEvent(event);
return true;
}
private boolean scrollBy(float xDiff, float yDiff) {
if (!isReady()) {
return false;
}
RectF bound = MathUtils.rectFTake();
getImageBound(bound);
float displayWidth = getWidth();
float displayHeight = getHeight();
if (bound.right - bound.left < displayWidth) {
xDiff = 0;
} else if (bound.left + xDiff > 0) {
if (bound.left < 0) {
xDiff = -bound.left;
} else {
xDiff = 0;
}
} else if (bound.right + xDiff < displayWidth) {
if (bound.right > displayWidth) {
xDiff = displayWidth - bound.right;
} else {
xDiff = 0;
}
}
if (bound.bottom - bound.top < displayHeight) {
yDiff = 0;
} else if (bound.top + yDiff > 0) {
if (bound.top < 0) {
yDiff = -bound.top;
} else {
yDiff = 0;
}
} else if (bound.bottom + yDiff < displayHeight) {
if (bound.bottom > displayHeight) {
yDiff = displayHeight - bound.bottom;
} else {
yDiff = 0;
}
}
MathUtils.rectFGiven(bound);
mOuterMatrix.postTranslate(xDiff, yDiff);
dispatchOuterMatrixChanged();
invalidate();
if (xDiff != 0 || yDiff != 0) {
return true;
} else {
return false;
}
}
private void saveScaleContext(float x1, float y1, float x2, float y2) {
mScaleBase = MathUtils.getMatrixScale(mOuterMatrix)[0] / MathUtils.getDistance(x1, y1, x2, y2);
float[] center = MathUtils.inverseMatrixPoint(MathUtils.getCenterPoint(x1, y1, x2, y2), mOuterMatrix);
mScaleCenter.set(center[0], center[1]);
}
private void scale(PointF scaleCenter, float scaleBase, float distance, PointF lineCenter) {
if (!isReady()) {
return;
}
float scale = scaleBase * distance;
Matrix matrix = MathUtils.matrixTake();
matrix.postScale(scale, scale, scaleCenter.x, scaleCenter.y);
matrix.postTranslate(lineCenter.x - scaleCenter.x, lineCenter.y - scaleCenter.y);
mOuterMatrix.set(matrix);
MathUtils.matrixGiven(matrix);
dispatchOuterMatrixChanged();
invalidate();
}
private void doubleTap(float x, float y) {
if (!isReady()) {
return;
}
Matrix innerMatrix = MathUtils.matrixTake();
getInnerMatrix(innerMatrix);
float innerScale = MathUtils.getMatrixScale(innerMatrix)[0];
float outerScale = MathUtils.getMatrixScale(mOuterMatrix)[0];
float currentScale = innerScale * outerScale;
float displayWidth = getWidth();
float displayHeight = getHeight();
float maxScale = getMaxScale();
float nextScale = calculateNextScale(innerScale, outerScale);
if (nextScale > maxScale) {
nextScale = maxScale;
}
if (nextScale < innerScale) {
nextScale = innerScale;
}
Matrix animEnd = MathUtils.matrixTake(mOuterMatrix);
animEnd.postScale(nextScale / currentScale, nextScale / currentScale, x, y);
animEnd.postTranslate(displayWidth / 2f - x, displayHeight / 2f - y);
Matrix testMatrix = MathUtils.matrixTake(innerMatrix);
testMatrix.postConcat(animEnd);
RectF testBound = MathUtils.rectFTake(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
testMatrix.mapRect(testBound);
float postX = 0;
float postY = 0;
if (testBound.right - testBound.left < displayWidth) {
postX = displayWidth / 2f - (testBound.right + testBound.left) / 2f;
} else if (testBound.left > 0) {
postX = -testBound.left;
} else if (testBound.right < displayWidth) {
postX = displayWidth - testBound.right;
}
if (testBound.bottom - testBound.top < displayHeight) {
postY = displayHeight / 2f - (testBound.bottom + testBound.top) / 2f;
} else if (testBound.top > 0) {
postY = -testBound.top;
} else if (testBound.bottom < displayHeight) {
postY = displayHeight - testBound.bottom;
}
animEnd.postTranslate(postX, postY);
if (postX!=0){
isZoomed=false;
recyclerView.setLayoutFrozen(false);
}
else{ isZoomed=true;
recyclerView.setLayoutFrozen(true);
}
cancelAllAnimator();
mScaleAnimator = new ScaleAnimator(mOuterMatrix, animEnd);
mScaleAnimator.start();
MathUtils.rectFGiven(testBound);
MathUtils.matrixGiven(testMatrix);
MathUtils.matrixGiven(animEnd);
MathUtils.matrixGiven(innerMatrix);
}
private void scaleEnd() {
recyclerView.setLayoutFrozen(true);
if (!isReady()) {
return;
}
boolean change = false;
Matrix currentMatrix = MathUtils.matrixTake();
getCurrentImageMatrix(currentMatrix);
float currentScale = MathUtils.getMatrixScale(currentMatrix)[0];
float outerScale = MathUtils.getMatrixScale(mOuterMatrix)[0];
float displayWidth = getWidth();
float displayHeight = getHeight();
float maxScale = getMaxScale();
float scalePost = 1f;
float postX = 0;
float postY = 0;
if (currentScale > maxScale) {
scalePost = maxScale / currentScale;
}
if (outerScale * scalePost < 1f) {
recyclerView.setLayoutFrozen(false);
scalePost = 1f / outerScale;
}
if (scalePost != 1f) {
change = true;
}
Matrix testMatrix = MathUtils.matrixTake(currentMatrix);
testMatrix.postScale(scalePost, scalePost, mLastMovePoint.x, mLastMovePoint.y);
RectF testBound = MathUtils.rectFTake(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
testMatrix.mapRect(testBound);
if (testBound.right - testBound.left < displayWidth) {
postX = displayWidth / 2f - (testBound.right + testBound.left) / 2f;
} else if (testBound.left > 0) {
postX = -testBound.left;
} else if (testBound.right < displayWidth) {
postX = displayWidth - testBound.right;
}
if (testBound.bottom - testBound.top < displayHeight) {
postY = displayHeight / 2f - (testBound.bottom + testBound.top) / 2f;
} else if (testBound.top > 0) {
postY = -testBound.top;
} else if (testBound.bottom < displayHeight) {
postY = displayHeight - testBound.bottom;
}
if (postX != 0 || postY != 0) {
change = true;
}
if (change) {
Matrix animEnd = MathUtils.matrixTake(mOuterMatrix);
animEnd.postScale(scalePost, scalePost, mLastMovePoint.x, mLastMovePoint.y);
animEnd.postTranslate(postX, postY);
cancelAllAnimator();
mScaleAnimator = new ScaleAnimator(mOuterMatrix, animEnd);
mScaleAnimator.start();
MathUtils.matrixGiven(animEnd);
}
MathUtils.rectFGiven(testBound);
MathUtils.matrixGiven(testMatrix);
MathUtils.matrixGiven(currentMatrix);
}
private void fling(float vx, float vy) {
if (!isReady()) {
return;
}
cancelAllAnimator();
mFlingAnimator = new FlingAnimator(vx / 60f, vy / 60f);
mFlingAnimator.start();
}
private void cancelAllAnimator() {
if (mScaleAnimator != null) {
mScaleAnimator.cancel();
mScaleAnimator = null;
}
if (mFlingAnimator != null) {
mFlingAnimator.cancel();
mFlingAnimator = null;
}
}
private class FlingAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener {
private float[] mVector;
public FlingAnimator(float vectorX, float vectorY) {
super();
setFloatValues(0, 1f);
setDuration(1000000);
addUpdateListener(this);
mVector = new float[]{vectorX, vectorY};
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
boolean result = scrollBy(mVector[0], mVector[1]);
mVector[0] *= FLING_DAMPING_FACTOR;
mVector[1] *= FLING_DAMPING_FACTOR;
if (!result || MathUtils.getDistance(0, 0, mVector[0], mVector[1]) < 1f) {
animation.cancel();
}
}
}
private class ScaleAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener {
private float[] mStart = new float[9];
private float[] mEnd = new float[9];
private float[] mResult = new float[9];
public ScaleAnimator(Matrix start, Matrix end) {
this(start, end, SCALE_ANIMATOR_DURATION);
}
public ScaleAnimator(Matrix start, Matrix end, long duration) {
super();
setFloatValues(0, 1f);
setDuration(duration);
addUpdateListener(this);
start.getValues(mStart);
end.getValues(mEnd);
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
for (int i = 0; i < 9; i++) {
mResult[i] = mStart[i] + (mEnd[i] - mStart[i]) * value;
}
mOuterMatrix.setValues(mResult);
dispatchOuterMatrixChanged();
invalidate();
}
}
private static abstract class ObjectsPool<T> {
private int mSize;
private Queue<T> mQueue;
public ObjectsPool(int size) {
mSize = size;
mQueue = new LinkedList<T>();
}
public T take() {
if (mQueue.size() == 0) {
return newInstance();
} else {
return resetInstance(mQueue.poll());
}
}
public void given(T obj) {
if (obj != null && mQueue.size() < mSize) {
mQueue.offer(obj);
}
}
abstract protected T newInstance();
abstract protected T resetInstance(T obj);
}
private static class MatrixPool extends ObjectsPool<Matrix> {
public MatrixPool(int size) {
super(size);
}
@Override
protected Matrix newInstance() {
return new Matrix();
}
@Override
protected Matrix resetInstance(Matrix obj) {
obj.reset();
return obj;
}
}
private static class RectFPool extends ObjectsPool<RectF> {
public RectFPool(int size) {
super(size);
}
@Override
protected RectF newInstance() {
return new RectF();
}
@Override
protected RectF resetInstance(RectF obj) {
obj.setEmpty();
return obj;
}
}
public static class MathUtils {
private static MatrixPool mMatrixPool = new MatrixPool(16);
public static Matrix matrixTake() {
return mMatrixPool.take();
}
public static Matrix matrixTake(Matrix matrix) {
Matrix result = mMatrixPool.take();
if (matrix != null) {
result.set(matrix);
}
return result;
}
public static void matrixGiven(Matrix matrix) {
mMatrixPool.given(matrix);
}
private static RectFPool mRectFPool = new RectFPool(16);
public static RectF rectFTake() {
return mRectFPool.take();
}
public static RectF rectFTake(float left, float top, float right, float bottom) {
RectF result = mRectFPool.take();
result.set(left, top, right, bottom);
return result;
}
public static RectF rectFTake(RectF rectF) {
RectF result = mRectFPool.take();
if (rectF != null) {
result.set(rectF);
}
return result;
}
public static void rectFGiven(RectF rectF) {
mRectFPool.given(rectF);
}
public static float getDistance(float x1, float y1, float x2, float y2) {
float x = x1 - x2;
float y = y1 - y2;
return (float) Math.sqrt(x * x + y * y);
}
public static float[] getCenterPoint(float x1, float y1, float x2, float y2) {
return new float[]{(x1 + x2) / 2f, (y1 + y2) / 2f};
}
public static float[] getMatrixScale(Matrix matrix) {
if (matrix != null) {
float[] value = new float[9];
matrix.getValues(value);
return new float[]{value[0], value[4]};
} else {
return new float[2];
}
}
public static float[] inverseMatrixPoint(float[] point, Matrix matrix) {
if (point != null && matrix != null) {
float[] dst = new float[2];
Matrix inverse = matrixTake();
matrix.invert(inverse);
inverse.mapPoints(dst, point);
matrixGiven(inverse);
return dst;
} else {
return new float[2];
}
}
public static void calculateRectTranslateMatrix(RectF from, RectF to, Matrix result) {
if (from == null || to == null || result == null) {
return;
}
if (from.width() == 0 || from.height() == 0) {
return;
}
result.reset();
result.postTranslate(-from.left, -from.top);
result.postScale(to.width() / from.width(), to.height() / from.height());
result.postTranslate(to.left, to.top);
}
public static void calculateScaledRectInContainer(RectF container, float srcWidth, float srcHeight, ScaleType scaleType, RectF result) {
if (container == null || result == null) {
return;
}
if (srcWidth == 0 || srcHeight == 0) {
return;
}
if (scaleType == null) {
scaleType = ScaleType.FIT_CENTER;
}
result.setEmpty();
if (ScaleType.FIT_XY.equals(scaleType)) {
result.set(container);
} else if (ScaleType.CENTER.equals(scaleType)) {
Matrix matrix = matrixTake();
RectF rect = rectFTake(0, 0, srcWidth, srcHeight);
matrix.setTranslate((container.width() - srcWidth) * 0.5f, (container.height() - srcHeight) * 0.5f);
matrix.mapRect(result, rect);
rectFGiven(rect);
matrixGiven(matrix);
result.left += container.left;
result.right += container.left;
result.top += container.top;
result.bottom += container.top;
} else if (ScaleType.CENTER_CROP.equals(scaleType)) {
Matrix matrix = matrixTake();
RectF rect = rectFTake(0, 0, srcWidth, srcHeight);
float scale;
float dx = 0;
float dy = 0;
if (srcWidth * container.height() > container.width() * srcHeight) {
scale = container.height() / srcHeight;
dx = (container.width() - srcWidth * scale) * 0.5f;
} else {
scale = container.width() / srcWidth;
dy = (container.height() - srcHeight * scale) * 0.5f;
}
matrix.setScale(scale, scale);
matrix.postTranslate(dx, dy);
matrix.mapRect(result, rect);
rectFGiven(rect);
matrixGiven(matrix);
result.left += container.left;
result.right += container.left;
result.top += container.top;
result.bottom += container.top;
} else if (ScaleType.CENTER_INSIDE.equals(scaleType)) {
Matrix matrix = matrixTake();
RectF rect = rectFTake(0, 0, srcWidth, srcHeight);
float scale;
float dx;
float dy;
if (srcWidth <= container.width() && srcHeight <= container.height()) {
scale = 1f;
} else {
scale = Math.min(container.width() / srcWidth, container.height() / srcHeight);
}
dx = (container.width() - srcWidth * scale) * 0.5f;
dy = (container.height() - srcHeight * scale) * 0.5f;
matrix.setScale(scale, scale);
matrix.postTranslate(dx, dy);
matrix.mapRect(result, rect);
rectFGiven(rect);
matrixGiven(matrix);
result.left += container.left;
result.right += container.left;
result.top += container.top;
result.bottom += container.top;
} else if (ScaleType.FIT_CENTER.equals(scaleType)) {
Matrix matrix = matrixTake();
RectF rect = rectFTake(0, 0, srcWidth, srcHeight);
RectF tempSrc = rectFTake(0, 0, srcWidth, srcHeight);
RectF tempDst = rectFTake(0, 0, container.width(), container.height());
matrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.CENTER);
matrix.mapRect(result, rect);
rectFGiven(tempDst);
rectFGiven(tempSrc);
rectFGiven(rect);
matrixGiven(matrix);
result.left += container.left;
result.right += container.left;
result.top += container.top;
result.bottom += container.top;
} else if (ScaleType.FIT_START.equals(scaleType)) {
Matrix matrix = matrixTake();
RectF rect = rectFTake(0, 0, srcWidth, srcHeight);
RectF tempSrc = rectFTake(0, 0, srcWidth, srcHeight);
RectF tempDst = rectFTake(0, 0, container.width(), container.height());
matrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.START);
matrix.mapRect(result, rect);
rectFGiven(tempDst);
rectFGiven(tempSrc);
rectFGiven(rect);
matrixGiven(matrix);
result.left += container.left;
result.right += container.left;
result.top += container.top;
result.bottom += container.top;
} else if (ScaleType.FIT_END.equals(scaleType)) {
Matrix matrix = matrixTake();
RectF rect = rectFTake(0, 0, srcWidth, srcHeight);
RectF tempSrc = rectFTake(0, 0, srcWidth, srcHeight);
RectF tempDst = rectFTake(0, 0, container.width(), container.height());
matrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.END);
matrix.mapRect(result, rect);
rectFGiven(tempDst);
rectFGiven(tempSrc);
rectFGiven(rect);
matrixGiven(matrix);
result.left += container.left;
result.right += container.left;
result.top += container.top;
result.bottom += container.top;
} else {
result.set(container);
}
}
}
}
MoxGeekImageView imageViewPreview = itemView.findViewById(R.id.image_preview);
imageViewPreview.setRecycler(recycler);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment