web-dev-qa-db-ja.com

ツールバーメニュー項目のクリックリップル効果を実現する

paddingを使用して、ボタンのタッチ領域を拡大しようとしています。私が使う

<ImageButton
    Android:paddingRight="32dp"
    Android:paddingEnd="32dp"
    Android:id="@+id/confirm_image_button"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_alignParentEnd="true"
    Android:layout_alignParentRight="true"
    Android:layout_centerVertical="true"
    Android:background="?selectableItemBackgroundBorderless"
    Android:src="?attr/confirmIcon" />

クリックエリアが拡大されます。ただし、selectableItemBackgroundBorderlessクリック効果は完全な円として表示されなくなりました。

enter image description here


私はduplicateParentStateテクニックを使って克服しようとしています。

<FrameLayout
    Android:clickable="true"
    Android:paddingRight="32dp"
    Android:paddingEnd="32dp"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_alignParentEnd="true"
    Android:layout_alignParentRight="true"
    Android:layout_centerVertical="true">
    <ImageButton
        Android:duplicateParentState="true"
        Android:id="@+id/confirm_image_button"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:background="?selectableItemBackgroundBorderless"
        Android:src="?attr/confirmIcon" />
</FrameLayout>

enter image description here

さて、

  1. クリックした部分が拡大されます。
  2. selectableItemBackgroundBorderless円効果は完全な円です。

しかし、奇妙な振る舞いをしているようです。 ImageButtonの実際の領域をクリックしても、サークルプレス効果が表示されません。

enter image description here

なぜそうなのか、どうすればそれを克服できるのかわかりますか? API 26を使用してテストしました

コードをより複雑にするため、強制されない限り、TouchDelegateテクニックの使用を避けようとしていることに注意してください。


追加情報

以下は、Toolbarのボタンによって示される正しい動作です。

クリック領域がボタンの外側にある場合にリップル効果が表示されます

enter image description here

リップル効果は、クリック領域がボタン内にあるときに表示されます

enter image description here

しかし、私は彼らがそのような振る舞いをどのように実装するのかわかりません。

14
Cheok Yan Cheng

しばらく時間を費やした後、私は最終的に「ツールバーミステリー」の仕組みを見つけました。ツールバーに表示されているのはActionMenuItemViewです。そして、ご覧のように、内部では xml filestyle="?attr/actionButtonStyle"が適用されています。 ?attr/actionButtonStyleWidget.Material.ActionButton に対応し、このスタイル内で<item name="background">?attr/actionBarItemBackground</item>を確認できます。

同じ効果をImageButtonに適用したい場合は、Android:background="?attr/actionBarItemBackground"を適用するだけです。したがって、次のxmlレイアウト:

<ImageButton
    Android:id="@+id/confirm_image_button"
    Android:layout_width="50dp"
    Android:layout_height="50dp"
    Android:background="?attr/actionBarItemBackground"
    Android:src="@drawable/ic_check_black_24dp" />

次の出力が表示されます。

enter image description here

「レイアウト境界の表示」をオンにして、ImageButtonの実際の境界が表示されるようにします

?attr/actionBarItemBackgroundが実際に何を表しているのか知りたい場合は、- ここにあります

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:color="?attr/colorControlHighlight"
    Android:radius="20dp" />

したがって、ドローアブルを作成し、それを背景としてImageButtonに適用できます。

23
azizbekian

Android 5.0の波紋は各ビューの中央から始まります。マテリアルデザインルールによれば、波紋はタッチイベントが発生した場所から始まるため、指から外側に流れるように見えますこれはAndroid 5.1で対処されましたが、Android 5.0のバグでした

これを修正するには、APIレベル21のDrawableに追加されたsetHotspot()メソッドを使用する必要があります。setHotspot()は、ドローアブルに“ホットスポットを提供”、そしてRippleDrawableは明らかにこれを波及効果の発散点として使用しています。 setHotspot()は、おそらくOnTouchListenerがfloat値を含むタッチイベントの_X/Y_位置を報告するため、MotionEvent内でsetHotspot()を使用することを目的として、一対のfloat値を取ります。

ビジーコーダーのガイドブックのおかげで

このコードを使用して、さざ波のバグを修正できます。

_if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
       btn.setOnTouchListener(new View.OnTouchListener() {
          @TargetApi(Build.VERSION_CODES.Lollipop)
          @Override
          public boolean onTouch(View v, MotionEvent event) {
              v.getBackground()
                .setHotspot(event.getX(), event.getY());
              return(false);
          }
     });
}
_

その他の解決策:

サポートライブラリを使用してリップルアニメーションを実現するには?

