【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android 實(shí)現(xiàn)RippleEffect水波紋效果_第1頁(yè)
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android 實(shí)現(xiàn)RippleEffect水波紋效果_第2頁(yè)
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android 實(shí)現(xiàn)RippleEffect水波紋效果_第3頁(yè)
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android 實(shí)現(xiàn)RippleEffect水波紋效果_第4頁(yè)
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android 實(shí)現(xiàn)RippleEffect水波紋效果_第5頁(yè)
已閱讀5頁(yè),還剩20頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論