Làm cách nào để có được chức năng thu phóng cho hình ảnh?


204

Có một cách phổ biến để hiển thị một hình ảnh lớn và cho phép người dùng phóng to và thu nhỏ và xoay hình ảnh?

Cho đến bây giờ tôi đã tìm thấy hai cách:

  1. ghi đè ImageView, có vẻ hơi quá đối với một vấn đề phổ biến như vậy.
  2. sử dụng webview nhưng ít kiểm soát bố cục tổng thể, v.v.

Có KIỂM SOÁT ZOOM (Widget) và bạn có thể nghe sự kiện OnTouch để xử lý panning.
tobrien

1
Một câu hỏi tương tự stackoverflow.com/questions/2537394/ , có một liên kết đến hướng dẫn này anddev.org/iêu . Bạn có thể thấy rằng hữu ích để pan iamge của bạn. Tôi chưa đọc chi tiết, nhưng nó cũng có thể cung cấp cho bạn một số ý tưởng về cách thực hiện chức năng thu phóng.
Steve Haley

Có ai đã cố gắng lưu hình ảnh khi phóng to? Tôi muốn hình ảnh được lưu ở trạng thái mặc định thay vì trạng thái phóng to. Xin vui lòng xem câu hỏi của tôi: stackoverflow.com/questions/24730793/ Cảm ơn
Blaze Tama

Câu trả lời:


208

CẬP NHẬT

Tôi vừa cung cấp cho TouchImageView một bản cập nhật mới. Giờ đây nó bao gồm Double Tap Zoom và Fling ngoài Panning và Pinch Zoom. Mã dưới đây là rất ngày. Bạn có thể kiểm tra dự án github để lấy mã mới nhất.

SỬ DỤNG

Đặt TouchImageView.java trong dự án của bạn. Sau đó, nó có thể được sử dụng giống như ImageView. Thí dụ:

TouchImageView img = (TouchImageView) findViewById(R.id.img);

Nếu bạn đang sử dụng TouchImageView trong xml, thì bạn phải cung cấp tên gói đầy đủ, vì đây là chế độ xem tùy chỉnh. Thí dụ:

<com.example.touch.TouchImageView
    android:id="@+id/img”
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Lưu ý: Tôi đã xóa câu trả lời trước của mình, bao gồm một số mã rất cũ và hiện liên kết thẳng đến mã được cập nhật nhất trên github.

ViewPager

Nếu bạn quan tâm đến việc đưa TouchImageView vào ViewPager, hãy tham khảo câu trả lời này.


4
Paulo, tôi đã không gặp vấn đề về hiệu suất, nhưng tôi đã không được thử nghiệm trên máy tính bảng. Bởi chậm, bạn có nghĩa là chậm trễ? Tôi đặt hệ số thu phóng tối đa là 1,05 khi bắt đầu onScale. Đây có phải là những gì bạn đang nói về? Nếu không, hãy thử các cách sau: 1. Bạn có ở chế độ gỡ lỗi không? Điều này sẽ làm chậm nó đáng kể. 2. Kích thước hình ảnh bạn đang thiết lập. Tôi đã không kiểm tra với hình ảnh rất lớn (8mp), nhưng điều này có thể làm chậm nó. 3. Bạn có điện thoại nào bạn có thể thử nghiệm không? 4. Nếu vẫn thất bại, hãy xem việc nhân mScaleFactor với 2 (nếu> 1) hoặc 0,5 (nếu <1) sẽ giúp ích cho tình huống của bạn.
Mike Ortiz

3
@Ahsan Thay đổi hàm tạo View thành: TouchImageView(Context context, AttributeSet attrs)và gọi super(context, attrs);Điều này là do khi bạn tăng chế độ xem tùy chỉnh, nó được xây dựng với hai tham số, thay vì chỉ một. Khi tôi tiếp cận nó, tôi sẽ sửa TouchImageView để hỗ trợ ba hàm tạo và khung vẽ.
Mike Ortiz

2
@Ahsan Vì là chế độ xem tùy chỉnh, bạn cần phải viết ra toàn bộ tên trong tệp XML, tức là <com.example.TouchImageView android:id="@+id/img" />. Bạn đã làm điểu đó?
Mike Ortiz

