[英]How to get a ripple effect on click of menu item in toolbar of android
[英]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>
现在,
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.setHotspotsetHotspot()
为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);
}
});
}
其他方案:
另一种方案:
在你的第一次尝试:
我尝试使用填充来增加按钮的触摸区域。
您可以使用此代码将纹波中心置于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的实际区域时,不显示圆按效果。
解决这个问题的方法可能是在ImageButton
的onClick()
方法中添加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)对于TextView
, ImageView
(或某些默认具有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.