SoFunction
Updated on 2025-03-10

An example of the Android App that implements the function of double-clicking to enlarge and reduce pictures

Let’s first look at a very simple method of scaling the core image:

public static Bitmap scale(Bitmap bitmap, float scaleWidth, float scaleHeight) { 
  int width = (); 
  int height = (); 
  Matrix matrix = new Matrix(); 
  (scaleWidth, scaleHeight); 
  (TAG, "scaleWidth:"+ scaleWidth +", scaleHeight:"+ scaleHeight); 
  return (bitmap, 0, 0, width, height, matrix, true); 
} 

Note that the scale should be set correctly, otherwise memory may overflow. For example, I encountered this problem when using image scaling:

: bitmap size exceeds 32bits

Later, I looked up the code line by line and found that the scale calculation error was the scale, and the original image was enlarged by more than 20 times, resulting in memory overflow. It was normal after re-modifying the scale value.

Okay, let’s take a look at this module that implements two levels of zooming and original large.
Functions include:

  • Zoom in with the touch point as the center (this is not available in other codes on the Internet)
  • Boundary control (this is not available in other codes on the Internet)
  • Double-click to zoom in or out (mainly consider the resistor screen)
  • Multi-touch zoom in and out

This module has passed the test and has been used by users for a while, so it is relatively stable.

The following is the code and usage method (I have not written a test project, please forgive me):

ImageControl is similar to a user-defined ImageView control. The usage will be posted in the following code.

import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
 
public class ImageControl extends ImageView { 
  public ImageControl(Context context) { 
    super(context); 
    // TODO Auto-generated constructor stub 
  } 
 
  public ImageControl(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    // TODO Auto-generated constructor stub 
  } 
 
  public ImageControl(Context context, AttributeSet attrs, int defStyle) { 
    super(context, attrs, defStyle); 
    // TODO Auto-generated constructor stub 
  } 
 
  // ImageView img; 
  Matrix imgMatrix = null; // Define the transformation matrix of the picture 
  static final int DOUBLE_CLICK_TIME_SPACE = 300; // Double-click the time interval  static final int DOUBLE_POINT_DISTANCE = 10; // Two points enlarge the minimum spacing between two points  static final int NONE = 0; 
  static final int DRAG = 1; // Drag operation  static final int ZOOM = 2; // Zoom in and out operation  private int mode = NONE; // Current mode 
  float bigScale = 3f; // Default magnification  Boolean isBig = false; // Is it in a zoomed state  long lastClickTime = 0; // Click time  float startDistance; // Multi-touch two points distance  float endDistance; // Multi-touch two points distance 
  float topHeight; // Status bar height and title bar height  Bitmap primaryBitmap = null; 
 
  float contentW; // Width of the screen content area  float contentH; // The height of the screen content area 
  float primaryW; // Original image width  float primaryH; // Original image height 
  float scale; // Suitable for screen zoom multiples  Boolean isMoveX = true; // Is it allowed to drag on the X-axis  Boolean isMoveY = true; // Is it allowed to drag on the Y axis  float startX; 
  float startY; 
  float endX; 
  float endY; 
  float subX; 
  float subY; 
  float limitX1; 
  float limitX2; 
  float limitY1; 
  float limitY2; 
  ICustomMethod mCustomMethod = null; 
 
  /**
    * Initialize the picture
    *
    * @param bitmap
    * Image to display
    * @param contentW
    * Content area width
    * @param contentH
    * Content area height
    * @param topHeight
    * Sum of status bar height and title bar height
    */ 
  public void imageInit(Bitmap bitmap, int contentW, int contentH, 
      int topHeight, ICustomMethod iCustomMethod) { 
     = bitmap; 
     = contentW; 
     = contentH; 
     = topHeight; 
    mCustomMethod = iCustomMethod; 
    primaryW = (); 
    primaryH = (); 
    float scaleX = (float) contentW / primaryW; 
    float scaleY = (float) contentH / primaryH; 
    scale = scaleX < scaleY ? scaleX : scaleY; 
    if (scale < 1 && 1 / scale < bigScale) { 
      bigScale = (float) (1 / scale + 0.5); 
    } 
 
    imgMatrix = new Matrix(); 
    subX = (contentW - primaryW * scale) / 2; 
    subY = (contentH - primaryH * scale) / 2; 
    (primaryBitmap); 
    (); 
    (scale, scale); 
    (subX, subY); 
    (imgMatrix); 
  } 
 