1
Đây là công cụ tuyệt vời, đã được tìm kiếm này từ lâu. Sử dụng mã từ github vì nó gần đây hơn và chỉ hoạt động tốt hơn
Alex

2
@Mike tôi đã thử mã này nhưng thư viện tùy chỉnh không hoạt động. Có bất kỳ thủ thuật làm việc xung quanh vấn đề này.
Umesh

80

Tôi đã điều chỉnh một số mã để tạo TouchImageView hỗ trợ cảm ứng đa điểm (> 2.1). Nó được lấy cảm hứng từ cuốn sách Hello, Android! (Ấn bản thứ 3)

Nó được chứa trong 3 tệp sau TouchImageView.java WrapMotionEvent.java EclairMotionEvent.java

TouchImageView.java

import se.robertfoss.ChanImageBrowser.Viewer;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;

public class TouchImageView extends ImageView {

    private static final String TAG = "Touch";
    // These matrices will be used to move and zoom image
    Matrix matrix = new Matrix();
    Matrix savedMatrix = new Matrix();

    // We can be in one of these 3 states
    static final int NONE = 0;
    static final int DRAG = 1;
    static final int ZOOM = 2;
    int mode = NONE;

    // Remember some things for zooming
    PointF start = new PointF();
    PointF mid = new PointF();
    float oldDist = 1f;

    Context context;


    public TouchImageView(Context context) {
        super(context);
        super.setClickable(true);
        this.context = context;

        matrix.setTranslate(1f, 1f);
        setImageMatrix(matrix);
        setScaleType(ScaleType.MATRIX);

        setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent rawEvent) {
                WrapMotionEvent event = WrapMotionEvent.wrap(rawEvent);

                // Dump touch event to log
                if (Viewer.isDebug == true){
                    dumpEvent(event);
                }

                // Handle touch events here...
                switch (event.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    savedMatrix.set(matrix);
                    start.set(event.getX(), event.getY());
                    Log.d(TAG, "mode=DRAG");
                    mode = DRAG;
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    oldDist = spacing(event);
                    Log.d(TAG, "oldDist=" + oldDist);
                    if (oldDist > 10f) {
                        savedMatrix.set(matrix);
                        midPoint(mid, event);
                        mode = ZOOM;
                        Log.d(TAG, "mode=ZOOM");
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    int xDiff = (int) Math.abs(event.getX() - start.x);
                    int yDiff = (int) Math.abs(event.getY() - start.y);
                    if (xDiff < 8 && yDiff < 8){
                        performClick();
                    }
                case MotionEvent.ACTION_POINTER_UP:
                    mode = NONE;
                    Log.d(TAG, "mode=NONE");
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (mode == DRAG) {
                        // ...
                        matrix.set(savedMatrix);
                        matrix.postTranslate(event.getX() - start.x, event.getY() - start.y);
                    } else if (mode == ZOOM) {
                        float newDist = spacing(event);
                        Log.d(TAG, "newDist=" + newDist);
                        if (newDist > 10f) {
                            matrix.set(savedMatrix);
                            float scale = newDist / oldDist;
                            matrix.postScale(scale, scale, mid.x, mid.y);
                        }
                    }
                    break;
                }

                setImageMatrix(matrix);
                return true; // indicate event was handled
            }

        });
    }


    public void setImage(Bitmap bm, int displayWidth, int displayHeight) { 
        super.setImageBitmap(bm);

        //Fit to screen.
        float scale;
        if ((displayHeight / bm.getHeight()) >= (displayWidth / bm.getWidth())){
            scale =  (float)displayWidth / (float)bm.getWidth();
        } else {
            scale = (float)displayHeight / (float)bm.getHeight();
        }

        savedMatrix.set(matrix);
        matrix.set(savedMatrix);
        matrix.postScale(scale, scale, mid.x, mid.y);
        setImageMatrix(matrix);


        // Center the image
        float redundantYSpace = (float)displayHeight - (scale * (float)bm.getHeight()) ;
        float redundantXSpace = (float)displayWidth - (scale * (float)bm.getWidth());

        redundantYSpace /= (float)2;
        redundantXSpace /= (float)2;


        savedMatrix.set(matrix);
        matrix.set(savedMatrix);
        matrix.postTranslate(redundantXSpace, redundantYSpace);
        setImageMatrix(matrix);
    }


    /** Show an event in the LogCat view, for debugging */
    private void dumpEvent(WrapMotionEvent event) {
        // ...
        String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",
            "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
        StringBuilder sb = new StringBuilder();
        int action = event.getAction();
        int actionCode = action & MotionEvent.ACTION_MASK;
        sb.append("event ACTION_").append(names[actionCode]);
        if (actionCode == MotionEvent.ACTION_POINTER_DOWN
                || actionCode == MotionEvent.ACTION_POINTER_UP) {
            sb.append("(pid ").append(
                    action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
            sb.append(")");
        }
        sb.append("[");
        for (int i = 0; i < event.getPointerCount(); i++) {
            sb.append("#").append(i);
            sb.append("(pid ").append(event.getPointerId(i));
            sb.append(")=").append((int) event.getX(i));
            sb.append(",").append((int) event.getY(i));
            if (i + 1 < event.getPointerCount())
            sb.append(";");
        }
        sb.append("]");
        Log.d(TAG, sb.toString());
    }

    /** Determine the space between the first two fingers */
    private float spacing(WrapMotionEvent event) {
        // ...
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return FloatMath.sqrt(x * x + y * y);
    }

    /** Calculate the mid point of the first two fingers */
    private void midPoint(PointF point, WrapMotionEvent event) {
        // ...
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }
}

