RelativeLayout
とその中にSimpleOnScaleGestureListener
を拡張するプライベートクラスを持つアクティビティがあります。リスナーのonScale
メソッドで、ユーザーが指を広げる/ピンチすることで、レイアウト全体(ユーザーが見るものすべて)をズームイン/アウトしたいと思います。
レイアウトの変更が永続的ではないようにしたい、つまり、スプレッド/ピンチジェスチャが終了したときに、レイアウトを元の場所に戻したい(onScaleEnd
たとえばSimpleOnScaleGestureListener
のメソッド)。
setScaleX
でsetScaleY
とRelativeLayout
を呼び出し、さらにScaleAnimation
を使用して実装しようとしました。どちらもスムーズなズーム(またはズームと呼ばれるもの)をもたらしませんでした。 RelativeLayout
を拡大/縮小することも可能ですか?
私が残した唯一のアイデアは、キャッシュからスクリーンショットを読み取り、レイアウト全体の上に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>
[〜#〜] edit [〜#〜]次の2つの関連トピックが見つかりました。
RelativeLayoutを拡張し、dispatchDraw()をオーバーライドしてズーム可能なViewGroupを作成します
RelativeLayoutのズームコンテンツ
ただし、実装は取得できませんでした。レイアウトを実際にスケーリングまたはリセットするには、拡張クラスに他にどのメソッドを含める必要がありますか?
上記のトピックで説明したように、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
を使用して、ZoomableRelativelayout
にScaleGestureDetector
を統合できます。
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;
}
});
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);
}
}
この後、子が1つしかない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=(ZoomLayout)findviewbyid(R.id.zoomLayout);
zoomlayout.setOnTouchListener(FullScreenVideoActivity.this);
public boolean onTouch(View v, MotionEvent event) {
linear.init(FullScreenVideoActivity.this);
return false;
}
このコードが機能する場合は、受け入れられたとおりに作成してください。
シュノダヒップの答えを少し改善できたと思います。 ZoomableRelativeLayoutクラスに2つのメソッドを追加しました。
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();
}
}
元の答えでは、タッチイベントが終了するたびにスケールがリセットされますが、このように複数回ズームインおよびズームアウトできます。
フラグメントの場合、アクティビティ名の代わりにgetActivity()を渡すだけです
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;
}
});