一、動(dòng)畫效果
? 1.動(dòng)效描述
? 2.關(guān)鍵點(diǎn)
? 3.實(shí)現(xiàn)方式 ?
二觉壶、LinearGradient簡(jiǎn)介
三、代碼功能實(shí)現(xiàn)
?1.繪制閃光
? 2.兩道閃光順序出現(xiàn)
一、動(dòng)畫效果
1.動(dòng)效描述
實(shí)現(xiàn)的動(dòng)畫效果主要就是纵菌,一束白光從圖片或者文字上閃過(guò),這束光由兩道光組成休涤,具體細(xì)節(jié)描述如下:
2.關(guān)鍵點(diǎn)
- 兩道閃光咱圆,傾斜-330°,透明度功氨、寬度不一樣
- 兩道閃光按順序先后出現(xiàn)
3.實(shí)現(xiàn)方式
可以用FrameLayout序苏,在背景圖或文字上兩個(gè)View,然后對(duì)這兩個(gè)view做動(dòng)畫捷凄,控制時(shí)間忱详、旋轉(zhuǎn)角度等。這里主要講怎么用LinearGradient來(lái)實(shí)現(xiàn)兩道光閃過(guò)的動(dòng)畫效果跺涤。
二匈睁、LinearGradient簡(jiǎn)介
講實(shí)現(xiàn)之前先認(rèn)識(shí)下主角LinearGradient。LinearGradient作用是實(shí)現(xiàn)某一區(qū)域內(nèi)顏色的線性漸變效果桶错,網(wǎng)上相關(guān)資料也很多航唆,這里就簡(jiǎn)單介紹下常用的構(gòu)造函數(shù):
public LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions,Shader.TileMode tile)
注:Android中計(jì)算x,y坐標(biāo)都是以屏幕左上角為原點(diǎn)院刁,向右為x+糯钙,向下為y+
- float x0:漸變起始點(diǎn)x坐標(biāo)
- float y0:漸變起始點(diǎn)y坐標(biāo)
- float x1:漸變結(jié)束點(diǎn)x坐標(biāo)
- float y1:漸變結(jié)束點(diǎn)y坐標(biāo)
- int[] colors:顏色 的int 數(shù)組
- float[] positions: 相對(duì)位置的顏色數(shù)組,可為null退腥,若為null超营,顏色沿漸變線均勻分布
- Shader.TileMode tile: 渲染器平鋪模式
Shader.TileMode有3種參數(shù)可供選擇,分別為CLAMP阅虫、REPEAT和MIRROR:
- CLAMP的作用是如果渲染器超出原始邊界范圍演闭,則會(huì)復(fù)制邊緣顏色對(duì)超出范圍的區(qū)域進(jìn)行著色
- REPEAT的作用是在橫向和縱向上以平鋪的形式重復(fù)渲染位圖
- MIRROR的作用是在橫向和縱向上以鏡像的方式重復(fù)渲染位圖
代碼功能實(shí)現(xiàn)
1.繪制閃光
閃光的繪制由LinearGradient完成,通過(guò)改變其構(gòu)造函數(shù)各個(gè)參數(shù)值颓帝,就能繪制出不同的光效果
(1)閃光傾斜-330°
調(diào)節(jié)漸變閃光的傾斜角度米碰,需用LinearGradient構(gòu)造函數(shù)中的x0窝革,y0,x1吕座,y1參數(shù)虐译,即調(diào)節(jié)漸變的起始點(diǎn),更多用法可參考Android中的LinearGradient吴趴。所以我們將這個(gè)4個(gè)參數(shù)設(shè)置成如下:
(2)兩道閃光
這里主要用到LinearGradient構(gòu)造函數(shù)中的colors漆诽,positions參數(shù)。colors參數(shù)很好理解锣枝,就是一組顏色值厢拭;positions的釋義是“相對(duì)位置、權(quán)重”撇叁,看完釋義是不是還是沒(méi)有太明白(/□\*)供鸠,來(lái)直接上代碼和效果圖。
LinearGradient mGradient = new LinearGradient(0, 0, mViewWidth / 2, mViewHeight,
new int[]{0x00ffffff, 0x73ffffff, 0x00ffffff, 0x99ffffff, 0x00ffffff},
new float[]{0.2f, 0.35f, 0.45f, 0.5f, 0.8f},
Shader.TileMode.CLAMP);
上面代碼可以這么理解陨闹,它定義了一組漸變的數(shù)值是{ 0x00ffffff, 0x73ffffff, 0x00ffffff, 0x99ffffff, 0x00ffffff}楞捂,這組數(shù)值分別在相對(duì)應(yīng)的0.2f, 0.35f, 0.45f, 0.5f, 0.8f中顯示:
- 第一道閃光顏色有效值是0.35f位置的35%白色,0.35f前后位置的顏色值都為透明趋厉,調(diào)節(jié)這兩個(gè)透明顏色的position值就可以調(diào)節(jié)第一道閃光的寬度寨闹;
- 第二道閃光顏色有效值是0.5f位置的50%白色,同理0.5f前后位置顏色為透明君账,調(diào)節(jié)第二道閃光寬度就可以調(diào)節(jié)這兩個(gè)position值鼻忠;
- 中間0.45f位置設(shè)為透明,也就把第一道光和第二道光隔開了杈绸。
2.兩道閃光順序出現(xiàn)
現(xiàn)在兩道光用LinearGradient一起繪制出來(lái)了帖蔓,要怎樣實(shí)現(xiàn)順序出現(xiàn)呢?這里配合Matrix瞳脓、屬性動(dòng)畫ValueAnimator來(lái)控制塑娇,先看核心代碼:
private void initGradientAnimator() {
valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.setDuration(5000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float v = (Float) animation.getAnimatedValue();
//? 改變每次動(dòng)畫的平移x、y值
mTranslateX = 4 * mViewWidth * v - mViewWidth * 2;
mTranslateY = mViewHeight * v;
//? mGradientMatrix為變換矩陣劫侧,設(shè)置矩陣x埋酬、y平移量
if (mGradientMatrix != null) {
mGradientMatrix.setTranslate(mTranslateX, mTranslateY);
}
//? 為線性漸變mGradient設(shè)置matrix
if (mGradient != null) {
mGradient.setLocalMatrix(mGradientMatrix);
}
//? 重繪
invalidate();
}
});
}
重點(diǎn)看下第?步怎么移動(dòng)的,每次根據(jù)當(dāng)前的動(dòng)畫屬性值設(shè)置x烧栋、y平移量写妥,x的范圍是[-2mViewWidth, 2mViewWidth],y的范圍是范圍是[0, mViewHeight]审姓,如下圖所示(x軸)珍特。也就是兩道閃光從不可見(jiàn)到可見(jiàn),調(diào)節(jié)valueAnimator的duration魔吐,或者更改x扎筒、y變化方式就能控制兩道閃光的出場(chǎng)順序了莱找。
注:上面方式實(shí)現(xiàn)的閃光動(dòng)效和文章開頭列的條件并不是百分百一樣,大致效果相同嗜桌。
最后奥溺,自定義LightningView的的所有代碼:
public class LightningView extends View {
private Shader mGradient;
private Matrix mGradientMatrix;
private Paint mPaint;
private int mViewWidth = 0, mViewHeight = 0;
private float mTranslateX = 0, mTranslateY = 0;
private boolean mAnimating = false;
private Rect rect;
private ValueAnimator valueAnimator;
private boolean autoRun = true; //是否自動(dòng)運(yùn)行動(dòng)畫
public LightningView(Context context) {
super(context);
init();
}
public LightningView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public LightningView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
rect = new Rect();
mPaint = new Paint();
initGradientAnimator();
}
public void setAutoRun(boolean autoRun) {
this.autoRun = autoRun;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
rect.set(0, 0, getWidth(), getHeight());
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mViewWidth == 0) {
mViewWidth = getWidth();
mViewHeight = getHeight();
if (mViewWidth > 0) {
//亮光閃過(guò)
mGradient = new LinearGradient(0, 0, mViewWidth / 2, mViewHeight,
new int[]{0x00ffffff, 0x73ffffff, 0x00ffffff, 0x99ffffff, 0x00ffffff},
new float[]{0.2f, 0.35f, 0.5f, 0.7f, 1},
Shader.TileMode.CLAMP);
mPaint.setShader(mGradient);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));
mGradientMatrix = new Matrix();
mGradientMatrix.setTranslate(-2 * mViewWidth, mViewHeight);
mGradient.setLocalMatrix(mGradientMatrix);
rect.set(0, 0, w, h);
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mAnimating && mGradientMatrix != null) {
canvas.drawRect(rect, mPaint);
}
}
private void initGradientAnimator() {
valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.setDuration(5000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float v = (Float) animation.getAnimatedValue();
//? 改變每次動(dòng)畫的平移x、y值骨宠,范圍是[-2mViewWidth, 2mViewWidth]
mTranslateX = 4 * mViewWidth * v - mViewWidth * 2;
mTranslateY = mViewHeight * v;
//? 平移matrix, 設(shè)置平移量
if (mGradientMatrix != null) {
mGradientMatrix.setTranslate(mTranslateX, mTranslateY);
}
//? 設(shè)置線性變化的matrix
if (mGradient != null) {
mGradient.setLocalMatrix(mGradientMatrix);
}
//? 重繪
invalidate();
}
});
if (autoRun) {
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
getViewTreeObserver().removeGlobalOnLayoutListener(this);
mAnimating = true;
if (valueAnimator != null) {
valueAnimator.start();
}
}
});
}
}
//停止動(dòng)畫
public void stopAnimation() {
if (mAnimating && valueAnimator != null) {
mAnimating = false;
valueAnimator.cancel();
invalidate();
}
}
//開始動(dòng)畫
public void startAnimation() {
if (!mAnimating && valueAnimator != null) {
mAnimating = true;
valueAnimator.start();
}
}
}