WrapMotionEvent.java

import android.view.MotionEvent;

public class WrapMotionEvent {
protected MotionEvent event;




    protected WrapMotionEvent(MotionEvent event) {
        this.event = event;
    }

    static public WrapMotionEvent wrap(MotionEvent event) {
            try {
                return new EclairMotionEvent(event);
            } catch (VerifyError e) {
                return new WrapMotionEvent(event);
            }
    }



    public int getAction() {
            return event.getAction();
    }

    public float getX() {
            return event.getX();
    }

    public float getX(int pointerIndex) {
            verifyPointerIndex(pointerIndex);
            return getX();
    }

    public float getY() {
            return event.getY();
    }

    public float getY(int pointerIndex) {
            verifyPointerIndex(pointerIndex);
            return getY();
    }

    public int getPointerCount() {
            return 1;
    }

    public int getPointerId(int pointerIndex) {
            verifyPointerIndex(pointerIndex);
            return 0;
    }

    private void verifyPointerIndex(int pointerIndex) {
            if (pointerIndex > 0) {
                throw new IllegalArgumentException(
                    "Invalid pointer index for Donut/Cupcake");
            }
    }

}

EclairMotionEvent.java

import android.view.MotionEvent;

public class EclairMotionEvent extends WrapMotionEvent {

    protected EclairMotionEvent(MotionEvent event) {
            super(event);
    }

    public float getX(int pointerIndex) {
            return event.getX(pointerIndex);
    }

    public float getY(int pointerIndex) {
            return event.getY(pointerIndex);
    }

    public int getPointerCount() {
            return event.getPointerCount();
    }

    public int getPointerId(int pointerIndex) {
            return event.getPointerId(pointerIndex);
    }
}

Robert Foss, nếu điều này thêm thẩm phán ranh giới, nó có thể giảm nhiều hơn. Cảm ơn mã của bạn rất tốt
pengwang

3
Nó hoạt động, nhưng tôi không thấy điểm trong WrapMotionEventEclairMotionEvent... dù sao, +1.
Cipi

2
Multitouch cho điện thoại hỗ trợ nó. Một liên lạc thường xuyên cho Android <2.0
Robert Foss

Ví dụ hay, nó hoạt động tốt nhưng tôi không hiểu Trình xem trong if (Viewer.isDebug == true) {dumpEvent (event); }
Tofeeq Ahmad

2
Cái này là cái gì? >> se.robertfoss.ChanImageBrowser.Viewer
emeraldhieu

60

Tôi đã sử dụng WebView và tải hình ảnh từ bộ nhớ thông qua

webview.loadUrl("file://...")

