Recently I used an implementation for a pinch to zoom in my TextureView using a SurfaceView. The zoom works perfectly but the view does not grow to the dimensions of the screen. In my case the TextureView is always in the dimensions 1080, 612. I would like to modify this zoom so that the view is filling the screen as I do the pinch.
Here is the code for my class that receives TextureView:
public class ScaleManager extends BaseInputManager {
private final TextureView mTargetView;
private final ScaleGestureDetector mScaleDetector;
private static final float MIN_SCALE = 1.0f;
private static final float MAX_SCALE = 5.0f;
private float mCurrentScale = MIN_SCALE;
private Matrix mMatrix = new Matrix();
private float[] mMatrixValues = new float[9];
private float mPreviousX;
private float mPreviousY;
private boolean mIsDragInProgress = false;
public ScaleManager(TextureView view) {
mTargetView = view;
mScaleDetector = new ScaleGestureDetector(view.getContext(), new ScaleListener());
mScaleDetector.setQuickScaleEnabled(false);
}
@Override
public boolean handle(KeyEvent event) {
return false;
}
@Override
public boolean handle(MotionEvent event) {
final int index = event.getActionIndex();
final int type = event.getToolType(index);
if (type == MotionEvent.TOOL_TYPE_FINGER || type == MotionEvent.TOOL_TYPE_STYLUS) {
mScaleDetector.onTouchEvent(event);
mMatrix.getValues(mMatrixValues);
boolean isScaling = (mMatrixValues[Matrix.MSCALE_X] != 1.0f && mMatrixValues[Matrix.MSCALE_Y] != 1.0f);
if (isScaling || mScaleDetector.isInProgress()) {
switch (event.getAction()) {
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_DOWN:
if (event.getPointerCount() <= 2) {
mPreviousX = event.getX();
mPreviousY = event.getY();
}
if (event.getPointerCount() == 2) {
mIsDragInProgress = true;
}
break;
case MotionEvent.ACTION_MOVE:
if (event.getPointerCount() == 2) {
float x = mMatrixValues[Matrix.MTRANS_X];
float y = mMatrixValues[Matrix.MTRANS_Y];
float width = mTargetView.getWidth();
float height = mTargetView.getHeight();
float right = width * mCurrentScale - width;
float bottom = height * mCurrentScale - height;
float deltaX = event.getX() - mPreviousX;// x difference
float deltaY = event.getY() - mPreviousY;// y difference
float scaledWidth = Math.round(width * mCurrentScale);// width after applying current scale
float scaledHeight = Math.round(height * mCurrentScale);// height after applying current scale
//if scaledWidth is smaller than the views width
//in other words if the image width fits in the view
//limit left and right movement
if (scaledWidth < width) {
deltaX = 0;
if (y + deltaY > 0) {
deltaY = -y;
} else if (y + deltaY < -bottom) {
deltaY = -(y + bottom);
}
//if scaledHeight is smaller than the views height
//in other words if the image height fits in the view
//limit up and down movement
} else if (scaledHeight < height) {
deltaY = 0;
if (x + deltaX > 0) {
deltaX = -x;
} else if (x + deltaX < -right) {
deltaX = -(x + right);
}
//if the image doesn't fit in the width or height
//limit both up and down and left and right
} else {
if (x + deltaX > 0) {
deltaX = -x;
} else if (x + deltaX < -right) {
deltaX = -(x + right);
}
if (y + deltaY > 0) {
deltaY = -y;
} else if (y + deltaY < -bottom) {
deltaY = -(y + bottom);
}
}
//move the image with the matrix
mMatrix.postTranslate(deltaX, deltaY);
mTargetView.setTransform(mMatrix);
mTargetView.invalidate();
mPreviousX = event.getX();
mPreviousY = event.getY();
}
break;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_UP:
if (event.getPointerCount() == 1) {
mIsDragInProgress = false;
}
break;
}
}
return mScaleDetector.isInProgress() || mIsDragInProgress;
}
return false;
}
private class ScaleListener implements ScaleGestureDetector.OnScaleGestureListener {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
float width = mTargetView.getWidth();
float height = mTargetView.getHeight();
float factor = detector.getScaleFactor();
float scale = mCurrentScale * factor;
if (scale > MAX_SCALE) {
factor = MAX_SCALE / mCurrentScale;
mCurrentScale = MAX_SCALE;
} else if (scale < MIN_SCALE) {
factor = MIN_SCALE / mCurrentScale;
mCurrentScale = MIN_SCALE;
} else {
mCurrentScale = scale;
}
if (width * scale <= width || height * scale <= height) {
mMatrix.postScale(factor, factor, width / 2, height / 2);
} else {
mMatrix.postScale(factor, factor, detector.getFocusX(), detector.getFocusY());
}
if (factor < 1) {
float right = width * mCurrentScale - width;
float bottom = height * mCurrentScale - height;
mMatrix.getValues(mMatrixValues);
float x = mMatrixValues[Matrix.MTRANS_X];
float y = mMatrixValues[Matrix.MTRANS_Y];
if (x < -right) {
mMatrix.postTranslate(-(x + right), 0);
} else if (x > 0) {
mMatrix.postTranslate(-x, 0);
}
if (y < -bottom) {
mMatrix.postTranslate(0, -(y + bottom));
} else if (y > 0) {
mMatrix.postTranslate(0, -y);
}
}
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
}
}
When I start the view I use a class called AspectFrameLayout
to determine the initial ratio of the view. And this AspectFrameLayout
is called each time the view is being modified to maintain the aspect ratio. Follow the code:
<com.examples.views.AspectRatioFrameLayout
android:id="@+id/player_video"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:focusable="true"
android:focusableInTouchMode="true" />
mSurfaceView = layoutInflater.inflate(R.layout.texture_view,
player_video_frame, false)
This is the AspectFrameLayout
class that I use to keep the aspect ratio:
public final class AspectRatioFrameLayout extends FrameLayout {
private static final float MAX_ASPECT_RATIO_DEFORMATION_FRACTION = 0.01f;
private float mVideoAspectRatio;
public AspectRatioFrameLayout(Context context) {
super(context);
}
public AspectRatioFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setAspectRatio(float ratio) {
if (this.mVideoAspectRatio != ratio) {
this.mVideoAspectRatio = ratio;
requestLayout();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mVideoAspectRatio == 0) {
// Aspect ratio not set.
return;
}
int width = getMeasuredWidth();
int height = getMeasuredHeight();
float viewAspectRatio = (float) width / height;
float aspectDeformation = mVideoAspectRatio / viewAspectRatio - 1;
if (Math.abs(aspectDeformation) <= MAX_ASPECT_RATIO_DEFORMATION_FRACTION) {
// We're within the allowed tolerance.
return;
}
if (aspectDeformation > 0) {
height = (int) (width / mVideoAspectRatio);
} else {
width = (int) (height * mVideoAspectRatio);
}
super.onMeasure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
);
}
}