ボタンクリックでリップルアニメーションを追加しようとしています。私は以下のようにしましたが、それは21にminSdKVersionを必要とします。
ripple.xml
<ripple xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:color="?android:colorControlHighlight">
<item>
<shape Android:shape="rectangle">
<solid Android:color="?android:colorAccent" />
</shape>
</item>
</ripple>
ボタン
<com.devspark.robototextview.widget.RobotoButton
Android:id="@+id/loginButton"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:background="@drawable/ripple"
Android:text="@string/login_button" />
デザインライブラリとの下位互換性を保ちたいのです。
どのようにこれを行うことができますか?
基本波紋設定
ビュー内に含まれる波紋。Android:background="?selectableItemBackground"
ビューの境界を超えて広がる波紋:Android:background="?selectableItemBackgroundBorderless"
Javaコード内の?(attr)
xml参照を解決するためには、 をご覧ください 。
サポートライブラリ
?attr:
の代わりに?
(または?android:attr
の省略形)を使用すると、 サポートライブラリ が参照されるため、API 7に戻ることができます。画像/背景の波紋
setForeground()
またはsetBackground()
で設定した波紋でView
をFrameLayout
にラップすることです。Nick Butcherが波紋の付いたImageView
sを題材にして this を投稿しましたが、正直なところ、これ以外の方法でこれを行う明確な方法はありません。
私は以前、この質問をトピック外としてクローズすることを投票しましたが、残念ながらまだサポートライブラリには含まれていないため、実際には気が変わりました。それはおそらく将来のアップデートで現れるでしょうが、発表される時間枠はありません。
幸いなことに、すでにいくつかのカスタム実装が利用可能です。
androidの旧バージョンと互換性のあるMaterlialをテーマにしたウィジェットセットを含みます。
それで、あなたはこれらのうちの1つを試みることができるか、または他の「物質的なウィジェット」またはグーグルのためにグーグル...
私は波紋ボタンを作る簡単なクラスを作りました、私は最後にそれを必要としなかったので、それは最高ではありません、しかし、ここでそれは以下のとおりです
import Android.content.Context;
import Android.graphics.Canvas;
import Android.graphics.Color;
import Android.graphics.Paint;
import Android.os.Handler;
import Android.support.annotation.NonNull;
import Android.util.AttributeSet;
import Android.view.MotionEvent;
import Android.widget.Button;
public class RippleView extends Button
{
private float duration = 250;
private float speed = 1;
private float radius = 0;
private Paint paint = new Paint();
private float endRadius = 0;
private float rippleX = 0;
private float rippleY = 0;
private int width = 0;
private int height = 0;
private OnClickListener clickListener = null;
private Handler handler;
private int touchAction;
private RippleView thisRippleView = this;
public RippleView(Context context)
{
this(context, null, 0);
}
public RippleView(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public RippleView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
init();
}
private void init()
{
if (isInEditMode())
return;
handler = new Handler();
Paint.setStyle(Paint.Style.FILL);
Paint.setColor(Color.WHITE);
Paint.setAntiAlias(true);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
}
@Override
protected void onDraw(@NonNull Canvas canvas)
{
super.onDraw(canvas);
if(radius > 0 && radius < endRadius)
{
canvas.drawCircle(rippleX, rippleY, radius, Paint);
if(touchAction == MotionEvent.ACTION_UP)
invalidate();
}
}
@Override
public boolean onTouchEvent(@NonNull MotionEvent event)
{
rippleX = event.getX();
rippleY = event.getY();
switch(event.getAction())
{
case MotionEvent.ACTION_UP:
{
getParent().requestDisallowInterceptTouchEvent(false);
touchAction = MotionEvent.ACTION_UP;
radius = 1;
endRadius = Math.max(Math.max(Math.max(width - rippleX, rippleX), rippleY), height - rippleY);
speed = endRadius / duration * 10;
handler.postDelayed(new Runnable()
{
@Override
public void run()
{
if(radius < endRadius)
{
radius += speed;
Paint.setAlpha(90 - (int) (radius / endRadius * 90));
handler.postDelayed(this, 1);
}
else
{
clickListener.onClick(thisRippleView);
}
}
}, 10);
invalidate();
break;
}
case MotionEvent.ACTION_CANCEL:
{
getParent().requestDisallowInterceptTouchEvent(false);
touchAction = MotionEvent.ACTION_CANCEL;
radius = 0;
invalidate();
break;
}
case MotionEvent.ACTION_DOWN:
{
getParent().requestDisallowInterceptTouchEvent(true);
touchAction = MotionEvent.ACTION_UP;
endRadius = Math.max(Math.max(Math.max(width - rippleX, rippleX), rippleY), height - rippleY);
Paint.setAlpha(90);
radius = endRadius/4;
invalidate();
return true;
}
case MotionEvent.ACTION_MOVE:
{
if(rippleX < 0 || rippleX > width || rippleY < 0 || rippleY > height)
{
getParent().requestDisallowInterceptTouchEvent(false);
touchAction = MotionEvent.ACTION_CANCEL;
radius = 0;
invalidate();
break;
}
else
{
touchAction = MotionEvent.ACTION_MOVE;
invalidate();
return true;
}
}
}
return false;
}
@Override
public void setOnClickListener(OnClickListener l)
{
clickListener = l;
}
}
EDIT
多くの人がこのようなものを探しているので、他のビューに波及効果を持たせることができるクラスを作りました。
import Android.content.Context;
import Android.graphics.Canvas;
import Android.graphics.Paint;
import Android.os.Handler;
import Android.support.annotation.NonNull;
import Android.util.AttributeSet;
import Android.view.MotionEvent;
import Android.view.View;
import Android.view.ViewGroup;
import Android.widget.FrameLayout;
public class RippleViewCreator extends FrameLayout
{
private float duration = 150;
private int frameRate = 15;
private float speed = 1;
private float radius = 0;
private Paint paint = new Paint();
private float endRadius = 0;
private float rippleX = 0;
private float rippleY = 0;
private int width = 0;
private int height = 0;
private Handler handler = new Handler();
private int touchAction;
public RippleViewCreator(Context context)
{
this(context, null, 0);
}
public RippleViewCreator(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public RippleViewCreator(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
init();
}
private void init()
{
if (isInEditMode())
return;
Paint.setStyle(Paint.Style.FILL);
Paint.setColor(getResources().getColor(R.color.control_highlight_color));
Paint.setAntiAlias(true);
setWillNotDraw(true);
setDrawingCacheEnabled(true);
setClickable(true);
}
public static void addRippleToView(View v)
{
ViewGroup parent = (ViewGroup)v.getParent();
int index = -1;
if(parent != null)
{
index = parent.indexOfChild(v);
parent.removeView(v);
}
RippleViewCreator rippleViewCreator = new RippleViewCreator(v.getContext());
rippleViewCreator.setLayoutParams(v.getLayoutParams());
if(index == -1)
parent.addView(rippleViewCreator, index);
else
parent.addView(rippleViewCreator);
rippleViewCreator.addView(v);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
}
@Override
protected void dispatchDraw(@NonNull Canvas canvas)
{
super.dispatchDraw(canvas);
if(radius > 0 && radius < endRadius)
{
canvas.drawCircle(rippleX, rippleY, radius, Paint);
if(touchAction == MotionEvent.ACTION_UP)
invalidate();
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event)
{
return true;
}
@Override
public boolean onTouchEvent(@NonNull MotionEvent event)
{
rippleX = event.getX();
rippleY = event.getY();
touchAction = event.getAction();
switch(event.getAction())
{
case MotionEvent.ACTION_UP:
{
getParent().requestDisallowInterceptTouchEvent(false);
radius = 1;
endRadius = Math.max(Math.max(Math.max(width - rippleX, rippleX), rippleY), height - rippleY);
speed = endRadius / duration * frameRate;
handler.postDelayed(new Runnable()
{
@Override
public void run()
{
if(radius < endRadius)
{
radius += speed;
Paint.setAlpha(90 - (int) (radius / endRadius * 90));
handler.postDelayed(this, frameRate);
}
else if(getChildAt(0) != null)
{
getChildAt(0).performClick();
}
}
}, frameRate);
break;
}
case MotionEvent.ACTION_CANCEL:
{
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
case MotionEvent.ACTION_DOWN:
{
getParent().requestDisallowInterceptTouchEvent(true);
endRadius = Math.max(Math.max(Math.max(width - rippleX, rippleX), rippleY), height - rippleY);
Paint.setAlpha(90);
radius = endRadius/3;
invalidate();
return true;
}
case MotionEvent.ACTION_MOVE:
{
if(rippleX < 0 || rippleX > width || rippleY < 0 || rippleY > height)
{
getParent().requestDisallowInterceptTouchEvent(false);
touchAction = MotionEvent.ACTION_CANCEL;
break;
}
else
{
invalidate();
return true;
}
}
}
invalidate();
return false;
}
@Override
public final void addView(@NonNull View child, int index, ViewGroup.LayoutParams params)
{
//limit one view
if (getChildCount() > 0)
{
throw new IllegalStateException(this.getClass().toString()+" can only have one child.");
}
super.addView(child, index, params);
}
}
時々あなたはカスタム背景を持っています、その場合より良い解決策はAndroid:foreground="?selectableItemBackground"
を使うことです
とても簡単です;-)
最初に、古いAPIバージョン用と新しいバージョン用に2つのドロアブルファイルを作成する必要があります。もちろん!あなたが最新のapiバージョンのAndroidスタジオ用のドロアブルファイルを作成するなら、あなたは自動的に古いものを作成することを勧めます。そして最後にこのdrawableをあなたの背景ビューに設定します。
新しいapiバージョン用のサンプルドロアブル(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:colorControlHighlight">
<item>
<shape Android:shape="rectangle">
<solid Android:color="@color/colorPrimary" />
<corners Android:radius="@dimen/round_corner" />
</shape>
</item>
</ripple>
旧バージョンのAPI用に描画可能なサンプル(res/drawable/ripple.xml)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:shape="rectangle">
<solid Android:color="@color/colorPrimary" />
<corners Android:radius="@dimen/round_corner" />
</shape>
リップルドロアブルの詳細については、こちらをご覧ください。 https://developer.Android.com/reference/Android/graphics/drawable/RippleDrawable.html
時にはこの行をレイアウトやコンポーネントに使用することができます。
Android:background="?attr/selectableItemBackground"
のように。
<RelativeLayout
Android:id="@+id/relative_ticket_checkin"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:layout_weight="1"
Android:background="?attr/selectableItemBackground">