WebView xử lý tất cả các phóng to thu nhỏ và cuộn. Nếu bạn sử dụng quấn_content thì webview sẽ không lớn hơn thì hình ảnh và không có vùng trắng nào được hiển thị. WebView là ImageView tốt hơn;)


5
Tôi đang sử dụng cùng một phương pháp. Tôi có một bản đồ tàu điện ngầm lớn mà tôi muốn người dùng có thể phóng to và cuộn xung quanh. Mặc dù vậy, tôi nhận thấy rằng nếu bạn có một hình ảnh khá lớn (tức là rộng 1000 hoặc 3000 pixel), hình ảnh sẽ bị mờ khi bạn phóng to. Có vẻ như coliris không thể hiển thị hình ảnh được phóng to rất lớn. Mặc dù hình ảnh ban đầu là không nén và rất sắc nét. Do đó, cuối cùng tôi đã cắt một hình ảnh lớn thành các lát nhỏ hơn và đặt chúng lại với nhau thông qua HTML. Bằng cách này, hình ảnh vẫn sắc nét khi phóng to. (Tôi đang sử dụng Nexus One, 2.1update trước và bây giờ vào ngày 2.2)
Mathias Conradt

@Mathias Lin: nếu một hình ảnh lớn được gửi qua dây, tôi đã nghe thấy các nhà mạng nén hình ảnh lớn. trường hợp sử dụng này phù hợp với bạn hoặc bạn đã tải hình ảnh cục bộ.
Samuel

@Sam Quest: tải nó tại địa phương
Mathias Conradt

4
tốt hơn nhiều để sử dụng các nút thu phóng tích hợp của webview và hỗ trợ cho pinch để phóng to / thu nhỏ hơn là viết một thuật toán hoàn toàn mới có thể không hoạt động trên các điện thoại khác nhau và các bản phát hành nền tảng Android trong tương lai
sami

2
giải pháp này chỉ có thể được áp dụng nếu bạn có hình ảnh ngồi trên đĩa hoặc hình ảnh đủ nhỏ để bạn có thể mã hóa 64 và truyền giá trị chuỗi vào loadUrlWithData ().
Jeffrey Blattman

7

Trả lời câu hỏi ban đầu của Janusz, có một số cách để đạt được điều này, tất cả đều khác nhau về mức độ khó của chúng và đã được nêu dưới đây. Sử dụng một chế độ xem web là tốt, nhưng nó rất hạn chế về giao diện và khả năng kiểm soát. Nếu bạn đang vẽ một bitmap từ một khung vẽ, các giải pháp linh hoạt nhất đã được đề xuất dường như là của MikeOrtiz, Robert Foss và / hoặc những gì Jacob Nordfalk đề xuất. Có một ví dụ tuyệt vời cho việc kết hợp bộ điều khiển đa cảm ứng android của PaulBourke , và thật tuyệt vời khi có sự hỗ trợ đa chạm và các kiểu xem tùy chỉnh.

Cá nhân, nếu bạn chỉ đơn giản là vẽ một bức tranh thành bitmap và sau đó hiển thị nó bên trong và ImageView và muốn có thể phóng to và di chuyển xung quanh bằng cách sử dụng đa chạm, tôi thấy giải pháp của MikeOrtiz là dễ nhất. Tuy nhiên, với mục đích của tôi, mã từ Git mà anh ta đã cung cấp dường như chỉ hoạt động khi lớp ImageView tùy chỉnh TouchImageView của anh ta là con duy nhất hoặc cung cấp các tham số bố cục như:

android:layout_height="match_parent"
android:layout_height="match_parent"

