前言
上一篇藉由step的方式,來達到圖形自動增減形成呼吸的跳動效果,這次稍微把程式碼調整加上動畫效果,練習做成一個會呼吸跳動的聚焦效果,效果如下。
以下我歸納為幾個實作步驟:
- 初始化
- 紀錄HighlightView的相關資料
- 動態添加一層可點擊的FrameLayout
- 製作HighlightView
- 加入進場和退場動畫特效
初始化
初始化分為兩部分,一個是初始欲聚焦視圖的資源,我們可以從Activity傳入所需的配置,像是欲聚焦視圖、聚焦的形狀和疊加上去的背景顏色,程式碼如下。
private void initMemberVariables(Activity activity, View focusedOnView, int highlightShape, int backgroundColor) {
this.activity = activity;
this.focusedOnView = focusedOnView;
this.highlightShape = (highlightShape == 0 ? SHAPE_CIRCLE : SHAPE_RECT);
this.backgroundColor = backgroundColor != 0 ? this.activity.getResources().getColor(backgroundColor) : this.activity.getResources().getColor(R.color.default_color_CC000000);
}
第二則是儲存手機的寬高,並且取得其中心點的XY值,程式碼如下。
private void initScreenResources() {
DisplayMetrics displayMetrics = new DisplayMetrics();
this.activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int deviceWidth = displayMetrics.widthPixels;
int deviceHeight = displayMetrics.heightPixels;
this.centerX = deviceWidth / 2;
this.centerY = deviceHeight / 2;
}
紀錄HighlightView的相關資料
初始化完成後,我們可以記錄一些關於聚焦視圖的資料,像是聚焦視圖的XY值、寬度、高度、繪製圖形的半徑,程式碼如下。
- 聚焦視圖的XY值可以用getLocationInWindow()方法取得,如此一來,我們就能快速得知聚焦圖形需要繪製在什麼位置,程式碼如下。
int[] viewPoints = new int[2];
focusedOnView.getLocationInWindow(viewPoints);
聚焦視圖的寬高,可以直接利用getWidth()與getHeight()兩個方法取得,程式碼如下。
this.focusedOnViewWidth = focusedOnView.getWidth();
this.focusedOnViewHeight = focusedOnView.getHeight();
繪製圖形的半徑要比原本聚焦的視圖來得大一些,所以可以使用聚焦視圖對角線的一半作為半徑,當然你也可以用聚焦視圖的一半,再往外加一點也行。。
this.focusedOnViewRadius = (int) (Math.hypot(this.focusedOnViewWidth, this.focusedOnViewHeight) / 2);
動態添加一層可點擊的FrameLayout
因為我們需要在原本的Acitivity上疊加一層,所以這邊我們動態加入一層FrameLayout,程式碼如下。
this.highlightContainer = new FrameLayout(this.activity);
this.highlightContainer.setTag(HIGHLIGHT_CONTAINER_TAG);
this.highlightContainer.setClickable(true);
this.highlightContainer.setOnClickListener(this);
this.highlightContainer.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
this.mainViewGroup.addView(this.highlightContainer);
製作HighlightView
我們要客製一個聚焦的視圖,除了基本的繪圓和繪方外,還要用到上篇文章提到的方式,才能讓聚焦視圖產生呼吸跳動的效果,程式碼如下。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (this.bitmap == null) {
this.bitmap = Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config.ARGB_8888);
this.bitmap.eraseColor(this.backgroundColor);
}
canvas.drawBitmap(this.bitmap, 0, 0, this.backgroundPaint);
if (this.renderHelper.isFocus()) {
this.animCounter = this.animCounter + this.step;
if (this.renderHelper.getHighlightShape() == ViewManager.SHAPE_CIRCLE) {
this.drawCircle(canvas);
} else {
this.drawRoundedRect(canvas);
}
if (this.animCounter == ANIM_COUNTER_MAX) {
this.step = -1;
} else if (this.animCounter == 0) {
this.step = 1;
}
}
this.postInvalidate();
}
加入進場和退場動畫特效
我們要讓Framlayout產生正確的動畫,所以需要取得正確的寬高,所以須透過getViewTreeObserver()方法,取得正確的數值,才能順利完成動畫效果;而退場效果因為已經繪製完成,所以無需透過該方法取得,可以直接利用getWidth()和getHeight()兩個方法,程式碼如下。
private void enterAnimation() {
final int revealRadius = (int) Math.hypot(highlightContainer.getWidth(), highlightContainer.getHeight());
int startRadius = 0;
if (focusedOnView != null) {
startRadius = focusedOnView.getWidth() / 2;
}
Animator enterAnimator = ViewAnimationUtils.createCircularReveal(highlightContainer, centerX, centerY, startRadius, revealRadius);
enterAnimator.setDuration(1000);
enterAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
enterAnimator.start();
this.highlightContainer.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
highlightContainer.getViewTreeObserver().removeOnPreDrawListener(this);
final int revealRadius = (int) Math.hypot(highlightContainer.getWidth(), highlightContainer.getHeight());
int startRadius = 0;
if (focusedOnView != null) {
startRadius = focusedOnView.getWidth() / 2;
}
Animator enterAnimator = ViewAnimationUtils.createCircularReveal(highlightContainer, centerX, centerY, startRadius, revealRadius);
enterAnimator.setDuration(1000);
enterAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
enterAnimator.start();
return false;
}
});
}
private void exitAnimation() {
final int revealRadius = (int) Math.hypot(highlightContainer.getWidth(), highlightContainer.getHeight());
Animator exitAnimator = ViewAnimationUtils.createCircularReveal(highlightContainer, centerX, centerY, revealRadius, 0f);
exitAnimator.setDuration(300);
exitAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
exitAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mainViewGroup.removeView(highlightContainer);
}
});
exitAnimator.start();
}