2012-05-02 16 views
5

Sto cercando di fare un ViewGroup che supporta la panoramica e lo zoom dei suoi contenuti. Tutto quello che potevo trovare online erano idee e implementazioni per farlo in un ImageView, ma mai un contenitore. Voglio visualizzare una mappa e in cima a essa voglio visualizzare più marker che sono ImageButtons, quindi l'utente può toccarli per ottenere maggiori informazioni. Questo è realizzato su iOS tramite UIScrollView, ma non sono riuscito a trovare un'alternativa su Android.Costruire un/recipiente-teglia in grado di zoom-in grado

Ho deciso di utilizzare un FrameView, così ho potuto impostare un ImageView con l'immagine come sfondo, e sopra di esso aggiungere un RelativeLayout, sul quale potrei aggiungere i ImageButtons e posizionarli usando i margini.

Ho preso in prestito parte dell'implementazione di TouchImageView here, ma mi sono imbattuto in complicazioni. Ho iniziato con il panning, e in parte ho avuto successo, fa girare il contenitore, ma il panning funziona in modo orribile, fa tremare molto. Ecco il mio codice:

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 
      } 

     }); 
    } 

Tutte le idee sono molto apprezzate.

edit: jitter sembra essere causa, perché quando si spostano, deltaX e DeltaY alternano tra numeri positivi e negativi, la verifica LogCat ... ancora non so perché. Questo è causato dalla variabile curr che dà valori diversi ogni volta, ma invece di essere coerenti, sembrano come se il dito si muovesse avanti e indietro invece che solo in avanti. Ad esempio, invece che curr.x è 0,1,2,3,4, ecc., È 0,1,0,5,2,1,5, ecc. Non so perché.

+0

Avete mai trovato una soluzione a questo? Sto cercando la stessa identica cosa. Una mappa zoom/pan in grado con i pulsanti – 0xSina

+0

Ehi, scusa, ho lasciato la compagnia qualche tempo fa e non sono in grado di fornire il codice, ho trovato qualche soluzione, ma non riesco a ricordare come l'ho fatto. –

risposta

0

Ho una soluzione che funziona con alcuni prerequisiti:

  • gli elementi nel contenitore devono essere di tipo ImageView
  • Tutte le immagini devono essere delle stesse dimensioni in modo da ingrandire altrettanto

Gli elementi nel contenitore possono essere ingrandite e panning (ma non allo stesso tempo). Il codice è anche in grado di capire se un utente ha fatto clic anziché spostare o zoomare. Le ImageViews sono memorizzate in un ArrayList in un FrameLayout e vengono ridimensionate e spostate insieme. Alcune parti di questo codice sono tratti da un articolo molto bello su ZDNet da Ed Burnette (Link), che è tratto dal libro veramente buono Android "Hello, Android".

Dai un'occhiata a questo codice. Puoi usare questa classe come layout in qualsiasi attività. Dovresti essere in grado di usarlo nell'XML. Per ora esiste un metodo initializeViews() che viene chiamato nel costruttore dove è possibile codificare in modo rigido le immagini visualizzate che devono essere caricate quando viene creato il layout. Dovresti aggiungere alcuni bitmap in questo metodo dopo la riga "ArrayList sampleBitmaps = new ArrayList();" Per l'uso reale è probabilmente meglio implementare un metodo addImageView (elemento ImageView) in cui è possibile aggiungere viste in modo dinamico.

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





}