Construcción de un contenedor capaz de hacer zoom
Estoy tratando de hacer un ViewGroup que soporta panorámica y zoom de su contenido. Todo lo que pude encontrar en línea fue ideas e implementaciones para hacerlo en un ImageView, pero nunca un contenedor. Quiero mostrar un mapa, y encima de él quiero mostrar múltiples marcadores que son ImageButtons, por lo que el usuario puede tocarlos para obtener más información. Esto se logra en iOS mediante el UIScrollView, pero no pude encontrar una alternativa en Android.
Decidí utilizar un FrameView, así que podría fijar un ImageView con la imagen como fondo, y encima de él agrego un RelativeLayout, en el cual podría agregar los ImageButtons y colocarlos usando márgenes.
- Abrir ImageView con Zoom y desplazamiento
- En evento de zoom para google maps en android
- Android GridView pinch zoom
- Zoom en los sitios web en android sdk emulator
- Pinch Zoom ListView Android
Tomé prestada parte de la implementación de TouchImageView aquí , pero he tenido complicaciones. He empezado con panoramización, y parcialmente he tenido éxito, que cava el contenedor alrededor, pero la panorámica funciona horriblemente, que se agita mucho. Aquí está mi código:
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 } }); }
Cualquier idea es muy apreciada.
Editar: jitter parece ser causa porque cuando se mueve, deltaX y deltaY alternar entre los números positivos y negativos, la comprobación de LogCat … aún no está seguro de por qué. Esto es causado por la variable curr que da valores diferentes cada vez, pero en lugar de ser consistente, parecen como si el dedo se movería hacia adelante y hacia atrás en lugar de sólo hacia delante. Por ejemplo, en lugar de curr.x siendo 0,1,2,3,4, etc, es 0,1,0,5,2,1,5, etc. No está seguro de por qué.
- ¿Cómo obtener el valor de zoom de la matriz arbitraria?
- Establecer el nivel de zoom mínimo para MapView
- Cómo ampliar una vista de texto en android?
- Android: ¿Cómo puedo configurar el nivel de zoom de la vista del mapa en un radio de 1 km alrededor de mi ubicación actual?
- Android google maps no se puede ampliar con compás activo
- ¿Cómo puedo obtener funcionalidad de zoom para las imágenes?
- Código de Android para ampliar y desplazar un diseño relativo que contiene cuadros de texto, botones, hilanderos, etc.
- Cómo agrandar el elemento central en un listview - android?
Tengo una solución que funciona con algunos requisitos previos:
- Los elementos del contenedor deben ser del tipo ImageView
- Todas las imágenes deben ser del mismo tamaño para acercarse
Los elementos del contenedor pueden ampliarse y desplazarse (pero no al mismo tiempo). El código también es capaz de averiguar si un usuario ha hecho clic en lugar de mover o ampliar. Los ImageViews se almacenan en una ArrayList en FrameLayout y se escalan y se mueven juntos. Algunas partes de este código se toman de un artículo muy agradable en ZDNet por Ed Burnette ( Link ), que se toma de la muy buena Android libro " Hola, Android ".
Echa un vistazo a este código. Puede utilizar esta clase como diseño en cualquier actividad. Incluso debería ser capaz de utilizarlo en el XML. Por ahora hay un método initializeViews () que se llama en el constructor donde se puede codificar las vistas de imagen que se deben cargar cuando se crea el diseño. Debe agregar algunos mapas de bits en este método después de la línea "ArrayList sampleBitmaps = new ArrayList ();" Para un uso real probablemente es mejor implementar un método addImageView (elemento ImageView) donde se pueden agregar vistas dinámicamente.
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 // eg 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); } }
- Grabación de audio de muchas fuentes / micrófonos
- Cómo abrir navegador predeterminado de cautivo en Android