2012-05-02 27 views
5

Tôi đang cố gắng tạo một ViewGroup hỗ trợ việc phóng to và thu phóng nội dung của nó. Tất cả những gì tôi có thể tìm thấy trực tuyến là ý tưởng và triển khai để thực hiện điều đó trong một ImageView, nhưng không bao giờ là một thùng chứa. Tôi muốn hiển thị một bản đồ, và trên đầu trang của nó tôi muốn hiển thị nhiều điểm đánh dấu là ImageButtons, vì vậy người dùng có thể chạm vào chúng để có thêm thông tin. Điều này đạt được trên iOS bằng phương tiện của UIScrollView, nhưng tôi không thể tìm thấy một thay thế trên Android.Xây dựng hộp chứa có thể thu phóng/có thể mở rộng

Tôi quyết định sử dụng FrameView, vì vậy tôi có thể đặt ImageView với hình ảnh làm nền và phía trên nó thêm RelativeLayout, trên đó tôi có thể thêm ImageButton và định vị chúng bằng lề.

Tôi đã mượn một phần của việc triển khai TouchImageView here, nhưng tôi đã gặp phải các biến chứng. Tôi đã bắt đầu với panning, và tôi một phần thành công, nó chảo container xung quanh, nhưng panning hoạt động hideously, nó jitters rất nhiều. Đây là mã của tôi:

public class ScrollingViewGroup extends FrameLayout { 

    private int x = 0; 
    private int y = 0; 

    // 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(); 

public ScrollingViewGroup(Context context) { 
     super(context); 
     sharedConstructing(context); 
    } 

    public ScrollingViewGroup(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     sharedConstructing(context); 
    } 

    private void sharedConstructing(Context context) { 
     super.setClickable(true); 
     this.context = context; 
     mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 

    setOnTouchListener(new OnTouchListener() { 

      @Override 
      public boolean onTouch(View v, MotionEvent event) { 
       mScaleDetector.onTouchEvent(event); 

       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; 
         Log.d("ScrollingViewGroup", Float.toString(deltaX)); 
         Log.d("ScrollingViewGroup", Float.toString(deltaY)); 
         float scaleWidth = Math.round(origWidth * saveScale); 
         float scaleHeight = Math.round(origHeight * saveScale); 


         x += deltaX; 
         y += 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); 
       setTranslationX(x); 
       setTranslationY(y); 
       invalidate(); 
       return true; // indicate event was handled 
      } 

     }); 
    } 

Mọi ý tưởng đều rất được đánh giá cao.

chỉnh sửa: jitter có vẻ là nguyên nhân bởi vì khi di chuyển, deltaX và deltaY thay thế giữa số dương và âm, kiểm tra LogCat ... vẫn không chắc chắn lý do. Điều này được gây ra bởi biến curr cung cấp các giá trị khác nhau mỗi lần, nhưng thay vì nhất quán, chúng có vẻ như ngón tay sẽ di chuyển qua lại thay vì chỉ tiến lên. Ví dụ, thay vì curr.x là 0,1,2,3,4, vv, nó là 0,1,0,5,2,1,5, vv .. Không chắc chắn lý do tại sao một trong hai.

+0

Bạn đã bao giờ tìm được giải pháp cho điều này chưa? Tôi đang tìm kiếm chính xác điều tương tự. Một bản đồ zoom/pan có thể với các nút – 0xSina

+0

Xin lỗi, tôi đã rời công ty một lúc và tôi không thể cung cấp mã, tôi đã tìm thấy một số cách giải quyết, nhưng không thể nhớ lại tôi đã làm như thế nào. –

Trả lời

0

Tôi có một giải pháp mà làm việc với một số điều kiện tiên quyết:

  • các mục trong thùng chứa cần phải được loại ImageView
  • Tất cả các hình ảnh cần phải có cùng kích thước để phóng to không kém