Thật không may do thiết kế bố trí của tôi, tôi cần "quấn_content" cho "layout_height". Khi tôi thay đổi nó thành hình ảnh này đã bị cắt ở phía dưới và tôi không thể cuộn hoặc thu phóng đến vùng bị cắt. Vì vậy, tôi đã xem Nguồn cho ImageView để xem Android triển khai "onMeasure" như thế nào và thay đổi MikeOrtiz cho phù hợp.

   @Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
{
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

  //**** ADDED THIS ********/////
      int  w = (int) bmWidth;
      int  h = (int) bmHeight;
     width = resolveSize(w, widthMeasureSpec);  
     height = resolveSize(h, heightMeasureSpec);
  //**** END ********///   

   // width = MeasureSpec.getSize(widthMeasureSpec);   // REMOVED
   // height = MeasureSpec.getSize(heightMeasureSpec); // REMOVED

    //Fit to screen.
    float scale;
    float scaleX =  (float)width / (float)bmWidth;
    float scaleY = (float)height / (float)bmHeight;

    scale = Math.min(scaleX, scaleY);
    matrix.setScale(scale, scale);
    setImageMatrix(matrix);
    saveScale = 1f;

    // Center the image
    redundantYSpace = (float)height - (scale * (float)bmHeight) ;
    redundantXSpace = (float)width - (scale * (float)bmWidth);
    redundantYSpace /= (float)2;
    redundantXSpace /= (float)2;

    matrix.postTranslate(redundantXSpace, redundantYSpace);

    origWidth = width - 2 * redundantXSpace;
    origHeight = height - 2 * redundantYSpace;
   // origHeight = bmHeight;
    right = width * saveScale - width - (2 * redundantXSpace * saveScale);
    bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);

    setImageMatrix(matrix);
}

Ở đây

Thông số:

 - size How big the view wants to be
 - MeasureSpec Constraints imposed by the parent

Trả về:

 - The size this view should be."

Vì vậy, về cơ bản cung cấp một hành vi giống với lớp ImageView ban đầu hơn khi hình ảnh được tải. Một số thay đổi khác có thể được thực hiện để hỗ trợ nhiều loại màn hình khác nhau điều chỉnh tỷ lệ khung hình. Nhưng bây giờ tôi hy vọng điều này sẽ giúp. Cảm ơn MikeOrtiz cho mã gốc của mình, công việc tuyệt vời.


Đã sửa lỗi này được tích hợp vào repo github của Mike?
LarsH


6

Tôi vừa tích hợp TouchImageView của Robert Foss: nó hoạt động hoàn hảo. Cảm ơn!

Tôi vừa sửa đổi một chút mã để tôi có thể khởi tạo nó từ layout.xml của mình.

Chỉ cần thêm hai hàm tạo

public TouchImageView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
}

public TouchImageView(Context context) {
    super(context);
    init(context);
}

và chuyển đổi hàm tạo cũ thành một phương thức init:

private void init(Context context){
    //...old code ofconstructor of Robert Moss's code
}

3

@Robert Foss, @Mike Ortiz, cảm ơn bạn rất nhiều vì công việc của bạn. Tôi đã hợp nhất công việc của bạn và hoàn thành các lớp Robert cho android> 2.0 với công việc bổ sung của Mike.

Là kết quả của công việc của tôi, tôi trình bày Android Touch Gallery, dựa trên ViewPager và đã sử dụng TouchImageView được sửa đổi. Hình ảnh tải theo URL và bạn có thể phóng to và kéo chúng. Bạn có thể tìm thấy nó ở đây https://github.com/Dreddik/AndroidTouchGallery



2

Thêm vào câu trả lời của @ Mike. Tôi cũng cần chạm hai lần để khôi phục hình ảnh về kích thước ban đầu khi xem lần đầu tiên. Vì vậy, tôi đã thêm cả đống biến thể "orig ..." và thêm SimpleOnGestureListener đã thực hiện thủ thuật này.

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;

public class TouchImageView extends ImageView {

    Matrix matrix = new Matrix();

    // We can be in one of these 3 states
    static final int NONE = 0;
    static final int DRAG = 1;
    static final int ZOOM = 2;
    int mode = NONE;

    // Remember some things for zooming
    PointF last = new PointF();
    PointF start = new PointF();
    float minScale = 1f;
    float maxScale = 3f;
    float[] m;

    float redundantXSpace, redundantYSpace, origRedundantXSpace, origRedundantYSpace;;

    float width, height;
    static final int CLICK = 3;
    static final float SAVE_SCALE = 1f;
    float saveScale = SAVE_SCALE;

    float right, bottom, origWidth, origHeight, bmWidth, bmHeight, origScale, origBottom,origRight;

    ScaleGestureDetector mScaleDetector;
    GestureDetector mGestureDetector;

