Android L 的涟漪效应 - 按钮的触摸反馈 - 使用 XML
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24863430/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android L's Ripple Effect - Touch Feedback for Buttons - Using XML
提问by Vamsi Challa
I am trying to understand how to implement the "Ripple Effect - Touch Feedback" for buttons and other views. I looked at the questions related to Ripple touch effect on SO and got some insight into it. I was able to successfully get the ripple effect using this java code.
我试图了解如何为按钮和其他视图实现“波纹效果 - 触摸反馈”。我查看了与 SO 上的 Ripple 触摸效应相关的问题,并对它有所了解。我能够使用此 java 代码成功获得连锁反应。
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RadialGradient;
import android.graphics.Region;
import android.graphics.Shader;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.AccelerateInterpolator;
import android.widget.Button;
public class MyButton extends Button {
private float mDownX;
private float mDownY;
private float mRadius;
private Paint mPaint;
public MyButton(final Context context) {
super(context);
init();
}
public MyButton(final Context context, final AttributeSet attrs) {
super(context, attrs);
init();
}
public MyButton(final Context context, final AttributeSet attrs,
final int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setAlpha(100);
}
@Override
public boolean onTouchEvent(@NonNull final MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
mDownX = event.getX();
mDownY = event.getY();
ObjectAnimator animator = ObjectAnimator.ofFloat(this, "radius", 0,
getWidth() * 3.0f);
animator.setInterpolator(new AccelerateInterpolator());
animator.setDuration(400);
animator.start();
}
return super.onTouchEvent(event);
}
public void setRadius(final float radius) {
mRadius = radius;
if (mRadius > 0) {
RadialGradient radialGradient = new RadialGradient(mDownX, mDownY,
mRadius * 3, Color.TRANSPARENT, Color.BLACK,
Shader.TileMode.MIRROR);
mPaint.setShader(radialGradient);
}
invalidate();
}
private Path mPath = new Path();
private Path mPath2 = new Path();
@Override
protected void onDraw(@NonNull final Canvas canvas) {
super.onDraw(canvas);
mPath2.reset();
mPath2.addCircle(mDownX, mDownY, mRadius, Path.Direction.CW);
canvas.clipPath(mPath2);
mPath.reset();
mPath.addCircle(mDownX, mDownY, mRadius / 3, Path.Direction.CW);
canvas.clipPath(mPath, Region.Op.DIFFERENCE);
canvas.drawCircle(mDownX, mDownY, mRadius, mPaint);
}
}
But, i want to use XML approach. How do i achieve this? I have looked at thisand this, but i am not yet that comfortable with styles, so i am finding it difficult to achieve the ripple effect.
但是,我想使用 XML 方法。我如何实现这一目标?我看过这个和这个,但我对样式还不太满意,所以我发现很难实现涟漪效应。
I have a button with the following XML code:
我有一个带有以下 XML 代码的按钮:
<Button
android:id="@+id/button_email"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="0.50"
android:gravity="center"
android:text="@string/email" />
How do i get ripple effect for this button. If someone can guide me, I will be thankful.
我如何获得此按钮的涟漪效果。如果有人可以指导我,我将不胜感激。
[EDIT] Adding ripple.xml and background.xml, as mentioned in one of the links above. I have created a drawable-v21 folder in res and added the below files there.
[编辑] 添加ripple.xml 和background.xml,如上述链接之一所述。我在 res 中创建了一个 drawable-v21 文件夹并在那里添加了以下文件。
ripple.xml
涟漪文件
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@android:color/black" >
<item android:drawable="@drawable/background">
</item>
</ripple>
background.xml
背景文件
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="@android:color/darker_gray" />
</shape>
I added the ripple as background for my button, here is the xml for my button now..
我添加了涟漪作为我按钮的背景,这是我现在按钮的 xml..
<Button
android:id="@+id/button_email"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="0.50"
android:gravity="center"
android:background="@drawable/ripple"
android:text="@string/email" />
When i run the application i get a ResourceNotFoundException. Here is the logcat trace..
当我运行应用程序时,我得到一个 ResourceNotFoundException。这是logcat跟踪..
07-21 17:03:39.043: E/AndroidRuntime(15710): FATAL EXCEPTION: main
07-21 17:03:39.043: E/AndroidRuntime(15710): Process: com.xx.xxx, PID: 15710
07-21 17:03:39.043: E/AndroidRuntime(15710): android.view.InflateException: Binary XML file line #60: Error inflating class <unknown>
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.LayoutInflater.createView(LayoutInflater.java:620)
07-21 17:03:39.043: E/AndroidRuntime(15710): at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.LayoutInflater.onCreateView(LayoutInflater.java:669)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:694)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.LayoutInflater.rInflate(LayoutInflater.java:758)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
07-21 17:03:39.043: E/AndroidRuntime(15710): at com.xx.xxx.BusinessAdapter.onCreateViewHolder(BusinessAdapter.java:106)
07-21 17:03:39.043: E/AndroidRuntime(15710): at com.xx.xxx.BusinessAdapter.onCreateViewHolder(BusinessAdapter.java:1)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:2915)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:2511)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.support.v7.widget.LinearLayoutManager$RenderState.next(LinearLayoutManager.java:1425)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:999)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:524)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1461)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:1600)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.View.layout(View.java:14817)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.ViewGroup.layout(ViewGroup.java:4631)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.View.layout(View.java:14817)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.ViewGroup.layout(ViewGroup.java:4631)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.View.layout(View.java:14817)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.ViewGroup.layout(ViewGroup.java:4631)
07-21 17:03:39.043: E/AndroidRuntime(15710): at com.android.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:374)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.View.layout(View.java:14817)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.ViewGroup.layout(ViewGroup.java:4631)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.View.layout(View.java:14817)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.ViewGroup.layout(ViewGroup.java:4631)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:1983)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1740)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:996)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5600)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.Choreographer.doCallbacks(Choreographer.java:574)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.Choreographer.doFrame(Choreographer.java:544)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.os.Handler.handleCallback(Handler.java:733)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.os.Handler.dispatchMessage(Handler.java:95)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.os.Looper.loop(Looper.java:136)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.app.ActivityThread.main(ActivityThread.java:5001)
07-21 17:03:39.043: E/AndroidRuntime(15710): at java.lang.reflect.Method.invokeNative(Native Method)
07-21 17:03:39.043: E/AndroidRuntime(15710): at java.lang.reflect.Method.invoke(Method.java:515)
07-21 17:03:39.043: E/AndroidRuntime(15710): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
07-21 17:03:39.043: E/AndroidRuntime(15710): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
07-21 17:03:39.043: E/AndroidRuntime(15710): at dalvik.system.NativeStart.main(Native Method)
07-21 17:03:39.043: E/AndroidRuntime(15710): Caused by: java.lang.reflect.InvocationTargetException
07-21 17:03:39.043: E/AndroidRuntime(15710): at java.lang.reflect.Constructor.constructNative(Native Method)
07-21 17:03:39.043: E/AndroidRuntime(15710): at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
07-21 17:03:39.043: E/AndroidRuntime(15710): at android.view.LayoutInflater.createView(LayoutInflater.java:594)
07-21 17:03:39.043: E/AndroidRuntime(15710): ... 50 more
07-21 17:03:39.043: E/AndroidRuntime(15710): Caused by: android.content.res.Resources$NotFoundException: Resource is not a Drawable (color or path): TypedValue{t=0x1/d=0x7f020075 a=-1 r=0x
回答by Gabriele Mariotti
You can do something like this:
你可以这样做:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ripple"
/>
Where the ripple.xml is:
其中ripple.xml 是:
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight">
<item android:id="@android:id/mask">
<shape android:shape="oval">
<solid android:color="?android:colorAccent" />
</shape>
</item>
</ripple>
UPDATE Material Components:
更新材料组件:
With the Material Components Libraryit is very easy to apply a ripple.
Just use the MaterialButton
and the app:rippleColor
attribute:
使用材质组件库可以很容易地应用波纹。
只需使用MaterialButton
和app:rippleColor
属性:
<com.google.android.material.button.MaterialButton
app:rippleColor="@color/my_selector"
../>
With a selector like this:
使用这样的选择器:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="..." android:color="?attr/colorOnPrimary" android:state_pressed="true"/>
<item android:alpha="..." android:color="?attr/colorOnPrimary" android:state_focused="true" android:state_hovered="true"/>
<item android:alpha="..." android:color="?attr/colorOnPrimary" android:state_focused="true"/>
<item android:alpha="..." android:color="?attr/colorOnPrimary" android:state_hovered="true"/>
<item android:alpha="..." android:color="?attr/colorOnPrimary"/>
</selector>
回答by Pacific P. Regmi
Just put ?attr/selectableItemBackground
in the background of button for API 21+ , like below:
只需放在?attr/selectableItemBackground
API 21+ 按钮的背景中,如下所示:
<Button
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="?attr/selectableItemBackground"
android:text="Button" />
回答by Pacific P. Regmi
For lollipop(API>21) make file as btn_ripple_effect.xml in drawable and put below code
对于棒棒糖(API> 21)在drawable中将文件制作为btn_ripple_effect.xml并放在下面的代码中
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:color="?android:colorAccent"
tools:targetApi="lollipop">
<item android:drawable="@color/cancel_btn_clr" /> <!-- default -->
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="?android:colorAccent" />
</shape>
</item>
</ripple>
For pre lollipop (API<21)make file as btn_ripple_effect.xml in drawable-v21 folder and put below code
对于 pre lollipop (API<21)make 文件作为 btn_ripple_effect.xml 在 drawable-v21 文件夹中并放入下面的代码
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<solid android:color="@color/colorAccent"></solid>
</shape>
</item>
<item>
<shape>
<solid android:color="@color/cancel_btn_clr"></solid>
</shape>
</item>
</selector>
回答by William
Slight addition to above answer: Note that the mask color is not used in any way.
对上述答案略加补充:请注意,不以任何方式使用遮罩颜色。
You can do more complicated things with ripple as well. For example, if you wanted a border on your ripple button you can use it like a layer-list.
你也可以用涟漪做更复杂的事情。例如,如果您想要波纹按钮上的边框,您可以像图层列表一样使用它。
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight">
<!-- Note: <ripple> acts like a layer-list -->
<item android:id="@android:id/mask">
<shape android:shape="oval">
<!-- This color is not displayed in any way -->
<solid android:color="@android:color/black" />
</shape>
</item>
<!-- This is the border -->
<item>
<shape android:shape="rectangle">
<corners android:radius="3dp"/>
<!-- Use your border color in place of #f00 -->
<stroke android:width="1dp" android:color="#f00"/>
</shape>
</item>
</ripple>
Note that the element with id @android:id/mask
is only used to show where the ripple effect will stop at. If you wanted it to cover the whole button, you could change the android:shape
to be rectangle
. You can imagine doing many more interesting things with this as well!
请注意,带有 id 的元素@android:id/mask
仅用于显示涟漪效应将停止的位置。如果您希望它覆盖整个按钮,您可以android:shape
将rectangle
. 你也可以想象用它做更多有趣的事情!
Also make sure to have a backup drawable for devices that aren't 21 yet or the app will crash on old devices.
还要确保为尚未 21 岁的设备提供备份可绘制,否则应用程序将在旧设备上崩溃。
回答by Md Imran Choudhury
The best way to use this in android:foreground, because it allows you use own background also.
在android:foreground 中使用它的最佳方法,因为它也允许您使用自己的背景。
android:foreground="?android:attr/selectableItemBackground"
android:foreground="?android:attr/selectableItemBackground"
Example:
例子:
<android.support.v7.widget.AppCompatButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:attr/selectableItemBackground"
android:background="@color/button.normal"
android:textColor="@color/white"/>
回答by Jason Marks
I was researching ripple effect as it was something I wanted to apply to a few buttons in my application and happened across your post. While your question is searching for an answer as to how to add the ripple effect using XML that was actually something I was trying to avoid as when trying to add that attribute you see it requires v21.
我正在研究涟漪效应,因为我想将它应用到我的应用程序中的几个按钮上,并且在您的帖子中发生了。虽然您的问题正在寻找有关如何使用 XML 添加涟漪效应的答案,但实际上我试图避免这种情况,因为在尝试添加该属性时,您看到它需要 v21。
If you are targeting lower than v21 than that new class extending Button (or ImageButton, etc.) will avoid complaints from the compiler.
如果您的目标低于 v21,那么扩展 Button(或 ImageButton 等)的新类将避免编译器的投诉。
As there was no explanation on how to implement the custom class above I thought I would fill in. All you need to do is create the new class and then in the XML change "Button" to "the.package.name.MyButton".
由于上面没有关于如何实现自定义类的解释,我想我会填写。您需要做的就是创建新类,然后在 XML 中将“Button”更改为“the.package.name.MyButton”。
From:
从:
<Button
android:id="@+id/Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
To:
到:
<the.package.name.MyButton
android:id="@+id/Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
That's it. Now your button when pressed will have a ripple contained within its bounds.
就是这样。现在您的按钮在按下时会在其边界内产生涟漪。
I like this approach I just wish the ripple would extend pass the bounds. For a small button this ripple effect really highlights how square or rectangular the button really is. Visually it would be more satisfying if the ripple just continued until it reached its full radius.
我喜欢这种方法,我只是希望涟漪能够超越界限。对于一个小按钮,这种涟漪效应真正突出了按钮的方形或矩形。从视觉上看,如果涟漪一直持续到它达到其完整半径,那会更令人满意。
回答by backslashN
You can add clickable
as true
and background
or foreround
as ?attr/selectableItemBackground
attributes to the view:
您可以将clickable
astrue
和background
or foreround
as?attr/selectableItemBackground
属性添加到视图中:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button"
android:clickable="true"
android:background="?attr/selectableItemBackground"
android:textColor="@android:color/white"/>
If in case your view already has a background
filled with something, you could fill your foreground
with selectableItemBackground
如果你的视图已经background
填充了一些东西,你可以foreground
用selectableItemBackground
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:background="@color/colorPrimary"
android:textColor="@android:color/white"/>