前幾天七夕情人節(jié)杈抢,街上滿是狗糧数尿,連電影選座也來(lái)蹭熱點(diǎn),被單身狗雨和愛(ài)心雨刷屏了惶楼。這效果看著不錯(cuò)右蹦,實(shí)現(xiàn)起來(lái)也不難,決定自己仿一波歼捐。圖做的不好何陆,將就一下吧,哈~
開(kāi)始之前先上效果圖
細(xì)節(jié)拆分
1豹储、每一個(gè)view從頂部落到底部贷盲,然后從底部消失。
2剥扣、每一個(gè)view在X軸上的初始隨機(jī)位置開(kāi)始落下巩剖。
3、每一個(gè)view隨機(jī)初始角度钠怯,并且落下過(guò)程中旋轉(zhuǎn)佳魔。
4、每一個(gè)view初始落下的高度隨機(jī)晦炊,但最終都會(huì)從底部消失鞠鲜。
實(shí)現(xiàn)步驟
首先宁脊,實(shí)現(xiàn)一個(gè)view的下落過(guò)程,這里我內(nèi)置了一個(gè)屬性動(dòng)畫贤姆。
// 效果系數(shù)
private float rainyFraction;
public void startAnimator() {
if (objectAnimator == null) {
objectAnimator = ObjectAnimator.ofFloat(this, "rainyFraction", 0, 1);
objectAnimator.setDuration(6000);
}
objectAnimator.start();
}
public float getRainyFraction() {
return rainyFraction;
}
public void setRainyFraction(float rainyFraction) {
this.rainyFraction = rainyFraction;
invalidate();
}
然后重寫onSizeChanged和onDraw方法榆苞,得到整個(gè)view的寬高,再來(lái)繪制bitmap
float dx霞捡;
@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(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
// 重置matrix坐漏,必須重置不然會(huì)疊加
matrix.reset();
dx = width/2;
float dy = height * rainyFraction;
matrix.postTranslate(width/2, dy);
canvas.drawBitmap(bitmap, matrix, paint);
canvas.restore();
}
落下的效果有了弄砍,現(xiàn)在考慮在X軸隨機(jī)位置的問(wèn)題仙畦,獲取初始X軸位置的方法如下:
// 減去圖片大小是為了不超出
float dx = (float) ((width - imageSize) * Math.random());
把matrix的dx替換就行了音婶,下來(lái)圖片旋轉(zhuǎn)可以用matrix.postRotate(degrees, px, py)這個(gè)方法慨畸,計(jì)算出初始角度就ok了
// dx和dy都在上面計(jì)算過(guò),把這兩句代碼加上去就好
int angle = (int) (Math.random() * 360)衣式;
// 加上落下的系數(shù)寸士,就可以實(shí)現(xiàn)落下并旋轉(zhuǎn)了
matrix.postRotate(180 * rainyFraction + angle, imageSize / 2 + dx, imageSize / 2 + dy);
最后把隨機(jī)初始高度的計(jì)算也簡(jiǎn)單
// 距離頂部的最大高度設(shè)為自身高度碴卧,獲取隨機(jī)值弱卡。
float tempHeight = (float) (height * Math.random());
// 這里的最大高度,就是在頂部的上面的距離住册,最后圖片的高度婶博,而這個(gè)高度+height就是整個(gè)落下的總高度,后面做多個(gè)圖落下的時(shí)候用到
maxHeight = Math.max(tempHeight, maxHeight);
好了荧飞,到這里一個(gè)圖的落下就完成了凡人。接下來(lái)就是多個(gè)圖的效果,怎么做叹阔?for循環(huán)挠轴!這里我是準(zhǔn)備了三個(gè)list保存對(duì)應(yīng)的初始X位置、Y位置以及初始的角度耳幢,看代碼:
public void initCount() {
widthArr.clear();
heightArr.clear();
angles.clear();
for (int i = 0; i < num; i++) {
// 隨機(jī)橫向x的位置
widthArr.add((float) ((width - imageSize) * Math.random()));
float tempHeight = (float) (height * Math.random());
// 隨機(jī)的距離頂部的高度岸晦,是負(fù)值。加上imageSize是為了保證從頂部開(kāi)始落下
heightArr.add(-(imageSize + tempHeight));
maxHeight = Math.max(tempHeight, maxHeight);
// 隨機(jī)初始角度
angles.add((int) (Math.random() * 360));
}
}
同樣在onDraw方法中也是for循壞使用這些值
for (int i = 0; i < num; i++) {
// 重置matrix睛藻,必須重置不然會(huì)疊加
if (num != widthArr.size()) {
break;
}
matrix.reset();
// 當(dāng)前的高度 = 隨機(jī)的初始高度(負(fù)值) + (view的高度 + 最高的狗頭所在的高度 == 整個(gè)動(dòng)畫的高度)*系數(shù)
// 加上2 * imageSize是為了保證狗頭都落下底部外
float dy = heightArr.get(i) + (maxHeight + height + 2 * imageSize) * rainyFraction;
matrix.postTranslate(widthArr.get(i), dy);
// 以狗頭為中心旋轉(zhuǎn)
matrix.postRotate(angles.get(i) + 180 * rainyFraction, imageSize / 2 + widthArr.get(i), imageSize / 2 + dy);
canvas.drawBitmap(bitmap, matrix, paint);
}
最后在activity使用RainyView
rainyView.initCount();
rainyView.startAnimator();
這個(gè)效果的完成到這里就結(jié)束了启上。
寫在最后
這是GitHub地址單身狗雨,完整代碼可以從上面fork下來(lái)修档。喜歡的給個(gè)star唄碧绞,謝謝。
另外說(shuō)一下吱窝,這個(gè)下落的效果很粗暴讥邻,哈哈。而且沒(méi)有把落下的速度計(jì)算出來(lái)院峡,每個(gè)view的速度都一樣的兴使。不過(guò)這些都沒(méi)關(guān)系,重要的是學(xué)習(xí)到里面的精髓照激。