버튼 클릭시 리플 애니메이션을 추가하려고합니다. 나는 아래를 좋아했지만 minSdKVersion은 21이 필요합니다.
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 코드에서 XML 참조 를 해결 하려면 여기 를 살펴보십시오
?(attr)
.
지원 라이브러리
- 사용
?attr:
(또는?
대신 속기)?android:attr
참조합니다 지원 라이브러리를 , 그래서 API 7 사용할 수 돌아왔다.
이미지 / 배경이있는 잔물결
- 이미지 또는 배경과 오버레이 리플을 갖기 위해 가장 쉬운 해결책은 또는로 설정된 리플을 사용하여 를 감싸는 것
View
입니다 .FrameLayout
setForeground()
setBackground()
솔직히 그렇지 않으면 이것을하는 확실한 방법이 없습니다.
답변
나는 이전 에이 질문을 주 제외로 닫으려고 투표했지만 실제로 불행히도 아직 지원 라이브러리의 일부가 아닌 매우 훌륭한 시각 효과이기 때문에 마음을 바꿨습니다. 향후 업데이트에 표시 될 가능성이 높지만 발표 된 시간은 없습니다.
다행히도 사용 가능한 사용자 지정 구현은 거의 없습니다.
- https://github.com/traex/RippleEffect
- https://github.com/balysv/material-ripple
- https://github.com/siriscac/RippleView
- https://github.com/ozodrukh/RippleDrawable
이전 버전의 Android와 호환되는 Materlial 테마 위젯 세트 포함 :
그래서 당신은 다른 “재료 위젯”등을 위해 이것들 또는 구글 중 하나를 시도 할 수 있습니다 …
답변
리플 버튼을 만드는 간단한 클래스를 만들었습니다. 결국에는 필요하지 않았으므로 최고는 아니지만 여기에 있습니다.
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;
}
}
편집하다
많은 사람들이 이와 같은 것을 찾고 있기 때문에 다른 뷰가 파급 효과를 갖도록 클래스를 만들었습니다.
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 버전 용 파일 하나와 최신 버전 용 파일 하나를 만들어야합니다. 물론! 최신 API 버전 Android Studio 용 드로어 블 파일을 만들면 오래된 파일을 자동으로 만들 것을 제안합니다. 마지막 으로이 드로어 블을 배경보기로 설정하십시오.
새 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">