在Android TV 中,当选中某一个item时,背景切换成模糊处理后的该资源图片,切换时不能很突兀,要有过渡效果.
实现步骤
- 获取当前选中item中的图片:首先获取当前焦点所在的View,通过ViewTreeObserver.OnGlobalFocusChangeListener监听全局焦点,当焦点移动时,获取焦点所在View,如果按键间隔时间低于350ms,则不执行。
public class LauncherActivity extends BaseActivity implements ViewTreeObserver.OnGlobalFocusChangeListener{
private final static int MSG_BLUR_BG = 0x2003;
private final static int MSG_UPDATE_BG = 0x2004;
private final static int MSG_REFRESH_BG_DELAY = 350;
private ViewTreeObserver mViewTreeObserver;
private TransitionDrawable mTransitionDrawable;
private View focusView;
.......
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launcher);
.......
mViewTreeObserver = this.getWindow().getDecorView().getViewTreeObserver();
mViewTreeObserver.addOnGlobalFocusChangeListener(this);
.......
}
@Override
public void onGlobalFocusChanged(View oldFocus, View newFocus) {
mHandler.removeMessages(MSG_BLUR_BG);
Message msg = mHandler.obtainMessage();
msg.what = MSG_BLUR_BG;
msg.obj = newFocus;
mHandler.sendMessageDelayed(msg, MSG_REFRESH_BG_DELAY);
}
}
- 获取到焦点所在的View后,通过View.getDrawingCache()获取View上的Bitmap对象,此方法比较耗时,故放在线程中执行,然后进行压缩模糊处理。CustomFrameLayout 为每个item的布局。使用View.getDrawingCache()前必须调用View.setDrawingCacheEnabled(true);使用完Bitmap对象之后,要 调用View.destroyDrawingCache();
View.setDrawingCacheEnabled(false);释放资源。
高斯模糊算法使用android系统提供的ScriptIntrinsicBlur,直接调用底层C/C++,效率很快,在执行模糊之前,最好是将图片进行缩放出来,这样效率会更快。
private void blurCurFocusImage(View curFocusView) {
if (curFocusView == null) {
return;
}
focusView = curFocusView;
if (focusView instanceof CustomFrameLayout) {//如果焦点在顶部导航栏时,不做处理
focusView.setDrawingCacheEnabled(true);
new Thread(new Runnable() {
@Override
public void run() {
Bitmap newBitmap = focusView.getDrawingCache();//此方法耗时,放在线程中执行
Drawable newDrawable = new BitmapDrawable(DisplayUtil.rsBlur(LauncherActivity.this, newBitmap));
Message msg = mHandler.obtainMessage();
msg.what = MSG_UPDATE_BG;
msg.obj = newDrawable;
mHandler.sendMessage(msg);
newBitmap.recycle();
}
}).start();
}
public static Bitmap rsBlur(Context context, Bitmap source) {
int radius = BLUR_RADIUS;//模糊程度
float scale = BLUR_SCALE;//缩放比例
int width = Math.round(source.getWidth() * scale);
int height = Math.round(source.getHeight() * scale);
Bitmap inputBmp = Bitmap.createScaledBitmap(source, width, height, false);
RenderScript renderScript = RenderScript.create(context);
// Allocate memory for Renderscript to work with
final Allocation input = Allocation.createFromBitmap(renderScript, inputBmp);
final Allocation output = Allocation.createTyped(renderScript, input.getType());
// Load up an instance of the specific script that we want to use.
ScriptIntrinsicBlur scriptIntrinsicBlur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
scriptIntrinsicBlur.setInput(input);
// Set the blur radius
scriptIntrinsicBlur.setRadius(radius);
// Start the ScriptIntrinisicBlur
scriptIntrinsicBlur.forEach(output);
// Copy the output to the blurred bitmap
output.copyTo(inputBmp);
renderScript.destroy();
return inputBmp;
}
- 至此我们已经完成一半了,如果将处理后的图片直接设置为背景,在一瞬间由原先的背景切换到新的背景,会造成视觉上的突兀感,所以我们要在两者之间添加过渡动画。Android提供了TransitionDrawable可以实现此功能。当获取到最新的背景图时,执行下面方法,实现背景过渡。mBgView为背景View。TransitionDrawable 可以设置一个Drawable数组,数组中保存我们要转换的图片,TransitionDrawable startTransition 时会从数组中图片依次渐变。
获取上次焦点所在View的背景的结束背景 作为当前焦点所在View 的起始背景,模糊处理后的图片为结束背景。
private void updateMainBg(Drawable drawable) {
if (drawable == null) {
return;
}
Drawable oldDrawable;
//第一次未设置背景时,currentBgDrawable=null
Drawable currentBgDrawable = mBgView.getDrawable();
if (currentBgDrawable instanceof TransitionDrawable) {
oldDrawable = ((TransitionDrawable) currentBgDrawable).getDrawable(1);
mTransitionDrawable.setDrawable(0, oldDrawable);
mTransitionDrawable.setDrawable(1, drawable);
} else {
//第一次设置背景
oldDrawable = getDrawable(R.drawable.launcher_bg_new);
mTransitionDrawable = new TransitionDrawable(new Drawable[]{oldDrawable, drawable});
mTransitionDrawable.setCrossFadeEnabled(true);
mBgView.setImageDrawable(mTransitionDrawable);
}
mTransitionDrawable.startTransition(500);//转换时间
focusView.destroyDrawingCache();
focusView.setDrawingCacheEnabled(false);
}
handler 代码部分
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MSG_BLUR_BG:
blurCurFocusImage((View) msg.obj);
break;
case MSG_UPDATE_BG:
updateMainBg((Drawable) msg.obj);
break;
}
}
};