Các mục trong vùng chứa có thể được thu phóng và quét (nhưng không phải cùng một lúc). Mã này cũng có thể tìm ra liệu người dùng đã nhấp vào thay vì di chuyển hay thu phóng. ImageViews được lưu trữ trong một ArrayList trong FrameLayout và được thu nhỏ và di chuyển cùng nhau. Một số phần của mã này được lấy từ một bài viết rất hay trên ZDNet của Ed Burnette (Link), được lấy từ cuốn sách Android thực sự tốt "Hello, Android".

Kiểm tra mã này. Bạn có thể sử dụng lớp này làm bố cục trong bất kỳ hoạt động nào. Bạn thậm chí có thể sử dụng nó trong XML. Bây giờ có một phương thức initializeViews() được gọi trong hàm tạo, nơi bạn có thể mã hóa cứng các lần xem hình ảnh sẽ được tải khi bố cục được tạo. Bạn nên thêm một số bitmap trong phương thức này sau dòng "ArrayList sampleBitmaps = new ArrayList();" Để sử dụng thực tế, có thể tốt hơn là triển khai phương thức addImageView (mục ImageView), nơi bạn có thể thêm chế độ xem động.

import java.util.ArrayList; 

import android.content.Context; 
import android.graphics.Bitmap; 
import android.graphics.Bitmap.Config; 
import android.graphics.Matrix; 
import android.graphics.PointF; 
import android.util.AttributeSet; 
import android.util.FloatMath; 
import android.util.Log; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.View.OnTouchListener; 
import android.widget.FrameLayout; 
import android.widget.ImageView; 
import android.widget.ImageView.ScaleType; 


public class TouchContainer extends FrameLayout implements OnTouchListener { 

    // constants 
    private static final Config DEFAULT_COLOR_DEPTH = Bitmap.Config.ARGB_4444; 
    private static final String TAG = "TouchContainer"; 

    // fields 
    private ArrayList<ImageView> items; 

    public TouchContainer(Context ctx) { 
     this(ctx, null); 
    } 

    public TouchContainer(Context ctx, AttributeSet attrs) { 
     super(ctx, attrs); 

     initializeViews(); // initialize some sample Bitmaps 
    } 

    /** 
    * This method is just to make an example 
    */ 
    protected void initializeViews() { 
     ScaleType scaleType = ScaleType.MATRIX; 

     // array needs to be created here if used in XML 
     items = new ArrayList<ImageView>(); 


     ArrayList<Bitmap> sampleBitmaps = new ArrayList<Bitmap>(); 
     // here you should add some bitmaps to the Array that will then be displayed in the container 
     // e.g. sampleBitmaps.add(blabla I'm a bitmap) :-) 

     ImageView iv = null; 
     boolean firstLoop = true; 

     for (Bitmap bitmap : sampleBitmaps) { 
      // Load the bitmaps into imageviews 
      iv = new ImageView(getContext()); 
      iv.setImageBitmap(bitmap); 
      iv.setScaleType(scaleType); 
      if (firstLoop) { 
       // add the touch listener to the first image view that is stored in the ArrayList 
       iv.setOnTouchListener(this); 
       firstLoop = false; 
      } 

      // add view to the FrameLayout 
      this.addView(iv); 

      // add the imageview to the array 
      items.add(iv); 
     } 

    } 



    protected void transformImages(Matrix matrix) { 
     for (ImageView image : items) { 
      image.setImageMatrix(matrix); 
     } 

    } 




    Matrix matrix = new Matrix(); 
    Matrix savedMatrix = new Matrix(); 

    // states 
    static final int NONE = 0; 
    static final int DRAG = 1; 
    static final int ZOOM = 2; 
    int mode = NONE; 
    static final int CLICK = 3; 


    PointF start = new PointF(); 
    PointF mid = new PointF(); 
    float oldDist = 1f; 

    public boolean onTouch(View v, MotionEvent event) { 


     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: 
      // figure out if user clicked 
      mode = NONE; 
      int xDiff = (int) Math.abs(event.getX() - start.x); 
      int yDiff = (int) Math.abs(event.getY() - start.y); 
      if (xDiff < CLICK && yDiff < CLICK) 
       performClick(); 
      break; 
     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; 
     } 


     transformImages(matrix); 


     return true; 
    } 




     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); 
     } 





} 
Các vấn đề liên quan