    Context context;

    public TouchImageView(Context context) {
        super(context);
        super.setClickable(true);
        this.context = context;
        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());

        matrix.setTranslate(1f, 1f);
        m = new float[9];
        setImageMatrix(matrix);
        setScaleType(ScaleType.MATRIX);

        setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {

                boolean onDoubleTapEvent = mGestureDetector.onTouchEvent(event);
                if (onDoubleTapEvent) {
                    // Reset Image to original scale values
                    mode = NONE;
                    bottom = origBottom;
                    right = origRight;
                    last = new PointF();
                    start = new PointF();
                    m = new float[9];
                    saveScale = SAVE_SCALE;
                    matrix = new Matrix();
                    matrix.setScale(origScale, origScale);
                    matrix.postTranslate(origRedundantXSpace, origRedundantYSpace);
                    setImageMatrix(matrix);
                    invalidate();
                    return true;
                } 


                mScaleDetector.onTouchEvent(event);

                matrix.getValues(m);
                float x = m[Matrix.MTRANS_X];
                float y = m[Matrix.MTRANS_Y];
                PointF curr = new PointF(event.getX(), event.getY());

                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    last.set(event.getX(), event.getY());
                    start.set(last);
                    mode = DRAG;
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (mode == DRAG) {
                        float deltaX = curr.x - last.x;
                        float deltaY = curr.y - last.y;
                        float scaleWidth = Math.round(origWidth * saveScale);
                        float scaleHeight = Math.round(origHeight * saveScale);
                        if (scaleWidth < width) {
                            deltaX = 0;
                            if (y + deltaY > 0)
                                deltaY = -y;
                            else if (y + deltaY < -bottom)
                                deltaY = -(y + bottom);
                        } else if (scaleHeight < height) {
                            deltaY = 0;
                            if (x + deltaX > 0)
                                deltaX = -x;
                            else if (x + deltaX < -right)
                                deltaX = -(x + 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);
                        }
                        matrix.postTranslate(deltaX, deltaY);
                        last.set(curr.x, curr.y);
                    }
                    break;

                case MotionEvent.ACTION_UP:
                    mode = NONE;
                    int xDiff = (int) Math.abs(curr.x - start.x);
                    int yDiff = (int) Math.abs(curr.y - start.y);
                    if (xDiff < CLICK && yDiff < CLICK)
                        performClick();
                    break;

                case MotionEvent.ACTION_POINTER_UP:
                    mode = NONE;
                    break;
                }

                setImageMatrix(matrix);
                invalidate();

