지원 라이브러리를 이용하여 리플 애니메이션을 달성하는 방법은?
버튼 클릭 시 리플 애니메이션을 추가하려고 합니다.아래와 같이 했는데 minSdK 버전이 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"
여기에서 해결 방법을 살펴봅니다.
?(attr)
자바 코드의 xml 참조.
지원 라이브러리
- 사용.
?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와 호환되는 Material 테마 위젯 세트 포함:
그래서 당신은 이것들 중 하나를 시도하거나 구글에서 다른 "소재 위젯"을 시도할 수 있습니다.
리플 버튼을 만드는 간단한 클래스를 만들었습니다. 결국 필요가 없었기 때문에 최고는 아니지만 여기 있습니다.
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"
갱신하다
둥근 모서리와 사용자 정의 배경으로 리플 효과를 원하는 경우 다음을 사용할 수 있습니다.
background_ ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/ripple_color">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners android:radius="5dp" />
</shape>
</item>
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners android:radius="5dp" />
</shape>
</item>
layout.xml
<Button
android:id="@+id/btn_play"
android:background="@drawable/background_ripple"
app:backgroundTint="@color/colorPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/play_now" />
API >= 21에서 사용하였습니다.
아주 간단해요 ;-)
먼저 오래된 api 버전과 최신 api 버전의 api 버전의 drawable 파일을 두 개 만들어야 합니다. 물론이죠! 최신 api 버전 안드로이드 스튜디오의 drawable 파일을 생성하면 오래된 api 버전을 자동으로 생성할 것을 제안하고 마지막으로 이 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">
언급URL : https://stackoverflow.com/questions/26604134/how-to-achieve-ripple-animation-using-support-library
'programing' 카테고리의 다른 글
Angularjs [$rootScope:inprog] 진행 중 오류 발생 (0) | 2023.11.04 |
---|---|
memcmp가 루프 체크보다 훨씬 빠른 이유는 무엇입니까? (0) | 2023.11.04 |
Swagger / springfox 자동으로 응답 예제 생성 (0) | 2023.11.04 |
응용 프로그램 개발자가 저지른 데이터베이스 개발 실수 (0) | 2023.10.30 |
angular2 특정 요소에서 수동으로 발사 클릭 이벤트 (0) | 2023.10.30 |