画面がタップされた時にその画面にあるとあるViewをGONEにしたい
画面いっぱいにギャラリーなどで写真を表示させた時のように拡大・縮小などができるScalingPhotoViewというカスタムViewを配置し、それに重ねるようにして画面下に半透明の黒いバーを配置し、その左にAddCartButtonというカスタムButtonを配置しています。
レイアウトのxmlは以下の通りです。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.sakaguchi.PhotoFragment"
android:clickable="true">
<com.example.sakaguchi.ScalingPhotoView
android:id="@+id/photo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@color/half_transparent_black">
<com.example.sakaguchi.AddCartButton
android:id="@+id/add_cart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@drawable/add_cart_button_for_photo"
android:gravity="center"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:text="@string/add_cart"
android:textColor="@color/orange"
android:textSize="15sp"/>
</FrameLayout>
画面がタップされた時にその画面にAddCartButtonをGONEにしたりしたいので、FragmentのrootViewにView.OnClickListenerをセットしてそこで行おうと思いました。
ですが、ScalingPhotoViewでジェスチャーを検知させるためにonTouchの戻り値をtrueにしているため、そのView.OnClickListenerは反応しないようです。
ScalingPhotoViewでシングルタッチを検知し、事前に持たせておいたAddCartButtonのオブジェクトを操作するかEventBusでAddCartButtonに通知して操作してもらうかしかないでしょうか?
ScalingPhotoViewの実装は以下のようになっています。
(PhotoViewというのはネットからの写真の読み込みや透かし追加をやっているカスタムViewです。)
package com.example.sakaguchi.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
/**
* 写真の拡大・縮小に対応したPhotoView
* 下記クラスのメソッド名等を書き換えたり非推奨のものを使わなくていいようにアルゴリズム変えたりしています。
*
* scale-imageview-android/ScaleImageView.java at master · matabii/scale-imageview-android
* 透かしはPhotoViewで設定。
*/
public class ScalingPhotoView extends PhotoView {
private static final float sMAX_SCALE = 2f;
private float mMinScale;
private Matrix mMatrix;
private int mViewWith;
private int mViewHeight;
private int mIntrinsicWidth;
private int mIntrinsicHeight;
public ScalingPhotoView(Context context, AttributeSet attr) {
super(context, attr);
initialize();
}
@Override
public void loadPhoto(String photoUrl) {
loadPhoto(photoUrl, sMAX_SCALE);
}
/**
* 写真に透かしを付けてからViewにセットしてサイズを初期化
* 透かしをつける処理はsuperであるPhotoViewで行う。
*
* @param photo 表示したい写真
*/
@Override
public void setImageBitmap(Bitmap photo) {
super.setImageBitmap(photo);
initialize();
}
/**
* イメージリソースを設定してサイズを初期化
*
* @param resId 設定したいイメージリソースのid
*/
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
this.initialize();
}
/**
* 初期設定
*/
private void initialize() {
setScaleType(ScaleType.MATRIX);
mMatrix = new Matrix();
Drawable d = getDrawable();
if (d != null) {
mIntrinsicWidth = d.getIntrinsicWidth();
mIntrinsicHeight = d.getIntrinsicHeight();
}
setOnTouchListener(new ScalingPhotoTouchListener());
}
/**
* Viewの位置から写真のサイズと位置を設定
*
* @param l 左の位置 親との相対
* @param t 上の位置 親との相対
* @param r 右の位置 親との相対
* @param b 下の位置 親との相対
* @return 前のサイズや位置と違うかどうか
*/
@Override
protected boolean setFrame(int l, int t, int r, int b) {
mViewWith = r - l;
mViewHeight = b - t;
mMatrix.reset();
float scaleWidth = (float) mViewWith / (float) mIntrinsicWidth;
float scaleHeight = (float) mViewHeight / (float) mIntrinsicHeight;
if (scaleWidth > scaleHeight) {
mMinScale = scaleHeight;
} else {
mMinScale = scaleWidth;
}
mMatrix.postScale(mMinScale, mMinScale);
float photoWidth = mIntrinsicWidth * mMinScale;
float photoHeight = mIntrinsicHeight * mMinScale;
mMatrix.postTranslate((mViewWith - photoWidth) / 2, (mViewHeight - photoHeight) / 2);
setImageMatrix(mMatrix);
return super.setFrame(l, t, r, b);
}
/**
* Matrixの特定の値だけ取得
*
* @param whichValue 取得したい値
* @return 取得した値
*/
private float getMatrixValue(int whichValue) {
float[] mMatrixValues = new float[9];
mMatrix.getValues(mMatrixValues);
return mMatrixValues[whichValue];
}
private float getScale() {
return getMatrixValue(Matrix.MSCALE_X);
}
private float getTranslateX() {
return getMatrixValue(Matrix.MTRANS_X);
}
private float getTranslateY() {
return getMatrixValue(Matrix.MTRANS_Y);
}
/**
* スケール変更
*
* @param scale スケール値
* @param x 中央に設定するx座標
* @param y 中央に設定するy座標
*/
private void scaleTo(float scale, float x, float y) {
if (getScale() * scale = 1 && getScale() * scale > sMAX_SCALE) {
return;
}
mMatrix.postScale(scale, scale);
//中央に移動 画面との差分の半分だけ移動する
mMatrix.postTranslate(-(mViewWith * scale - mViewWith) / 2, -(mViewHeight * scale - mViewHeight) / 2);
//xとyの距離だけ移動 タップ位置-画面の半分(つまり中央からの相対距離)にスケールをかけた分だけ移動
mMatrix.postTranslate(-(x - (mViewWith / 2)) * scale, -(y - (mViewHeight / 2)) * scale);
setImageMatrix(mMatrix);
}
/**
* 写真の端の向こうへスクロールできないよう制御
*/
private void scrollPhotoEdgeOverCutting() {
float photoWidth = mIntrinsicWidth * getScale();
float photoHeight = mIntrinsicHeight * getScale();
if ((photoWidth - mViewWith) == 0) {
mMatrix.postTranslate(-getTranslateX(), 0);
} else {
if (getTranslateX() 0) {
mMatrix.postTranslate(-getTranslateX(), 0);
}
if (photoWidth 0) {
mMatrix.postTranslate(0, -getTranslateY());
}
if (photoHeight minFilter) {
scale = mMinScale / getScale();
} else {
scale = sMAX_SCALE / getScale();
}
scaleTo(scale, e.getX(), e.getY());
scrollPhotoEdgeOverCutting();
return true; //falseでも挙動変化なし
}
/**
* スクロールで写真を移動
*
* @param e1 スクロールイベント1
* @param e2 スクロールイベント2
* @param distanceX スクロールのxの距離
* @param distanceY スクロールのyの距離
* @return このメソッドでイベントを消費する(true)
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
mMatrix.postTranslate(-distanceX, -distanceY);
scrollPhotoEdgeOverCutting();
return true; //falseでも挙動変化なし
}
}
}
追記
rootを以下のようなFrameLayoutを拡張したクラスにしてonInterceptTouchEventを利用してみたのですが、当然AddCartButton上でタップした時も反応してしまいました。
この方法はどうにも使えないでしょうか?
package com.example.sakaguchi;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
/**
* タッチイベントをインターセプトして子Viewに渡す前に親Viewで利用するためのカスタムView
*/
public class InterceptTouchView extends FrameLayout {
public InterceptTouchView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_UP) {
AddCartButton addCart = (AddCartButton) findViewById(R.id.add_cart);
addCart.toggleVisibleButton();
}
return false;
}
}