                return true; // indicate event was handled
            }

        });

        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onDoubleTapEvent(MotionEvent e) {
                return true;
            }
        });
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        bmWidth = bm.getWidth();
        bmHeight = bm.getHeight();
    }

    public void setMaxZoom(float x) {
        maxScale = x;
    }

    private class ScaleListener extends
            ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            mode = ZOOM;
            return true;
        }

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float mScaleFactor = (float) Math.min(
                    Math.max(.95f, detector.getScaleFactor()), 1.05);
            float origScale = saveScale;
            saveScale *= mScaleFactor;
            if (saveScale > maxScale) {
                saveScale = maxScale;
                mScaleFactor = maxScale / origScale;
            } else if (saveScale < minScale) {
                saveScale = minScale;
                mScaleFactor = minScale / origScale;
            }
            right = width * saveScale - width
                    - (2 * redundantXSpace * saveScale);
            bottom = height * saveScale - height
                    - (2 * redundantYSpace * saveScale);
            if (origWidth * saveScale <= width
                    || origHeight * saveScale <= height) {
                matrix.postScale(mScaleFactor, mScaleFactor, width / 2,
                        height / 2);
                if (mScaleFactor < 1) {
                    matrix.getValues(m);
                    float x = m[Matrix.MTRANS_X];
                    float y = m[Matrix.MTRANS_Y];
                    if (mScaleFactor < 1) {
                        if (Math.round(origWidth * saveScale) < width) {
                            if (y < -bottom)
                                matrix.postTranslate(0, -(y + bottom));
                            else if (y > 0)
                                matrix.postTranslate(0, -y);
                        } else {
                            if (x < -right)
                                matrix.postTranslate(-(x + right), 0);
                            else if (x > 0)
                                matrix.postTranslate(-x, 0);
                        }
                    }
                }
            } else {
                matrix.postScale(mScaleFactor, mScaleFactor,
                        detector.getFocusX(), detector.getFocusY());
                matrix.getValues(m);
                float x = m[Matrix.MTRANS_X];
                float y = m[Matrix.MTRANS_Y];
                if (mScaleFactor < 1) {
                    if (x < -right)
                        matrix.postTranslate(-(x + right), 0);
                    else if (x > 0)
                        matrix.postTranslate(-x, 0);
                    if (y < -bottom)
                        matrix.postTranslate(0, -(y + bottom));
                    else if (y > 0)
                        matrix.postTranslate(0, -y);
                }
            }
            return true;

        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = MeasureSpec.getSize(widthMeasureSpec);
        height = MeasureSpec.getSize(heightMeasureSpec);
        // Fit to screen.
        float scale;
        float scaleX = (float) width / (float) bmWidth;
        float scaleY = (float) height / (float) bmHeight;
        scale = Math.min(scaleX, scaleY);
        matrix.setScale(scale, scale);
        setImageMatrix(matrix);
        saveScale = SAVE_SCALE;
        origScale = scale;

        // Center the image
        redundantYSpace = (float) height - (scale * (float) bmHeight);
        redundantXSpace = (float) width - (scale * (float) bmWidth);
        redundantYSpace /= (float) 2;
        redundantXSpace /= (float) 2;

        origRedundantXSpace = redundantXSpace;
        origRedundantYSpace = redundantYSpace;

        matrix.postTranslate(redundantXSpace, redundantYSpace);

        origWidth = width - 2 * redundantXSpace;
        origHeight = height - 2 * redundantYSpace;
        right = width * saveScale - width - (2 * redundantXSpace * saveScale);
        bottom = height * saveScale - height
                - (2 * redundantYSpace * saveScale);
        origRight = right;
        origBottom = bottom;
        setImageMatrix(matrix);
    }

}

2

Đây là một bổ sung rất muộn cho chủ đề này nhưng tôi đã làm việc trên chế độ xem hình ảnh hỗ trợ thu phóng và xoay và có một số tính năng tôi chưa tìm thấy ở nơi khác. Điều này bắt đầu như một cách hiển thị hình ảnh rất lớn mà không gây ra OutOfMemoryErrors, bằng cách lấy mẫu hình ảnh khi thu nhỏ và tải các ô có độ phân giải cao hơn khi phóng to. Hiện tại nó hỗ trợ sử dụng trong ViewPager, xoay thủ công hoặc sử dụng thông tin EXIF ​​(dừng 90 °), ghi đè các sự kiện chạm được chọn bằng cách sử dụng OnClickListenerhoặc của riêng bạn GestureDetectorhoặc OnTouchListener, phân lớp để thêm lớp phủ, xoay trong khi thu phóng và động lượng.

Nó không nhằm mục đích thay thế sử dụng chung ImageViewvì vậy không mở rộng nó và không hỗ trợ hiển thị hình ảnh từ các tài nguyên, chỉ các tài sản và các tệp bên ngoài. Nó yêu cầu SDK 10.

Nguồn trên GitHub và có một mẫu minh họa việc sử dụng trong a ViewPager.

https://github.com/davemorrissey/subampling-scale-image-view


1

Bạn có thể thử sử dụng LayoutParams cho việc này

public void zoom(boolean flag){
    if(flag){
        int width=40;
        int height=40;
    }
    else{
        int width=20;
        int height=20;
    }
    RelativeLayout.LayoutParams param=new RelativeLayout.LayoutParams(width,height); //use the parent layout of the ImageView;
    imageView.setLayoutParams(param); //imageView is the view which needs zooming.
}

ZoomIn = zoom (đúng); ZoomOut = zoom (sai);


