版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android實(shí)現(xiàn)RippleEffect水波紋效果
最近看到360、UC、網(wǎng)易新聞客戶(hù)端都應(yīng)用了水波紋效果,就在私下里也研究了一下,參照GIT上大神的分享,自己也跟著做了一個(gè)示例,下面先看效果:1.RippleEffect核心實(shí)現(xiàn)類(lèi)package
com.example.RippleEffect;
import
android.content.Context;
import
android.content.res.TypedArray;
import
android.graphics.Bitmap;
import
android.graphics.Canvas;
import
android.graphics.Color;
import
android.graphics.Paint;
import
android.graphics.PorterDuff;
import
android.graphics.PorterDuffXfermode;
import
android.graphics.Rect;
import
android.os.Handler;
import
android.support.annotation.ColorRes;
import
android.util.AttributeSet;
import
android.view.GestureDetector;
import
android.view.MotionEvent;
import
android.view.animation.Animation;
import
android.view.animation.ScaleAnimation;
import
android.widget.AdapterView;
import
android.widget.RelativeLayout;
/**
*
RippleView
custom
layout
*
*
Custom
Layout
that
allows
to
use
Ripple
UI
pattern
above
API
21
*/
public
class
RippleView
extends
RelativeLayout
{
private
int
WIDTH;
private
int
HEIGHT;
private
int
frameRate
=
10;
private
int
rippleDuration
=
400;
private
int
rippleAlpha
=
90;
private
Handler
canvasHandler;
private
float
radiusMax
=
0;
private
boolean
animationRunning
=
false;
private
int
timer
=
0;
private
int
timerEmpty
=
0;
private
int
durationEmpty
=
-1;
private
float
x
=
-1;
private
float
y
=
-1;
private
int
zoomDuration;
private
float
zoomScale;
private
ScaleAnimation
scaleAnimation;
private
Boolean
hasToZoom;
private
Boolean
isCentered;
private
Integer
rippleType;
private
Paint
paint;
private
Bitmap
originBitmap;
private
int
rippleColor;
private
int
ripplePadding;
private
GestureDetector
gestureDetector;
private
final
Runnable
runnable
=
new
Runnable()
{
@Override
public
void
run()
{
invalidate();
}
};
private
OnRippleCompleteListener
onCompletionListener;
public
RippleView(Context
context)
{
super(context);
}
public
RippleView(Context
context,
AttributeSet
attrs)
{
super(context,
attrs);
init(context,
attrs);
}
public
RippleView(Context
context,
AttributeSet
attrs,
int
defStyle)
{
super(context,
attrs,
defStyle);
init(context,
attrs);
}
/**
*
Method
that
initializes
all
fields
and
sets
listeners
*
*
@param
context
Context
used
to
create
this
view
*
@param
attrs
Attribute
used
to
initialize
fields
*/
private
void
init(final
Context
context,
final
AttributeSet
attrs)
{
if
(isInEditMode())
return;
final
TypedArray
typedArray
=
context.obtainStyledAttributes(attrs,
R.styleable.RippleView);
rippleColor
=
typedArray.getColor(R.styleable.RippleView_rv_color,
getResources().getColor(R.color.rippelColor));
rippleType
=
typedArray.getInt(R.styleable.RippleView_rv_type,
0);
hasToZoom
=
typedArray.getBoolean(R.styleable.RippleView_rv_zoom,
false);
isCentered
=
typedArray.getBoolean(R.styleable.RippleView_rv_centered,
false);
rippleDuration
=
typedArray.getInteger(R.styleable.RippleView_rv_rippleDuration,
rippleDuration);
frameRate
=
typedArray.getInteger(R.styleable.RippleView_rv_framerate,
frameRate);
rippleAlpha
=
typedArray.getInteger(R.styleable.RippleView_rv_alpha,
rippleAlpha);
ripplePadding
=
typedArray.getDimensionPixelSize(R.styleable.RippleView_rv_ripplePadding,
0);
canvasHandler
=
new
Handler();
zoomScale
=
typedArray.getFloat(R.styleable.RippleView_rv_zoomScale,
1.03f);
zoomDuration
=
typedArray.getInt(R.styleable.RippleView_rv_zoomDuration,
200);
typedArray.recycle();
paint
=
new
Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(rippleColor);
paint.setAlpha(rippleAlpha);
this.setWillNotDraw(false);
gestureDetector
=
new
GestureDetector(context,
new
GestureDetector.SimpleOnGestureListener()
{
@Override
public
void
onLongPress(MotionEvent
event)
{
super.onLongPress(event);
animateRipple(event);
sendClickEvent(true);
}
@Override
public
boolean
onSingleTapConfirmed(MotionEvent
e)
{
return
true;
}
@Override
public
boolean
onSingleTapUp(MotionEvent
e)
{
return
true;
}
});
this.setDrawingCacheEnabled(true);
this.setClickable(true);
}
@Override
public
void
draw(Canvas
canvas)
{
super.draw(canvas);
if
(animationRunning)
{
if
(rippleDuration
<=
timer
*
frameRate)
{
animationRunning
=
false;
timer
=
0;
durationEmpty
=
-1;
timerEmpty
=
0;
canvas.restore();
invalidate();
if
(onCompletionListener
!=
null)
onCompletionListener.onComplete(this);
return;
}
else
canvasHandler.postDelayed(runnable,
frameRate);
if
(timer
==
0)
canvas.save();
canvas.drawCircle(x,
y,
(radiusMax
*
(((float)
timer
*
frameRate)
/
rippleDuration)),
paint);
paint.setColor(Color.parseColor("#ffff4444"));
if
(rippleType
==
1
&&
originBitmap
!=
null
&&
(((float)
timer
*
frameRate)
/
rippleDuration)
>
0.4f)
{
if
(durationEmpty
==
-1)
durationEmpty
=
rippleDuration
-
timer
*
frameRate;
timerEmpty++;
final
Bitmap
tmpBitmap
=
getCircleBitmap((int)
((radiusMax)
*
(((float)
timerEmpty
*
frameRate)
/
(durationEmpty))));
canvas.drawBitmap(tmpBitmap,
0,
0,
paint);
tmpBitmap.recycle();
}
paint.setColor(rippleColor);
if
(rippleType
==
1)
{
if
((((float)
timer
*
frameRate)
/
rippleDuration)
>
0.6f)
paint.setAlpha((int)
(rippleAlpha
-
((rippleAlpha)
*
(((float)
timerEmpty
*
frameRate)
/
(durationEmpty)))));
else
paint.setAlpha(rippleAlpha);
}
else
paint.setAlpha((int)
(rippleAlpha
-
((rippleAlpha)
*
(((float)
timer
*
frameRate)
/
rippleDuration))));
timer++;
}
}
@Override
protected
void
onSizeChanged(int
w,
int
h,
int
oldw,
int
oldh)
{
super.onSizeChanged(w,
h,
oldw,
oldh);
WIDTH
=
w;
HEIGHT
=
h;
scaleAnimation
=
new
ScaleAnimation(1.0f,
zoomScale,
1.0f,
zoomScale,
w
/
2,
h
/
2);
scaleAnimation.setDuration(zoomDuration);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleAnimation.setRepeatCount(1);
}
/**
*
Launch
Ripple
animation
for
the
current
view
with
a
MotionEvent
*
*
@param
event
MotionEvent
registered
by
the
Ripple
gesture
listener
*/
public
void
animateRipple(MotionEvent
event)
{
createAnimation(event.getX(),
event.getY());
}
/**
*
Launch
Ripple
animation
for
the
current
view
centered
at
x
and
y
position
*
*
@param
x
Horizontal
position
of
the
ripple
center
*
@param
y
Vertical
position
of
the
ripple
center
*/
public
void
animateRipple(final
float
x,
final
float
y)
{
createAnimation(x,
y);
}
/**
*
Create
Ripple
animation
centered
at
x,
y
*
*
@param
x
Horizontal
position
of
the
ripple
center
*
@param
y
Vertical
position
of
the
ripple
center
*/
private
void
createAnimation(final
float
x,
final
float
y)
{
if
(this.isEnabled()
&&
!animationRunning)
{
if
(hasToZoom)
this.startAnimation(scaleAnimation);
radiusMax
=
Math.max(WIDTH,
HEIGHT);
if
(rippleType
!=
2)
radiusMax
/=
2;
radiusMax
-=
ripplePadding;
if
(isCentered
||
rippleType
==
1)
{
this.x
=
getMeasuredWidth()
/
2;
this.y
=
getMeasuredHeight()
/
2;
}
else
{
this.x
=
x;
this.y
=
y;
}
animationRunning
=
true;
if
(rippleType
==
1
&&
originBitmap
==
null)
originBitmap
=
getDrawingCache(true);
invalidate();
}
}
@Override
public
boolean
onTouchEvent(MotionEvent
event)
{
if
(gestureDetector.onTouchEvent(event))
{
animateRipple(event);
sendClickEvent(false);
}
return
super.onTouchEvent(event);
}
@Override
public
boolean
onInterceptTouchEvent(MotionEvent
event)
{
this.onTouchEvent(event);
return
super.onInterceptTouchEvent(event);
}
/**
*
Send
a
click
event
if
parent
view
is
a
Listview
instance
*
*
@param
isLongClick
Is
the
event
a
long
click
?
*/
private
void
sendClickEvent(final
Boolean
isLongClick)
{
if
(getParent()
instanceof
AdapterView)
{
final
AdapterView
adapterView
=
(AdapterView)
getParent();
final
int
position
=
adapterView.getPositionForView(this);
final
long
id
=
adapterView.getItemIdAtPosition(position);
if
(isLongClick)
{
if
(adapterView.getOnItemLongClickListener()
!=
null)
adapterView.getOnItemLongClickListener().onItemLongClick(adapterView,
this,
position,
id);
}
else
{
if
(adapterView.getOnItemClickListener()
!=
null)
adapterView.getOnItemClickListener().onItemClick(adapterView,
this,
position,
id);
}
}
}
private
Bitmap
getCircleBitmap(final
int
radius)
{
final
Bitmap
output
=
Bitmap.createBitmap(originBitmap.getWidth(),
originBitmap.getHeight(),
Bitmap.Config.ARGB_8888);
final
Canvas
canvas
=
new
Canvas(output);
final
Paint
paint
=
new
Paint();
final
Rect
rect
=
new
Rect((int)(x
-
radius),
(int)(y
-
radius),
(int)(x
+
radius),
(int)(y
+
radius));
paint.setAntiAlias(true);
canvas.drawARGB(0,
0,
0,
0);
canvas.drawCircle(x,
y,
radius,
paint);
paint.setXfermode(new
PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(originBitmap,
rect,
rect,
paint);
return
output;
}
/**
*
Set
Ripple
color,
default
is
#FFFFFF
*
*
@param
rippleColor
New
color
resource
*/
@ColorRes
public
void
setRippleColor(int
rippleColor)
{
this.rippleColor
=
getResources().getColor(rippleColor);
}
public
int
getRippleColor()
{
return
rippleColor;
}
public
RippleType
getRippleType()
{
return
RippleType.values()[rippleType];
}
/**
*
Set
Ripple
type,
default
is
RippleType.SIMPLE
*
*
@param
rippleType
New
Ripple
type
for
next
animation
*/
public
void
setRippleType(final
RippleType
rippleType)
{
this.rippleType
=
rippleType.ordinal();
}
public
Boolean
isCentered()
{
return
isCentered;
}
/**
*
Set
if
ripple
animation
has
to
be
centered
in
its
parent
view
or
not,
default
is
False
*
*
@param
isCentered
*/
public
void
setCentered(final
Boolean
isCentered)
{
this.isCentered
=
isCentered;
}
public
int
getRipplePadding()
{
return
ripplePadding;
}
/**
*
Set
Ripple
padding
if
you
want
to
avoid
some
graphic
glitch
*
*
@param
ripplePadding
New
Ripple
padding
in
pixel,
default
is
0px
*/
public
void
setRipplePadding(int
ripplePadding)
{
this.ripplePadding
=
ripplePadding;
}
public
Boolean
isZooming()
{
return
hasToZoom;
}
/**
*
At
the
end
of
Ripple
effect,
the
child
views
has
to
zoom
*
*
@param
hasToZoom
Do
the
child
views
have
to
zoom
?
default
is
False
*/
public
void
setZooming(Boolean
hasToZoom)
{
this.hasToZoom
=
hasToZoom;
}
public
float
getZoomScale()
{
return
zoomScale;
}
/**
*
Scale
of
the
end
animation
*
*
@param
zoomScale
Value
of
scale
animation,
default
is
1.03f
*/
public
void
setZoomScale(float
zoomScale)
{
this.zoomScale
=
zoomScale;
}
public
int
getZoomDuration()
{
return
zoomDuration;
}
/**
*
Duration
of
the
ending
animation
in
ms
*
*
@param
zoomDuration
Duration,
default
is
200ms
*/
public
void
setZoomDuration(int
zoomDuration)
{
this.zoomDuration
=
zoomDuration;
}
public
int
getRippleDuration()
{
return
rippleDuration;
}
/**
*
Duration
of
the
Ripple
animation
in
ms
*
*
@param
rippleDuration
Duration,
default
is
400ms
*/
public
void
setRippleDuration(int
rippleDuration)
{
this.rippleDuration
=
rippleDuration;
}
public
int
getFrameRate()
{
return
frameRate;
}
/**
*
Set
framerate
for
Ripple
animation
*
*
@param
frameRate
New
framerate
value,
default
is
10
*/
public
void
setFrameRate(int
frameRate)
{
this.frameRate
=
frameRate;
}
public
int
getRippleAlpha()
{
return
rippleAlpha;
}
/**
*
Set
alpha
for
ripple
effect
color
*
*
@param
rippleAlpha
Alpha
value
between
0
and
255,
default
is
90
*/
public
void
setRippleAlpha(int
rippleAlpha)
{
this.rippleAlpha
=
rippleAlpha;
}
public
void
setOnRippleCompleteListener(OnRippleCompleteListener
listener)
{
this.onCompletionListener
=
listener;
}
/**
*
Defines
a
callback
called
at
the
end
of
the
Ripple
effect
*/
public
interface
OnRippleCompleteListener
{
void
onComplete(RippleView
rippleView);
}
public
enum
RippleType
{
SIMPLE(0),
DOUBLE(1),
RECTANGLE(2);
int
type;
RippleType(int
type)
{
this.type
=
type;
}
}
}2.自定義屬性<?xml
version="1.0"
encoding="utf-8"?>
<resources>
<declare-styleable
name="RippleView">rv_zoomDuration
<attr
name="rv_alpha"
format="integer"
/>
<attr
name="rv_framerate"
format="integer"/>
<attr
name="rv_rippleDuration"
format="integer"/>
<attr
name="rv_zoomDuration"
format="integer"
/>
<attr
name="rv_color"
format="color"
/>
<attr
name="rv_centered"
format="boolean"
/>
<attr
name="rv_type"
format="enum">
<enum
name="simpleRipple"
value="0"/>
<enum
name="doubleRipple"
value="1"/>
<enum
name="rectangle"
value="2"
/>
</attr>
<attr
name="rv_ripplePadding"
format="dimension"
/>
<attr
name="rv_zoom"
format="boolean"
/>
<attr
name="rv_zoomScale"
format="float"
/>
</declare-styleable>
</resources>3.主布局文件<LinearLayout
xmlns:android="/apk/res/android"
xmlns:ripple="/apk/res-auto"
xmlns:tools="/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
>
<!--
1
rv_centered="true"
rv_type="simpleRipple"
-->
<com.example.RippleEffect.RippleView
android:id="@+id/more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
ripple:rv_centered="true"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="#BAC9FF"
android:padding="15dp"
android:src="@drawable/ic_launcher"
/>
</com.example.RippleEffect.RippleView>
<!--
2
rv_centered="false"
rv_type="simpleRipple"
-->
<com.example.RippleEffect.RippleView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
ripple:rv_centered="false"
ripple:rv_type="simpleRipple"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="#BAC9FF"
android:padding="15dp"
android:src="@drawable/ic_launcher"
/>
</com.example.RippleEffect.RippleView>
<!--
3
rv_type="doubleRipple"
-->
<com.example.RippleEffect.RippleView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
ripple:rv_type="doubleRipple"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="#BAC9FF"
android:padding="15dp"
android:src="@drawable/ic_launcher"
/>
</com.example.RippleEffect.RippleView>
<!--
4
rv_type="rectangle"
-->
<com.example.RippleEffect.RippleView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
ripple:rv_type="doubleRipple"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="#BAC9FF"
android:padding="15dp"
android:src="@drawable/ic_launcher"
/>
</com.example.RippleEffect.RippleView>
<!--
5
rv_zoom
="true"
rv_ripplePadding
="20dp"
ripple:rv_zoomScale="1.25"
-->
<com.example.RippleEffect.RippleView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
ripple:rv_centered="false"
ripple:rv_color="#D91615"
ripple:rv_rippleDuration="2000"
ripple:rv_ripplePadding="20dp"
ripple:rv_zoom="true"
ripple:rv_zoomDuration="200"
ripple:rv_zoomScale="1.25"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="#BAC9FF"
android:padding="15dp"
android:src="@drawable/ic_launcher"
/>
</com.example.RippleEffect.RippleView>
<!--
6
rv_type="simpleRipple"
rv_alpha="10"
rv_framerate="100"
-->
<com.example.RippleEffect.RippleView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
ripple:rv_alpha="200"
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2008年公務(wù)員國(guó)考《申論》真題卷及答案
- 有關(guān)電話(huà)銷(xiāo)售月度工作總結(jié)模板
- 工程技術(shù)人員年度總結(jié)
- 消防安全知識(shí)培訓(xùn)總結(jié)(17篇)
- 石油化工企業(yè)現(xiàn)場(chǎng)安全檢查表
- 浙江省杭州“六縣九?!甭?lián)盟2023-2024學(xué)年高一上學(xué)期期中地理試題 含解析
- 6涵洞工程資料清單目錄(改)
- 推動(dòng)思政小課堂與社會(huì)大課堂相結(jié)合的大思政課探索
- 寶雞文理學(xué)院《西方史學(xué)史》2022-2023學(xué)年第一學(xué)期期末試卷
- 墻紙接縫滾筒手工具項(xiàng)目評(píng)價(jià)分析報(bào)告
- BCG矩陣圖文詳解
- PLC順序啟??刂凭幊?A11)
- 某汽車(chē)制造公司12萬(wàn)輛整車(chē)項(xiàng)目安全預(yù)評(píng)價(jià)報(bào)告
- 紅色溫馨生日快樂(lè)祝福相冊(cè)PPT模板課件
- 中小學(xué)落實(shí)“雙減政策”,加強(qiáng)“五項(xiàng)管理”資料(全套完整)
- 光纜通信線(xiàn)路工程的設(shè)計(jì)畢業(yè)論文
- 3歲兒童涂色畫(huà)簡(jiǎn)筆畫(huà)打印版
- 《指南》健康領(lǐng)域的解讀
- 第二語(yǔ)言學(xué)習(xí)者語(yǔ)言系統(tǒng)變異研究
- “通才”教育與“專(zhuān)才”教育的取向
- 注射用人臍帶間充質(zhì)干細(xì)胞質(zhì)量研究資料及文獻(xiàn)資料
評(píng)論
0/150
提交評(píng)論