35

我有一个活动,其中包含一个RelativeLayout和一个私有类,它扩展了SimpleOnScaleGestureListener. 在onScale听众的方法中,我想放大/缩小整个布局(用户看到的一切),用户展开/捏手指。

我希望布局的更改不是永久性的,即当展开/捏合手势结束时,我希望布局回到最初的状态(任何重置都可以onScaleEndSimpleOnScaleGestureListener例如)。

我尝试通过调用setScaleXandsetScaleY以及RelativeLayout使用ScaleAnimation. 两者都没有导致平滑缩放(或任何可以称为缩放的东西)。甚至可以放大/缩小 aRelativeLayout吗?

我剩下的唯一想法是从缓存中读取屏幕截图并将其ImageView放在整个布局的顶部,并通过setImageMatrix. 但是,我不知道如何实现这一点。

可能布局还包含一个片段容器,该容器在应该可以缩放时是空的。在onScaleEnd手势中,片段被放入它的容器中(已经实现并且工作正常)。这是我的布局:

<?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>

编辑 我发现了这两个相关主题:
扩展RelativeLayout,并覆盖dispatchDraw() 在RelativeLayout 中创建可缩放的ViewGroup
缩放内容

但是,我没有得到实施。我必须在扩展类中包含哪些其他方法才能实际缩放布局或重置布局?

4

4 回答 4

31

因此,我创建了RelativeLayout上述主题中描述的子类。它看起来像这样:

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

}

我的实现SimpleOnScaleGestureListener如下所示:

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

希望这可以帮助!

更新:

您可以使用以下方式OnPinchListener为您集成:ZoomableRelativelayoutScaleGestureDetector

ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(this, new OnPinchListener());

并且需要将 Zoomable 布局的触摸监听器与 ScaleGestureDetector 的触摸监听器绑定:

mZoomableLayout.setOnTouchListener(new OnTouchListener() {

                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    // TODO Auto-generated method stub
                    scaleGestureDetector.onTouchEvent(event);
                    return true;
                }
            });
于 2012-04-05T13:14:52.970 回答
19

创建名为 Zoomlayout 的类,它扩展了您想要放大的任何布局,在我的情况下它是相对布局。

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

}

在此之后,在只有一个孩子的 xml 中添加 ZoomLayout。例如

<?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>

现在在 MainActivity 创建 ZoomLayout 对象并定义 id.Like

ZoomLayout zoomlayout= findViewbyId(R.id.zoomLayout);
zoomlayout.setOnTouchListener(FullScreenVideoActivity.this);
 public boolean onTouch(View v, MotionEvent event) {
     linear.init(FullScreenVideoActivity.this);
     return false;
 }
于 2016-08-08T09:49:16.913 回答
6

我想我设法稍微改进了 Schnodahipfe 的答案。我向 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);
    }
}

并像这样重写了 OnPinchListener 类

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

每次触摸事件结束时,原始答案都会重置比例,但是像这样您可以多次放大和缩小。

于 2014-11-05T22:02:45.230 回答
3

对于片段,您只需要传递 getActivity() 而不是 Activity Name

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;
        }
    });
于 2016-11-23T17:13:04.233 回答