0
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    imageDetail = (ImageView) findViewById(R.id.imageView1);
    imageDetail.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            ImageView view = (ImageView) v;
            System.out.println("matrix=" + savedMatrix.toString());
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    savedMatrix.set(matrix);
                    startPoint.set(event.getX(), event.getY());
                    mode = DRAG;
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    oldDist = spacing(event);
                    if (oldDist > 10f) {
                        savedMatrix.set(matrix);
                        midPoint(midPoint, event);
                        mode = ZOOM;
                    }
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_POINTER_UP:
                    mode = NONE;
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (mode == DRAG) {
                        matrix.set(savedMatrix);
                        matrix.postTranslate(event.getX() - startPoint.x, event.getY() - startPoint.y);
                    } else if (mode == ZOOM) {
                        float newDist = spacing(event);
                        if (newDist > 10f) {
                            matrix.set(savedMatrix);
                            float scale = newDist / oldDist;
                            matrix.postScale(scale, scale, midPoint.x, midPoint.y);
                        }
                    }
                    break;
            }
            view.setImageMatrix(matrix);
            return true;

        }

        @SuppressLint("FloatMath")
        private float spacing(MotionEvent event) {
            float x = event.getX(0) - event.getX(1);
            float y = event.getY(0) - event.getY(1);
            return FloatMath.sqrt(x * x + y * y);
        }

        private void midPoint(PointF point, MotionEvent event) {
            float x = event.getX(0) + event.getX(1);
            float y = event.getY(0) + event.getY(1);
            point.set(x / 2, y / 2);
        }
    });
}

và thư mục drawable nên có tập tin hình ảnh bticn. hoạt động hoàn hảo :)


0

Một cái gì đó như dưới đây sẽ làm điều đó.

@Override public boolean onTouch(View v,MotionEvent e)
{

    tap=tap2=drag=pinch=none;
    int mask=e.getActionMasked();
    posx=e.getX();posy=e.getY();

    float midx= img.getWidth()/2f;
    float midy=img.getHeight()/2f;
    int fingers=e.getPointerCount();

    switch(mask)
    {
        case MotionEvent.ACTION_POINTER_UP:
            tap2=1;break;

        case MotionEvent.ACTION_UP:
            tap=1;break;

        case MotionEvent.ACTION_MOVE:
            drag=1;
    }
    if(fingers==2){nowsp=Math.abs(e.getX(0)-e.getX(1));}
    if((fingers==2)&&(drag==0)){ tap2=1;tap=0;drag=0;}
    if((fingers==2)&&(drag==1)){ tap2=0;tap=0;drag=0;pinch=1;}

    if(pinch==1)

    {
        if(nowsp>oldsp)scale+=0.1;
        if(nowsp<oldsp)scale-=0.1;
        tap2=tap=drag=0;    
    }
    if(tap2==1)
        {
            scale-=0.1;
            tap=0;drag=0;
        }
    if(tap==1)
        {
            tap2=0;drag=0;
            scale+=0.1;
        }
    if(drag==1)
        {
            movx=posx-oldx;
            movy=posy-oldy;
            x+=movx;
            y+=movy;
            tap=0;tap2=0;
        }
    m.setTranslate(x,y);
    m.postScale(scale,scale,midx,midy);
    img.setImageMatrix(m);img.invalidate();
    tap=tap2=drag=none;
    oldx=posx;oldy=posy;
    oldsp=nowsp;
    return true;
}


public void onCreate(Bundle b)
{
        super.onCreate(b);

    img=new ImageView(this);
    img.setScaleType(ImageView.ScaleType.MATRIX);
    img.setOnTouchListener(this);

    path=Environment.getExternalStorageDirectory().getPath();   
    path=path+"/DCIM"+"/behala.jpg";
    byte[] bytes;
    bytes=null;
    try{
        FileInputStream fis;
        fis=new FileInputStream(path);
        BufferedInputStream bis;
        bis=new BufferedInputStream(fis);
        bytes=new byte[bis.available()];
        bis.read(bytes);
        if(bis!=null)bis.close();
        if(fis!=null)fis.close();

     }
    catch(Exception e)
        {
        ret="Nothing";
        }
    Bitmap bmp=BitmapFactory.decodeByteArray(bytes,0,bytes.length);

    img.setImageBitmap(bmp);

    setContentView(img);
}

Để xem chương trình hoàn chỉnh, xem tại đây: Chương trình để phóng to hình ảnh trong Android

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.