RippleDrawable


別の解決策:

最初の試み:

ボタンのタッチ領域を増やすためにパディングを使用しようとします。

このコードを使用して、リップルの中心をImageButtonの中心に配置できます:

_if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
           btn.setOnTouchListener(new View.OnTouchListener() {
              @TargetApi(Build.VERSION_CODES.Lollipop)
              @Override
              public boolean onTouch(View v, MotionEvent event) {
                  v.getBackground()
                    .setHotspot(v.getWidth()/2f, v.getHeight()/2f);//setting hot spot at center of ImageButton
                  return(false);
              }
         });
    }
_

FrameLayoutImageButtonをラップする場合の別の解決策:

あなたが言うように:

しかし、奇妙な振る舞いをしているようです。 ImageButtonの実際の領域をクリックしても、サークルプレス効果が表示されません。

この問題の回避策は、ImageButtonframeLayout.performClick()メソッドにonClick()を追加することです。

または、frameLayoutがクリックされたときに、次のコードのようにImageButtonのタッチをシミュレートできます。

_// Obtain MotionEvent object
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis() + 100;
float x = 0.0f;// or x = frameLayout.getWidth()/2f
float y = 0.0f;// or y = frameLayout.getHeight()/2f
// List of meta states found here: developer.Android.com/reference/Android/view/KeyEvent.html#getMetaState()
int metaState = 0;
MotionEvent motionEvent = MotionEvent.obtain(
    downTime, 
    eventTime, 
    MotionEvent.ACTION_UP, 
    x, 
    y, 
    metaState
);

// Dispatch touch event to view
frameLayout.dispatchTouchEvent(motionEvent);
_
2
Bahman

あなたの問題は、ImageButtonだけではなくImageViewがあることにあると思います。 ImageButtonのデフォルトスタイルは次のとおりです。

<style name="Widget.ImageButton">
    <item name="Android:focusable">true</item>
    <item name="Android:clickable">true</item>
    <item name="Android:scaleType">center</item>
    <item name="Android:background">@Android:drawable/btn_default</item>
</style>

置き換えられたのは背景ですが、アイテムはクリック可能でフォーカス可能のままです。あなたの例では、実際のアクティブなアイテムはコンテナなので、通常のImageViewを使用するか、アイテムを手動でクリックおよびフォーカスできないように設定できます。また、コンテナにクリックリスナーを設定することも重要ですnotボタン/画像自体。クリック可能になるためです。

これは1つのバリアントです。

<FrameLayout
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:paddingEnd="48dp"
    Android:focusable="true"
    Android:clickable="true"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    >

    <ImageButton
        Android:duplicateParentState="true"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:background="?selectableItemBackgroundBorderless"
        Android:duplicateParentState="true"
        Android:src="@drawable/ic_check_black_24dp"
        Android:clickable="false"
        Android:focusable="false"
        />
</FrameLayout>

もう1つは同じですが、FrameLayout内のビューは次のとおりです。

<ImageView
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:background="?selectableItemBackgroundBorderless"
    Android:duplicateParentState="true"
    Android:src="@drawable/ic_check_black_24dp"
    />

ここに視覚的な証拠があります:

screen capture

これは、Android 5.0.2を実行しているエミュレータでキャプチャされました。8.0の電話でこれを試しましたが、同様に機能します。

1
Malcolm

_Android:duplicateParentState="true"_を使用する場合、_Android:clickable="false"_をImageButtonに設定するだけで、Buttonをクリックしてその親をハイライトします。

_<ImageButton
       ...
       Android:duplicateParentState="true"
       Android:clickable="false"
  />
_

説明

ドキュメント から

複製が有効になっている場合、このビューは独自の内部プロパティからではなく、親から描画可能状態を取得します

=>親の状態に基づいて状態の変更を表示します。 clickableのデフォルトのImageButtonはtrueです=> ImageButtonをクリックすると、親の状態は変更されません=> ImageButtonは強調表示されません

=>セット_clickable=false_は問題を解決します

いくつかの注意

1)親のセット_clickable=true_を忘れないでください
2)TextViewImageView(またはデフォルトでclickable = falseが設定されているビュー)の場合、_clickable=false_を設定する必要はありません。
3)注意してくださいsetOnClickListenerを使用するとき

_public void setOnClickListener(@Nullable OnClickListener l) {
    if (!isClickable()) {
        setClickable(true);
    }
    getListenerInfo().mOnClickListener = l;
}
_

setOnClickListenerを使用すると、setClickable(true)も使用されるため、この場合はのみ親レイアウトにsetOnClickListenerを設定する必要があります

1
Phan Van Linh