ボタンやその他のビューに「リップル効果-タッチフィードバック」を実装する方法を理解しようとしています。 SOのリップルタッチ効果に関連する質問を見て、その洞察を得ました。この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);
}
}
しかし、私はXMLアプローチを使用したいと思います。どうすればこれを達成できますか? this と this を見てきましたが、まだスタイルに慣れていないので、波及効果を達成するのは難しいと思っています。
次の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" />
このボタンのリップル効果を取得するにはどうすればよいですか。誰かが私を導くことができれば、私は感謝します。
[編集]上記のリンクのいずれかに記載されているとおり、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>
ボタンの背景として波紋を追加しました。ここにボタンの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" />
アプリケーションを実行すると、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
次のようなことができます:
<Button
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:background="@drawable/ripple"
/>
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>
以下のように、API 21+のボタンの背景に?attr/selectableItemBackground
を配置するだけです。
<Button
Android:layout_width="match_parent"
Android:layout_height="70dp"
Android:background="?attr/selectableItemBackground"
Android:text="Button" />
Lollipop(API> 21)の場合、Drawnableで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>
Pre Lollipop(API <21)の場合、drawable-v21フォルダーにbtn_ripple_effect.xmlとしてファイルを作成し、以下のコードを配置します
<?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>
上記の回答へのわずかな追加:マスクの色は決して使用されないことに注意してください。
リップルを使用して、より複雑なこともできます。たとえば、リップルボタンに境界線が必要な場合は、レイヤーリストのように使用できます。
<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>
Id @Android:id/mask
の要素は、波及効果がどこで止まるかを示すためにのみ使用されることに注意してください。ボタン全体をカバーしたい場合は、Android:shape
をrectangle
に変更できます。これでもっと面白いことをすることも想像できます!
また、まだ21でないデバイス用のバックアップ可能なドローアブルを用意してください。そうしないと、古いデバイスでアプリがクラッシュします。
Android:foregroundでこれを使用する最良の方法は、独自のバックグラウンドも使用できるためです。
Android:foreground = "?android:attr/selectableItemBackground"
例:
<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"/>
リップル効果は、アプリケーションのいくつかのボタンに適用したいものであり、投稿全体で発生したため、調査していました。あなたの質問はXMLを使用してリップル効果を追加する方法についての答えを探していますが、実際にはその属性を追加しようとするとv21が必要になるので避けたいと思っていました。
V21よりも低いターゲットを使用している場合、Button(またはImageButtonなど)を拡張する新しいクラスは、コンパイラからの苦情を回避します。
上記のカスタムクラスの実装方法に関する説明がなかったので、記入するつもりでした。新しいクラスを作成し、XMLで「ボタン」を「the.package.name.MyButton」に変更するだけです。
から:
<Button
Android:id="@+id/Button"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content" />
に:
<the.package.name.MyButton
Android:id="@+id/Button"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content" />
それでおしまい。ボタンを押すと、境界内に波紋が含まれるようになります。
私はこのアプローチが好きで、波紋が境界を越えて伸びることを望みます。小さなボタンの場合、この波及効果はボタンが実際にどれだけ正方形または長方形であるかを強調します。視覚的には、さざ波が完全な半径に達するまで継続した場合、より満足のいくものになります。
clickable
をtrue
として、およびbackground
またはforeround
を?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"/>
ビューに既に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"/>