  /**
    * Press to operate
    *
    * @param event
    */ 
  public void mouseDown(MotionEvent event) { 
    mode = NONE; 
    startX = (); 
    startY = (); 
    if (() == 1) { 
      // If the time interval between two clicks is less than a certain value, the default is the double-click event      if (() - lastClickTime < DOUBLE_CLICK_TIME_SPACE) { 
        changeSize(startX, startY); 
      } else if (isBig) { 
        mode = DRAG; 
      } 
    } 
 
    lastClickTime = (); 
  } 
 
  /**
    * Non-first click operation
    *
    * @param event
    */ 
  public void mousePointDown(MotionEvent event) { 
    startDistance = getDistance(event); 
    if (startDistance > DOUBLE_POINT_DISTANCE) { 
      mode = ZOOM; 
    } else { 
      mode = NONE; 
    } 
  } 
 
  /**
    * Mobile operation
    *
    * @param event
    */ 
  public void mouseMove(MotionEvent event) { 
    if ((mode == DRAG) && (isMoveX || isMoveY)) { 
      float[] XY = getTranslateXY(imgMatrix); 
      float transX = 0; 
      float transY = 0; 
      if (isMoveX) { 
        endX = (); 
        transX = endX - startX; 
        if ((XY[0] + transX) <= limitX1) { 
          transX = limitX1 - XY[0]; 
        } 
        if ((XY[0] + transX) >= limitX2) { 
          transX = limitX2 - XY[0]; 
        } 
      } 
      if (isMoveY) { 
        endY = (); 
        transY = endY - startY; 
        if ((XY[1] + transY) <= limitY1) { 
          transY = limitY1 - XY[1]; 
        } 
        if ((XY[1] + transY) >= limitY2) { 
          transY = limitY2 - XY[1]; 
        } 
      } 
 
      (transX, transY); 
      startX = endX; 
      startY = endY; 
      (imgMatrix); 
    } else if (mode == ZOOM && () > 1) { 
      endDistance = getDistance(event); 
      float dif = endDistance - startDistance; 
      if ((endDistance - startDistance) > DOUBLE_POINT_DISTANCE) { 
        if (isBig) { 
          if (dif < 0) { 
            changeSize(0, 0); 
            mode = NONE; 
          } 
        } else if (dif > 0) { 
          float x = (0) / 2 + (1) / 2; 
          float y = (0) / 2 + (1) / 2; 
          changeSize(x, y); 
          mode = NONE; 
        } 
      } 
    } 
  } 
 
  /**
    * Mouse lift event
    */ 
  public void mouseUp() { 
    mode = NONE; 
  } 
 
  /**
    * Picture enlarges and zooms in
    *
    * @param x
    * Click on the X coordinate
    * @param y
    * Click on the Y coordinate
    */ 
  private void changeSize(float x, float y) { 
    if (isBig) { 
      // If in maximum state, restore      (); 
      (scale, scale); 
      (subX, subY); 
      isBig = false; 
    } else { 
      (bigScale, bigScale); // Multiply the magnification after the original matrix      float transX = -((bigScale - 1) * x); 
      float transY = -((bigScale - 1) * (y - topHeight)); // (bigScale-1)(y-statusBarHeight-subY)+2*subY; 
      float currentWidth = primaryW * scale * bigScale; // The image size after enlargement      float currentHeight = primaryH * scale * bigScale; 
      // If the image is enlarged and exceeded the screen range, processing      if (currentHeight > contentH) { 
        limitY1 = -(currentHeight - contentH); // Pan limit        limitY2 = 0; 
        isMoveY = true; // Allow dragging on the Y axis        float currentSubY = bigScale * subY; // Current translation distance        // After translation, there is a blank solution in the upper part of the content area        if (-transY < currentSubY) { 
          transY = -currentSubY; 
        } 
        // After translation, there is a blank solution for the lower part of the content area        if (currentSubY + transY < limitY1) { 
          transY = -(currentHeight + currentSubY - contentH); 
        } 
      } else { 
        // If the image is not processed beyond the screen range after enlarging, drag is not allowed        isMoveY = false; 
      } 
 
      if (currentWidth > contentW) { 
        limitX1 = -(currentWidth - contentW); 
        limitX2 = 0; 
        isMoveX = true; 
        float currentSubX = bigScale * subX; 
        if (-transX < currentSubX) { 
          transX = -currentSubX; 
        } 
        if (currentSubX + transX < limitX1) { 
          transX = -(currentWidth + currentSubX - contentW); 
        } 
      } else { 
        isMoveX = false; 
      } 
 
      (transX, transY); 
      isBig = true; 
    } 
 
    (imgMatrix); 
    if (mCustomMethod != null) { 
      (isBig); 
    } 
  } 
 
  /**
    * Get the X-axis offset and Y-axis offset in the transformation matrix
    *
    * @param matrix
    * Transform matrix
    * @return
    */ 
  private float[] getTranslateXY(Matrix matrix) { 
    float[] values = new float[9]; 
    (values); 
    float[] floats = new float[2]; 
    floats[0] = values[Matrix.MTRANS_X]; 
    floats[1] = values[Matrix.MTRANS_Y]; 
    return floats; 
  } 
 
  /**
    * Get the distance between two points
    *
    * @param event
    * @return
    */ 
  private float getDistance(MotionEvent event) { 
    float x = (0) - (1); 
    float y = (0) - (1); 
    return (x * x + y * y); 
  } 
 
  /**
    * @author Administrator User-defined method
    */ 
  public interface ICustomMethod { 
    public void customMethod(Boolean currentStatus); 
  } 
} 

 

