故事還得從看到那張動圖說起痢法。
像往常一樣凉夯,休息時間我都會打開uplabs瀏覽一下國外大佬們的UI設(shè)計。
有個設(shè)計十分吸引眼球垢粮,就是下圖贴届。
仔細看每張圖片,在加載出來的時候背景都會有一個偏移的動效蜡吧,簡約而不簡單毫蚓。
這個能實現(xiàn)嗎?如果公司UI團隊給了這么一個效果圖昔善,你該咋辦元潘?
思路
首先說說思路,既然要做君仆,顯得有個載體吧翩概,可能很多同學(xué)一下子就想到了ImageView
這個東西。但現(xiàn)在是設(shè)么年代了返咱?Material Design的呀钥庇,所以再用ImageView是不是有點low了。所以自然想到就應(yīng)該是CardView
嘛咖摹。
但CardView有個蛋疼的設(shè)定评姨,不能設(shè)置背景圖片,不知道小伙伴們發(fā)現(xiàn)了沒有楞艾?
stackoverflow上的答案過于簡單粗暴参咙,不是我的菜龄广。
既然不用這種方法硫眯,那我們只能使用我們的神器onDraw
了,從根本上解決問題择同。
代碼
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if ((tobePaint!=null&&!tobePaint.isRecycled())) {
canvas.drawBitmap(tobePaint, backgroundSubRec, backgroundRec, paint);
}
}
onDraw方法很簡單两入,就是當(dāng)有可繪制背景的時候就去繪制。
這里有四個變量要關(guān)注一下:
- tobePaint: 需要被繪制的Bitmap對象敲才。
- backgroundSubRec:Rect對象裹纳,表示Bitmap中需要被繪制的區(qū)域择葡。后續(xù)就是通過改變這個變量來達到動畫效果。
- backgroundRec:Rect對象剃氧,表示繪制區(qū)域的大小敏储,大小同CardView的大小。
最開始我們自定義的這個視圖與普通的CardView沒有差異朋鞍,當(dāng)調(diào)用完public void enableActivation(Bitmap activationBg, String key)
這個方法后已添,背景就被繪制上去了,如下圖滥酥。
來看看代碼
public void enableActivation(Bitmap activationBg, String key) {
currentKey = key;
isActivation = false;
init(activationBg,key);
}
具體看init這個方法:
private void init(final Bitmap originBitmap,final String key) {
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
Executors.newSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
w = getWidth();
h = getHeight();
int scaledW = (int) (w * bgScale);
int scaledH = (int) (h * bgScale);
double preSH = 1.0 * originBitmap.getHeight() / scaledH;
double preSW = 1.0 * originBitmap.getWidth() / scaledW;
float smallPreS = (float) Math.min(preSH, preSW);
Matrix matrix = new Matrix();
float s = 1 / smallPreS;
matrix.postScale(s, s);
if(sIsEnableCache){
background = sCache.get(key);
if(background == null){
background = Bitmap.createBitmap(originBitmap, 0, 0, originBitmap.getWidth(), originBitmap.getHeight(), matrix, true);
sCache.put(key,background);
}
}else {
background = Bitmap.createBitmap(originBitmap, 0, 0, originBitmap.getWidth(), originBitmap.getHeight(), matrix, true);
}
defaultLeft = (background.getWidth() - w) / 2;
defaultTop = (background.getHeight() - h) / 2;
backgroundRec = new Rect(0, 0, w, h);
backgroundSubRec = new Rect(defaultLeft, defaultTop, w + defaultLeft, h + defaultTop);
currentPosition = POSITION_CENTER;
tobePaint = background;
Log.d("scott"," key = " + key + " current key = " + currentKey);
if(key.equals(currentKey)){
handler.post(new Runnable() {
@Override
public void run() {
invalidate();
isActivation = true;
}
});
}
}
});
return true;
}
});
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
invalidate();
}
這個方法做了這么幾件事:
- 確定CardView的長寬更舞。
- 根據(jù)CardView的實際大小對傳入的Bitmap進行適當(dāng)放大,為后續(xù)動畫做準備坎吻。
- 確定backgroundRec缆蝉,backgroundSubRec這兩個對象的值。
- 給tobePaint對象賦值瘦真。
- 調(diào)用invalidate()繪制背景刊头。
為了方便理解,我畫了如下這張圖吗氏。
下面是動畫部分芽偏,這部分。
先來說說原理弦讽,上面繪制的圖像是靠backgroundSubRec對tobePaint進行截取而來的污尉,一開始backgroundSubRec截取的是放大后tobePaint的中間部分,其大小和CardView一致往产,接著通過不斷的改變backgroundSubRec的值被碗,讓其慢慢向右移動。來截取tobePaint的右邊部分仿村。
下面是代碼:
public void postRight() {
if (!isActivation) {
handler.postDelayed(new Runnable() {
@Override
public void run() {
postRight();
}
}, 1000 / 60);
return;
}
if (currentPosition == POSITION_INVAL || currentPosition == POSITION_RIGHT) {
Log.d("scott", "current position is already right");
return;
}
currentPosition = POSITION_INVAL;
final int delta = defaultLeft * 2 - backgroundSubRec.left;
int tempStep = delta / animationDuration;
if (tempStep == 0) tempStep = 1;
final int step = tempStep;
handler.postDelayed(new Runnable() {
@Override
public void run() {
if(!isActivation) return;
invalidate();
if (backgroundSubRec.left < defaultLeft * 2) {
backgroundSubRec.left += step;
backgroundSubRec.right += step;
handler.postDelayed(this, fps);
} else {
currentPosition = POSITION_RIGHT;
}
}
}, fps);
}
最后是效果圖锐朴。
最后
雖然上面講的比較簡單,其實在這過程中有一些細節(jié)還是需要注意的蔼囊,比如bitmap的格式最好使用RGB_565來減少內(nèi)存占用焚志,使用LruCahce來緩存Bitmap增加背景切換速度,還有就是背景放大的比例也需要根據(jù)實際需求做調(diào)整畏鼓。
下面給出代碼酱酬,