繁体   English   中英

实现工具栏菜单项单击涟漪效果

[英]Achieve toolbar menu item click ripple effect

我尝试使用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点击效果不再显示为完美的圆圈。

在此输入图像描述


我尝试使用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>

在此输入图像描述

现在,

  1. 点击区域被放大。
  2. selectableItemBackgroundBorderless圆圈效果是一个完美的圆圈。

然而,它似乎有一些奇怪的行为。 当我单击ImageButton的实际区域时,不显示圆按效果。

在此输入图像描述

我可以知道为什么会这样,我怎么能克服它? 我使用API​​ 26进行了测试。

注意,我尽量避免使用TouchDelegate技术,除非我被迫,因为它使我们的代码更复杂。


附加信息

以下是Toolbar按钮显示的正确行为。

当点击区域位于按钮外时,会显示波纹效果

在此输入图像描述

当点击区域在按钮内时,会显示波纹效果

在此输入图像描述

但是,我不知道他们是如何实现这种行为的。

花了一些时间后,我终于找到了“工具栏神秘”的工作方式。 它是ActionMenuItemView ,它正在工具栏上显示。 你可以看到,在xml文件里面应用了style="?attr/actionButtonStyle" ?attr/actionButtonStyle对应于Widget.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" />

你会收到这个输出:

在此输入图像描述

打开“显示布局边界”,以便可以看到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" />

因此,您可以创建drawable并将其作为背景应用于ImageButton

Android 5.0上的涟漪从每个视图的中心开始。 根据材料设计规则,涟漪应该从触摸事件发生的地方开始,因此它们似乎从手指向外流动。 这在Android 5.1中得到解决,但在Android 5.0中是一个错误

要解决这个问题,你需要使用setHotspot()方法,在API Level 21中添加到Drawable.setHotspot setHotspot()为drawable提供了一个“热点” ,而RippleDrawable显然使用它作为涟漪效应的发散点。 setHotspot()在一对浮点值,推测具有朝向使用眼睛setHotspot()的内部OnTouchListener ,作为MotionEvent报告X/Y触摸事件的位置与浮点值。

感谢The Busy Coder的指南书

您可以使用此代码来纠正涟漪错误:

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

FrameLayout包装ImageButton时,可能是另一种解决方案:

正如你所说:

然而,它似乎有一些奇怪的行为。 当我单击ImageButton的实际区域时,不显示圆按效果。

解决这个问题的方法可能是在ImageButtononClick()方法中添加frameLayout.performClick()

或者,当单击ImageButton时,您可以像在此代码上一样模拟frameLayout上的触摸:

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

我怀疑你的问题在于你有一个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 ,也可以将项设置为不可单击并可手动聚焦。 同样重要的是在容器上设置单击侦听器, 而不是按钮/图像本身,因为它将变为可单击。

这是一个变种:

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

另一个是相同的,除了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"
    />

这是视觉证明:

屏幕截图

这是在运行Android 5.0.2的模拟器上捕获的。 我在8.0手机上试过这个,也可以。

如果您使用android:duplicateParentState="true" ,只需将android:clickable="false"为您的ImageButton当您点击它及其父级时,将使您的Button突出显示

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

说明

文件

启用重复时,此视图将从其父级而不是从其自己的内部属性获取其可绘制状态

=>根据父状态查看更改状态。 ImageButton的默认clickable是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它也将是setOnClickListener setClickable(true)因此在这种情况下,您只能为其父布局设置setOnClickListener

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM