English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Android Implementation of Image Scaling and Dragging by Custom ImageView Control

Overview: By customizing the ImageView control, image zoom can be achieved by calling the custom component in the xml layout.

/**
* Custom ImageView control, allows for multi-touch zoom and drag of images
* 
* @author qiuwanyong
*/
public class MyImageView extends ImageView {
/**
* Constant for initialization status
*/
public static final int STATUS_INIT = 1;
/**
* Constant for image zoom in status
*/
public static final int STATUS_ZOOM_OUT = 2;
/**
* Constant for image zoom out status
*/
public static final int STATUS_ZOOM_IN = 3;
/**
* Constant for image drag status
*/
public static final int STATUS_MOVE = 4;
/**
* Matrix used for moving and scaling image transformations
*/
private Matrix matrix = new Matrix();
/**
* the Bitmap object to be displayed
*/
private Bitmap sourceBitmap;
/**
* Records the current operation status, optional values are STATUS_INIT, STATUS_ZOOM_OUT, STATUS_ZOOM_IN, and STATUS_MOVE
*/
private int currentStatus;
/**
* The width of the ZoomImageView control
*/
private int width;
/**
* The height of the ZoomImageView control
*/
private int height;
/**
* Record the horizontal coordinate of the center point when both fingers are placed on the screen
*/
private float centerPointX;
/**
* Record the vertical coordinate of the center point when both fingers are placed on the screen
*/
private float centerPointY;
/**
* Record the current width of the image, this value will change together when the image is zoomed
*/
private float currentBitmapWidth;
/**
* Record the current height of the image, this value will change together when the image is zoomed
*/
private float currentBitmapHeight;
/**
* Record the horizontal coordinate of the last finger movement
*/
private float lastXMove = -1;
/**
* Record the vertical coordinate of the last finger movement
*/
private float lastYMove = -1;
/**
* Record the movement distance of the finger on the horizontal coordinate
*/
private float movedDistanceX;
/**
* Record the movement distance of the finger on the vertical coordinate
*/
private float movedDistanceY;
/**
* Record the horizontal offset value of the image on the matrix
*/
private float totalTranslateX;
/**
* Record the vertical offset value of the image on the matrix
*/
private float totalTranslateY;
/**
* Record the total zoom ratio of the image on the matrix
*/
private float totalRatio;
/**
* Record the zoom ratio caused by the finger movement
*/
private float scaledRatio;
/**
* Record the zoom ratio of the image at initialization
*/
private float initRatio;
/**
* Record the distance between the last two fingers
*/
private double lastFingerDis;
/**
* The constructor of ZoomImageView, sets the current operating status to STATUS_INIT.
* 
* @param context
* @param attrs
*/
public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
currentStatus = STATUS_INIT;
}
/**
* Set the image to be displayed.
* 
* @param bitmap
* the Bitmap object to be displayed
*/
public void setImageBitmap(Bitmap bitmap) {
sourceBitmap = bitmap;
invalidate();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
// obtain the width and height of ZoomImageView separately
width = getWidth();
height = getHeight();
}
}
@SuppressLint("NewApi") @Override
public boolean onTouchEvent(MotionEvent event) {
if (initRatio == totalRatio) {
getParent().requestDisallowInterceptTouchEvent(false);
}
getParent().requestDisallowInterceptTouchEvent(true);
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_POINTER_DOWN:
if (event.getPointerCount() == 2) {
// when there are two fingers pressed on the screen, calculate the distance between the two fingers
lastFingerDis = distanceBetweenFingers(event);
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_MOVE:
if (event.getPointerCount() == 1) {
// the dragging state is only when a single finger moves on the screen
float xMove = event.getX();
float yMove = event.getY();
if (lastXMove == -1 && lastYMove == -1) {
lastXMove = xMove;
lastYMove = yMove;
}
currentStatus = STATUS_MOVE;
movedDistanceX = xMove - lastXMove;
movedDistanceY = yMove - lastYMove;
// perform boundary check, not allowing the image to be dragged out of the boundary
if (totalTranslateX + movedDistanceX > 0) {}}
movedDistanceX = 0;
} else if (width - (totalTranslateX + movedDistanceX) > currentBitmapWidth) {
movedDistanceX = 0;
}
if (totalTranslateY + movedDistanceY > 0) {
movedDistanceY = 0;
} else if (height - (totalTranslateY + movedDistanceY) > currentBitmapHeight) {
movedDistanceY = 0;
}
// Call the onDraw() method to draw the image
invalidate();
lastXMove = xMove;
lastYMove = yMove;
} else if (event.getPointerCount() == 2) {
// There are two fingers pressed on the screen and moving, in zoom state
centerPointBetweenFingers(event);
double fingerDis = distanceBetweenFingers(event);
if (fingerDis > lastFingerDis) {
currentStatus = STATUS_ZOOM_OUT;
}
currentStatus = STATUS_ZOOM_IN;
}
// Perform zoom ratio check, the maximum is only allowed to enlarge the image4times, the smallest can be reduced to the initial ratio
if ((currentStatus == STATUS_ZOOM_OUT && totalRatio < 4 * initRatio)
|| (currentStatus == STATUS_ZOOM_IN && totalRatio > initRatio)) {
scaledRatio = (float) (fingerDis / lastFingerDis);
totalRatio = totalRatio * scaledRatio;
if (totalRatio > 4 * initRatio) {
totalRatio = 4 * initRatio;
} else if (totalRatio < initRatio) {
totalRatio = initRatio;
}
// Call the onDraw() method to draw the image
invalidate();
lastFingerDis = fingerDis;
}
}
break;
case MotionEvent.ACTION_POINTER_UP:
if (event.getPointerCount() == 2) {
// Restore temporary values when the finger leaves the screen
lastXMove = -1;
lastYMove = -1;
}
break;
case MotionEvent.ACTION_UP:
// Restore temporary values when the finger leaves the screen
lastXMove = -1;
lastYMove = -1;
break;
default:
break;
}
return true;
}
/**
* Determine the drawing operation to perform on the image based on the value of currentStatus.
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (currentStatus) {
case STATUS_ZOOM_OUT:
case STATUS_ZOOM_IN:
zoom(canvas);
break;
case STATUS_MOVE:
move(canvas);
break;
case STATUS_INIT:
initBitmap(canvas);
default:
if (sourceBitmap != null) {
canvas.drawBitmap(sourceBitmap, matrix, null);
}
break;
}
}
/**
* Perform zoom processing on the image.
* 
* @param canvas
*/
private void zoom(Canvas canvas) {
matrix.reset();
// Scale the image by the total scaling ratio
matrix.postScale(totalRatio, totalRatio);
float scaledWidth = sourceBitmap.getWidth() * totalRatio;
float scaledHeight = sourceBitmap.getHeight() * totalRatio;
float translateX = 0f;
float translateY = 0f;
// If the current image width is less than the screen width, then horizontally scale based on the horizontal coordinate of the screen center. Otherwise, horizontally scale based on the horizontal coordinate of the center point of two fingers.
if (currentBitmapWidth < width) {
translateX = (width - scaledWidth) / 2f;
}
translateX = totalTranslateX * scaledRatio + centerPointX
* (1 - scaledRatio);
// Perform boundary checks to ensure that the image does not shift out of the screen horizontally after scaling.
if (translateX > 0) {
translateX = 0;
} else if (width - translateX > scaledWidth) {
translateX = width - scaledWidth;
}
}
// If the current image height is less than the screen height, then vertically scale based on the vertical coordinate of the screen center. Otherwise, vertically scale based on the vertical coordinate of the center point of two fingers.
if (currentBitmapHeight < height) {}}
translateY = (height - scaledHeight) / 2f;
}
translateY = totalTranslateY * scaledRatio + centerPointY
* (1 - scaledRatio);
// Perform boundary checks to ensure that the image does not offset out of the screen vertically after scaling
if (translateY > 0) {
translateY = 0;
} else if (height - translateY > scaledHeight) {
translateY = height - scaledHeight;
}
}
// Offset the image after scaling to ensure that the center point position does not change after scaling
matrix.postTranslate(translateX, translateY);
totalTranslateX = translateX;
totalTranslateY = translateY;
currentBitmapWidth = scaledWidth;
currentBitmapHeight = scaledHeight;
canvas.drawBitmap(sourceBitmap, matrix, null);
}
/**
* Perform translation processing on the image
* 
* @param canvas
*/
private void move(Canvas canvas) {
matrix.reset();
// Calculate the total offset value according to the distance of the finger movement
float translateX = totalTranslateX + movedDistanceX;
float translateY = totalTranslateY + movedDistanceY;
// First scale the image according to the existing scaling ratio
matrix.postScale(totalRatio, totalRatio);
// Then offset according to the distance of movement
matrix.postTranslate(translateX, translateY);
totalTranslateX = translateX;
totalTranslateY = translateY;
canvas.drawBitmap(sourceBitmap, matrix, null);
}
/**
* Initialize the image, including centering the image and compressing the image when the image is larger than the screen width and height.
* 
* @param canvas
*/
private void initBitmap(Canvas canvas) {
if (sourceBitmap != null) {
matrix.reset();
int bitmapWidth = sourceBitmap.getWidth();
int bitmapHeight = sourceBitmap.getHeight();
if (bitmapWidth > width || bitmapHeight > height) {
if (bitmapWidth - width > bitmapHeight - height) {
// When the width of the image is greater than the screen width, compress the image proportionally to make it fully visible
float ratio = width / (bitmapWidth * 1.0f);
matrix.postScale(ratio, ratio);
float translateY = (height - (bitmapHeight * ratio)) / 2f;
// Offset in the vertical coordinate direction to ensure the image is centered
matrix.postTranslate(0, translateY);
totalTranslateY = translateY;
totalRatio = initRatio = ratio;
}
// When the height of the image is greater than the screen height, compress the image proportionally to make it fully visible
float ratio = height / (bitmapHeight * 1.0f);
matrix.postScale(ratio, ratio);
float translateX = (width - (bitmapWidth * ratio)) / 2f;
// Offset in the horizontal coordinate direction to ensure the image is centered
matrix.postTranslate(translateX, 0);
totalTranslateX = translateX;
totalRatio = initRatio = ratio;
}
currentBitmapWidth = bitmapWidth * initRatio;
currentBitmapHeight = bitmapHeight * initRatio;
}
// When the width and height of the image are both less than the screen width and height, display the image centered directly
float translateX = (width - sourceBitmap.getWidth()) / 2f;
float translateY = (height - sourceBitmap.getHeight()) / 2f;
matrix.postTranslate(translateX, translateY);
totalTranslateX = translateX;
totalTranslateY = translateY;
totalRatio = initRatio = 1f;
currentBitmapWidth = bitmapWidth;
currentBitmapHeight = bitmapHeight;
}
canvas.drawBitmap(sourceBitmap, matrix, null);
}
}
/**
* Calculate the distance between two fingers.
* 
* @param event
* @return The distance between two fingers
*/
@SuppressLint("NewApi") private double distanceBetweenFingers(MotionEvent event) {
float disX = Math.abs(event.getX(0) - event.getX(1));
float disY = Math.abs(event.getY(0) - event.getY(1));
return Math.sqrt(disX * disX + disY * disY);
}
/**
* Calculate the coordinates of the center point between two fingers.
* 
* @param event
*/
@SuppressLint("NewApi") private void centerPointBetweenFingers(MotionEvent event) {
float xPoint0 = event.getX(0);
float yPoint0 = event.getY(0);
float xPoint1 = event.getX(1);
float yPoint1 = event.getY(1);
centerPointX = (xPoint0 + xPoint1) / 2;
centerPointY = (yPoint0 + yPoint1) / 2;
}
}

Call in layout

The code for implementing the zoom and drag of images through the custom ImageView control in Android, as described above, is introduced by the editor for everyone. I hope it will be helpful to everyone. If you have any questions, please leave a message, and the editor will reply to everyone in time. At the same time, I would also like to express my sincere gratitude to everyone for their support of the Nao Yan tutorial website!

Declaration: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been manually edited, and does not assume relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (when sending an email, please replace # with @ for reporting, and provide relevant evidence. Once verified, this site will immediately delete the infringing content.)

You May Also Like