J'ai une activité avec un RelativeLayout
et une classe privée, qui étend le SimpleOnScaleGestureListener
. Dans la méthode onScale
de l'auditeur, j'aimerais zoomer/dézoomer sur toute la mise en page (tout ce que l'utilisateur voit) avec l'utilisateur écartant/pinçant ses doigts.
J'aimerais que les modifications apportées à la mise en page ne soient PAS permanentes, c'est-à-dire que lorsque le geste de propagation/pincement est terminé, je voudrais que la mise en page revienne à ce qu'elle était en premier lieu (toute réinitialisation pourrait être effectuée dans le onScaleEnd
méthode du SimpleOnScaleGestureListener
par exemple).
J'ai essayé de l'implémenter en appelant setScaleX
et setScaleY
sur le RelativeLayout
et également en utilisant un ScaleAnimation
. Aucun des deux n'a entraîné un zoom fluide (ou quelque chose qui pourrait du tout être appelé zoom). Est-il même possible de zoomer/dézoomer sur un RelativeLayout
?
La seule idée qui me reste serait de lire une capture d'écran du cache et de la mettre en tant que ImageView
en haut de la mise en page entière et le zoom avant/arrière de cette image via setImageMatrix
. Je n'ai cependant aucune idée de la façon de mettre cela en œuvre.
La disposition de mai contient également un conteneur pour un fragment, qui est vide au moment où le zoom est censé être possible. Dans le geste onScaleEnd
, le fragment est placé dans son conteneur (déjà implémenté et fonctionne correctement). Voici ma mise en page:
<?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>
[~ # ~] modifier [~ # ~] J'ai trouvé ces deux sujets connexes:
Extension de RelativeLayout et remplacement de dispatchDraw () pour créer un ViewGroup zoomable
Zoom sur le contenu dans un RelativeLayout
Cependant, je n'ai pas obtenu la mise en œuvre. Quelles autres méthodes dois-je inclure dans la classe étendue pour réellement mettre à l'échelle la disposition ou la réinitialiser?
J'ai donc créé une sous-classe de RelativeLayout
comme décrit dans les rubriques mentionnées ci-dessus. Cela ressemble à ceci:
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();
}
}
Mon implémentation de SimpleOnScaleGestureListener
ressemble à ceci:
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();
}
}
J'espère que cela t'aides!
Vous pouvez intégrer OnPinchListener
pour votre ZoomableRelativelayout
en utilisant ScaleGestureDetector
:
ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(this, new OnPinchListener());
Et vous devez lier l'écouteur tactile de la disposition Zoomable avec l'écouteur tactile de ScaleGestureDetector:
mZoomableLayout.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
scaleGestureDetector.onTouchEvent(event);
return true;
}
});
Créez une classe appelée Zoomlayout qui étend toute mise en page que vous souhaitez zoomer dans mon cas, il s'agit de mise en page relative.
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);
}
}
Après cela, ajoutez ZoomLayout en xml qui n'a qu'un seul enfant. Par exemple
<?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>
Maintenant, dans MainActivity, créez un objet de ZoomLayout et définissez 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;
}
Je pense que cela fonctionnera. Si ce code fonctionne, veuillez le faire comme accepté.
Je pense que j'ai réussi à améliorer un peu la réponse de Schnodahipfe. J'ai ajouté deux méthodes à la classe 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);
}
}
et réécrit la classe OnPinchListener comme ceci
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 réponse d'origine réinitialisait l'échelle à chaque fois que l'événement tactile se terminait, mais comme cela, vous pouvez zoomer et dézoomer plusieurs fois.
pour les fragments dont vous avez juste besoin de passer getActivity () au lieu du nom de l'activité
final ZoomLayout zoomlayout = (ZoomLayout) findViewById(R.id.zoomLayout);
zoomlayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
zoomlayout.init(getActivity());
return false;
}
});