ImageVewActivity This Activity for testing

import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
 
public class ImageViewActivity extends Activity { 
 
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
    // TODO Auto-generated method stub 
    (savedInstanceState); 
    setContentView(.common_image_view); 
    findView(); 
  } 
 
  public void onWindowFocusChanged(boolean hasFocus) { 
    (hasFocus); 
    init(); 
  } 
 
  ImageControl imgControl; 
  LinearLayout llTitle; 
  TextView tvTitle; 
 
  private void findView() { 
    imgControl = (ImageControl) findViewById(id.common_imageview_imageControl1); 
    llTitle = (LinearLayout) findViewById(id.common_imageview_llTitle); 
    tvTitle = (TextView) findViewById(id.common_imageview_title); 
  } 
 
  private void init() { 
    ("Picture Test"); 
    // Here you can dynamically assign values ​​to the image path of imgcontrol    // ............ 
     
    Bitmap bmp; 
    if (() != null) { 
      bmp = (()); 
    } else { 
      bmp = ((BitmapDrawable) ()).getBitmap(); 
    } 
    Rect frame = new Rect(); 
    getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); 
    int statusBarHeight = ; 
    int screenW = ().getDefaultDisplay().getWidth(); 
    int screenH = ().getDefaultDisplay().getHeight() 
        - statusBarHeight; 
    if (bmp != null) { 
      (bmp, screenW, screenH, statusBarHeight, 
          new ICustomMethod() { 
            
            @Override 
            public void customMethod(Boolean currentStatus) { 
              // When the picture is in the zoom-in or out state, control whether the title is displayed              if (currentStatus) { 
                (); 
              } else { 
                (); 
              } 
            } 
          }); 
    } 
    else 
    { 
      (, "The picture has failed to load, please try again!", Toast.LENGTH_SHORT) 
          .show(); 
    } 
 
  } 
 
  @Override 
  public boolean onTouchEvent(MotionEvent event) { 
    switch (() & MotionEvent.ACTION_MASK) { 
    case MotionEvent.ACTION_DOWN: 
      (event);       
      break; 
 
    /**
      * Non-first click
      */ 
    case MotionEvent.ACTION_POINTER_DOWN: 
     
        (event); 
     
      break; 
    case MotionEvent.ACTION_MOVE: 
        (event); 
       
      break; 
 
    case MotionEvent.ACTION_UP: 
      (); 
      break; 
 
    } 
 
    return (event); 
  } 
} 

In the above code, two points need to be paid attention to. In an Activity, you need to override the onTouchEvent method and pass the touch event to the ImageControl, which is similar to the routing event mechanism in WPF. Two initialize imgControl, that is, pay attention to the parameters in it. The last parameter is similar to the delegate in C#. I use the interface to implement it here. All the operations to be performed during the zoom-in switch are uninstalled in this method.


common_image_view.xml  Layout file

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:andro 
  android: 
  android:layout_width="fill_parent" 
  android:layout_height="fill_parent" > 
 
  < 
    android: 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:src="@drawable/ic_launcher" /> 
 
  <LinearLayout 
    android: 
    style="@style/reportTitle1" 
    android:layout_alignParentLeft="true" 
    android:layout_alignParentTop="true" > 
 
    <TextView 
      android: 
      style="@style/title2" 
      android:layout_width="fill_parent" 
      android:layout_height="wrap_content" 
      android:layout_weight="1" 
      android:text="Report" /> 
  </LinearLayout> 
 
</RelativeLayout>