Android – acercar / alejar RelativeLayout con spread / pinch
Tengo una actividad con un RelativeLayout
y una clase privada en él, que extiende el SimpleOnScaleGestureListener
. En el método onScale
del oyente me gustaría acercar / alejar el diseño completo (todo lo que ve el usuario) con el usuario extendiendo / pellizcando sus dedos.
Me gustaría que los cambios en el diseño no ser permanente, es decir, cuando el gesto de propagación / pellizco es más que me gustaría que el diseño para volver a lo que era en primer lugar (cualquier restablecimiento podría hacerse en el método onScaleEnd
de El SimpleOnScaleGestureListener
por ejemplo).
- Android: widget de búsqueda de ActionBarSherlock
- Arregle dinámicamente los botones alrededor de un círculo
- Menu.findItem devuelve null
- StackOverflowError al intentar inflar un diseño personalizado para un AlertDialog dentro de un DialogFragment
- TextViews con divisores entre ellos
He intentado implementarlo a través de llamar a setScaleX
y setScaleY
en RelativeLayout
y también usando una ScaleAnimation
. Tampoco resultó en un zoom suave (o cualquier cosa que se podría llamar zoom). ¿Es incluso posible acercar / alejar un RelativeLayout
?
La única idea que me queda es leer una captura de pantalla de la caché y ponerla como ImageView
en la parte superior de todo el diseño y el zoom in / out de esta imagen a través de setImageMatrix
. Sin embargo, no tengo ni idea de cómo implementar esto.
El diseño de mayo también contiene un contenedor para un fragmento, que está vacío en el momento en el que se supone que el zoom es posible. En el gesto onScaleEnd
, el fragmento se pone en su contenedor (ya implementado y funcionando bien). Aquí está mi diseño:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/layout_pinch" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff" > <!-- Layout containing the thumbnail ImageViews --> <LinearLayout android:id="@+id/thumbnail_group_pui" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:orientation="horizontal" > <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/tn_c1"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/tn_c2"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/tn_c3"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/tn_c4"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/tn_c5"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/tn_c6"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/tn_c7"/> </LinearLayout> <!-- Layout containing the dashed boxes --> <LinearLayout android:layout_width="match_parent" android:layout_height="152dp" android:layout_centerVertical="true" android:orientation="horizontal" > <ImageView android:layout_width="177dp" android:layout_height="match_parent" android:layout_marginLeft="3dp" android:layout_marginRight="3dp" android:background="@drawable/dashed_box"/> <ImageView android:layout_width="177dp" android:layout_height="match_parent" android:layout_marginLeft="3dp" android:layout_marginRight="3dp" android:background="@drawable/dashed_box"/> <ImageView android:layout_width="177dp" android:layout_height="match_parent" android:layout_marginLeft="3dp" android:layout_marginRight="3dp" android:background="@drawable/dashed_box"/> <ImageView android:layout_width="177dp" android:layout_height="match_parent" android:layout_marginLeft="3dp" android:layout_marginRight="3dp" android:background="@drawable/dashed_box"/> <ImageView android:layout_width="177dp" android:layout_height="match_parent" android:layout_marginLeft="3dp" android:layout_marginRight="3dp" android:background="@drawable/dashed_box"/> <ImageView android:layout_width="177dp" android:layout_height="match_parent" android:layout_marginLeft="3dp" android:layout_marginRight="3dp" android:background="@drawable/dashed_box"/> <ImageView android:layout_width="177dp" android:layout_height="match_parent" android:layout_marginLeft="3dp" android:layout_marginRight="3dp" android:background="@drawable/dashed_box"/> </LinearLayout> <!-- Container for the fragments --> <FrameLayout android:id="@+id/fragment_container_pui" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
EDIT He encontrado estos dos temas relacionados:
Extendiendo RelativeLayout y reemplazando dispatchDraw () para crear un ViewGroup con zoom
Zoom de contenido en un RelativeLayout
No obtuve la implementación, sin embargo. ¿Qué otros métodos tengo que incluir en la clase extendida para realmente escalar el diseño o restablecerlo?
- Imagen GridView Fragmento interior
- ¿Por qué ir para el diseño de restricciones ya que ya tenemos Disposición relativa?
- Estilo personalizado listdivider
- GetContentView en Android
- Cómo evitar onCreateView () al deslizar en las pestañas
- Vista de lista de Android setOnItemClickListener no funciona
- XML predeterminado para la notificación de estado
- Disposición del teclado que oculta la barra de acción del androide?
Así que creé una subclase de RelativeLayout
como se describe en los temas mencionados anteriormente. Se parece a esto:
public class ZoomableRelativeLayout extends RelativeLayout { float mScaleFactor = 1; float mPivotX; float mPivotY; public ZoomableRelativeLayout(Context context) { super(context); // TODO Auto-generated constructor stub } public ZoomableRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } public ZoomableRelativeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub } protected void dispatchDraw(Canvas canvas) { canvas.save(Canvas.MATRIX_SAVE_FLAG); canvas.scale(mScaleFactor, mScaleFactor, mPivotX, mPivotY); super.dispatchDraw(canvas); canvas.restore(); } public void scale(float scaleFactor, float pivotX, float pivotY) { mScaleFactor = scaleFactor; mPivotX = pivotX; mPivotY = pivotY; this.invalidate(); } public void restore() { mScaleFactor = 1; this.invalidate(); } }
Mi implementación de SimpleOnScaleGestureListener
tiene este aspecto:
private class OnPinchListener extends SimpleOnScaleGestureListener { float startingSpan; float endSpan; float startFocusX; float startFocusY; public boolean onScaleBegin(ScaleGestureDetector detector) { startingSpan = detector.getCurrentSpan(); startFocusX = detector.getFocusX(); startFocusY = detector.getFocusY(); return true; } public boolean onScale(ScaleGestureDetector detector) { mZoomableRelativeLayout.scale(detector.getCurrentSpan()/startingSpan, startFocusX, startFocusY); return true; } public void onScaleEnd(ScaleGestureDetector detector) { mZoomableRelativeLayout.restore(); } }
¡Espero que esto ayude!
Actualizar:
Puede integrar OnPinchListener
para su ZoomableRelativelayout
utilizando ScaleGestureDetector
:
ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(this, new OnPinchListener());
Y usted está obligado a ligar el oyente táctil de Zoomable layout con el oyente táctil de ScaleGestureDetector:
mZoomableLayout.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub scaleGestureDetector.onTouchEvent(event); return true; } });
Creo que logré mejorar la respuesta de Schnodahipfe un poco. He añadido dos métodos a la clase ZoomableRelativeLayout.
public void relativeScale(float scaleFactor, float pivotX, float pivotY) { mScaleFactor *= scaleFactor; if(scaleFactor >= 1) { mPivotX = mPivotX + (pivotX - mPivotX) * (1 - 1 / scaleFactor); mPivotY = mPivotY + (pivotY - mPivotY) * (1 - 1 / scaleFactor); } else { pivotX = getWidth()/2; pivotY = getHeight()/2; mPivotX = mPivotX + (pivotX - mPivotX) * (1 - scaleFactor); mPivotY = mPivotY + (pivotY - mPivotY) * (1 - scaleFactor); } this.invalidate(); } public void release() { if(mScaleFactor < MIN_SCALE) { final float startScaleFactor = mScaleFactor; Animation a = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { scale(startScaleFactor + (MIN_SCALE - startScaleFactor)*interpolatedTime,mPivotX,mPivotY); } }; a.setDuration(300); startAnimation(a); } else if(mScaleFactor > MAX_SCALE) { final float startScaleFactor = mScaleFactor; Animation a = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { scale(startScaleFactor + (MAX_SCALE - startScaleFactor)*interpolatedTime,mPivotX,mPivotY); } }; a.setDuration(300); startAnimation(a); } }
Y reescribió la clase OnPinchListener como esta
private class OnPinchListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { float currentSpan; float startFocusX; float startFocusY; public boolean onScaleBegin(ScaleGestureDetector detector) { currentSpan = detector.getCurrentSpan(); startFocusX = detector.getFocusX(); startFocusY = detector.getFocusY(); return true; } public boolean onScale(ScaleGestureDetector detector) { ZoomableRelativeLayout zoomableRelativeLayout= (ZoomableRelativeLayout) ImageFullScreenActivity.this.findViewById(R.id.imageWrapper); zoomableRelativeLayout.relativeScale(detector.getCurrentSpan() / currentSpan, startFocusX, startFocusY); currentSpan = detector.getCurrentSpan(); return true; } public void onScaleEnd(ScaleGestureDetector detector) { ZoomableRelativeLayout zoomableRelativeLayout= (ZoomableRelativeLayout) ImageFullScreenActivity.this.findViewById(R.id.imageWrapper); zoomableRelativeLayout.release(); } }
La respuesta original restablecería la escala cada vez que terminara el evento táctil, pero de esta manera puede acercar y alejar varias veces.
Crear una clase llamada Zoomlayout que se extiende cualquier diseño que desea ampliar en mi caso es Disposición relativa.
public class ZoomLayout extends RelativeLayout implements ScaleGestureDetector.OnScaleGestureListener { private enum Mode { NONE, DRAG, ZOOM } private static final String TAG = "ZoomLayout"; private static final float MIN_ZOOM = 1.0f; private static final float MAX_ZOOM = 4.0f; private Mode mode = Mode.NONE; private float scale = 1.0f; private float lastScaleFactor = 0f; // Where the finger first touches the screen private float startX = 0f; private float startY = 0f; // How much to translate the canvas private float dx = 0f; private float dy = 0f; private float prevDx = 0f; private float prevDy = 0f; public ZoomLayout(Context context) { super(context); init(context); } public ZoomLayout(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public ZoomLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } public void init(Context context) { final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, this); this.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: Log.i(TAG, "DOWN"); if (scale > MIN_ZOOM) { mode = Mode.DRAG; startX = motionEvent.getX() - prevDx; startY = motionEvent.getY() - prevDy; } break; case MotionEvent.ACTION_MOVE: if (mode == Mode.DRAG) { dx = motionEvent.getX() - startX; dy = motionEvent.getY() - startY; } break; case MotionEvent.ACTION_POINTER_DOWN: mode = Mode.ZOOM; break; case MotionEvent.ACTION_POINTER_UP: mode = Mode.DRAG; break; case MotionEvent.ACTION_UP: Log.i(TAG, "UP"); mode = Mode.NONE; prevDx = dx; prevDy = dy; break; } scaleDetector.onTouchEvent(motionEvent); if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM) { getParent().requestDisallowInterceptTouchEvent(true); float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale; float maxDy = (child().getHeight() - (child().getHeight() / scale))/ 2 * scale; dx = Math.min(Math.max(dx, -maxDx), maxDx); dy = Math.min(Math.max(dy, -maxDy), maxDy); Log.i(TAG, "Width: " + child().getWidth() + ", scale " + scale + ", dx " + dx + ", max " + maxDx); applyScaleAndTranslation(); } return true; } }); } // ScaleGestureDetector @Override public boolean onScaleBegin(ScaleGestureDetector scaleDetector) { Log.i(TAG, "onScaleBegin"); return true; } @Override public boolean onScale(ScaleGestureDetector scaleDetector) { float scaleFactor = scaleDetector.getScaleFactor(); Log.i(TAG, "onScale" + scaleFactor); if (lastScaleFactor == 0 || (Math.signum(scaleFactor) == Math.signum(lastScaleFactor))) { scale *= scaleFactor; scale = Math.max(MIN_ZOOM, Math.min(scale, MAX_ZOOM)); lastScaleFactor = scaleFactor; } else { lastScaleFactor = 0; } return true; } @Override public void onScaleEnd(ScaleGestureDetector scaleDetector) { Log.i(TAG, "onScaleEnd"); } private void applyScaleAndTranslation() { child().setScaleX(scale); child().setScaleY(scale); child().setTranslationX(dx); child().setTranslationY(dy); } private View child() { return getChildAt(0); } }
Después de esto agregue ZoomLayout en xml que tiene solamente un niño. Por ejemplo
<?xml version="1.0" encoding="utf-8"?> <com.focusmedica.digitalatlas.headandneck.ZoomLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:id="@+id/zoomLayout" android:background="#000000" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:paddingTop="5dp" android:textColor="#ffffff" android:text="Heading" android:gravity="center" android:textAlignment="textStart" android:paddingLeft="5dp" android:textSize="20sp" android:textStyle="bold" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/tvSubtitle2" android:layout_toLeftOf="@+id/ivOn" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> <ImageView android:id="@+id/ivOff" android:layout_width="40dp" android:layout_height="40dp" android:src="@drawable/off_txt" android:layout_alignParentTop="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> <ImageView android:id="@+id/ivOn" android:layout_width="40dp" android:layout_height="40dp" android:src="@drawable/on_txt" android:layout_alignParentTop="true" android:layout_alignLeft="@+id/pinOn" android:layout_alignStart="@+id/pinOn" /> <ImageView android:id="@+id/pinOff" android:visibility="invisible" android:layout_width="40dp" android:layout_height="40dp" android:src="@drawable/pin_off" android:layout_alignParentTop="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> <ImageView android:id="@+id/pinOn" android:layout_width="40dp" android:layout_height="40dp" android:src="@drawable/pin_on" android:layout_alignParentTop="true" android:layout_toLeftOf="@+id/ivOff" android:layout_toStartOf="@+id/ivOff" /> <RelativeLayout android:id="@+id/linear" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true"> <ImageView android:src="@drawable/wait" android:layout_width="match_parent" android:layout_height="300dp" android:id="@+id/fullIVideo"/> <ImageView android:src="@drawable/wait" android:layout_width="match_parent" android:layout_height="300dp" android:id="@+id/colorCode"/> <ImageView android:src="@drawable/wait" android:layout_width="match_parent" android:layout_height="300dp" android:id="@+id/labelText"/> <ImageView android:src="@drawable/download" android:layout_marginTop="91dp" android:layout_width="100dp" android:layout_height="100dp" android:id="@+id/label_play" android:layout_alignTop="@+id/fullIVideo" android:layout_centerVertical="true" android:layout_centerHorizontal="true" /> </RelativeLayout> <LinearLayout android:orientation="vertical" android:id="@+id/custom_toast_layout" android:layout_width="300dp" android:layout_above="@+id/up" android:background="@drawable/rectangle_frame" android:paddingLeft="10dp" android:paddingBottom="10dp" android:paddingTop="10dp" android:paddingRight="10dp" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:layout_height="wrap_content"> <TextView android:textSize="15sp" android:textColor="#ffffff" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Medium Text" android:id="@+id/tvLabel" /> <TextView android:textColor="#ffffff" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:text="New Text" android:layout_gravity="center" android:id="@+id/tvLabelDescription" /> </LinearLayout> <ImageView android:layout_width="50dp" android:layout_height="50dp" android:src="@drawable/up" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:id="@+id/up" /> </RelativeLayout> </com.focusmedica.digitalatlas.headandneck.ZoomLayout>
Ahora en MainActivity cree el objeto de ZoomLayout y defina id.Like
ZoomLayout zoomlayout=(ZoomLayout)findviewbyid(R.id.zoomLayout); zoomlayout.setOnTouchListener(FullScreenVideoActivity.this); public boolean onTouch(View v, MotionEvent event) { linear.init(FullScreenVideoActivity.this); return false; }
I hpoe él trabajará. Si este código trabajará entonces satisface hace como aceptado.
La solución de Ashish funcionó para mí. Tuve que hacer algunos ajustes de codificación, ya que había algunos errores de formato en la implementación MainActivity.
Vea a continuación los cambios:
final ZoomLayout zoomlayout = (ZoomLayout) findViewById(R.id.zoomLayout); zoomlayout.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { zoomlayout.init(FullScreenVideoActivity.this); return false; } });
- Compartición de datos entre dos aplicaciones
- Android: redimensionamiento / ampliación de imagen de alta calidad