In Android TV, when an item is selected, the background is switched to a blurred image of the resource. The switching should not be very abrupt, but should have transitional effect.
Implementation steps
- Get the image in the currently selected item: First, get the View where the current focus is, and monitor the global focus through ViewTreeObserver.OnGlobalFocusChangeListener. When the focus moves, get the View where the focus is. If the keystroke interval is less than 350 ms, it will not execute.
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);
}
}
- After getting the view where the focus is, the Bitmap object on the view is obtained by View.getDrawingCache(). This method is time-consuming, so it is executed in the thread, and then compressed and blurred. CustomFrameLayout is the layout of each item. View.setDrawingCacheEnabled(true) must be called before using View.getDrawingCache(), and View.destroyDrawingCache() must be called after using the Bitmap object.
View. setDrawing CacheEnabled (false); release resources.
Gauss blurring algorithm uses script Intrinsic Blur provided by android system to call the underlying C/C++. It is very efficient. Before implementing blurring, it is better to zoom out the image, so the efficiency will be faster.
private void blurCurFocusImage(View curFocusView) {
if (curFocusView == null) {
return;
}
focusView = curFocusView;
if (focusView instanceof CustomFrameLayout) {//If the focus is on the top navigation bar, do not deal with it
focusView.setDrawingCacheEnabled(true);
new Thread(new Runnable() {
@Override
public void run() {
Bitmap newBitmap = focusView.getDrawingCache();//This method is time-consuming and executed in threads
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;//Fuzzy degree
float scale = BLUR_SCALE;//Scaling ratio
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;
}
- So far we have finished half of the work. If we set the processed image directly as the background and switch it from the original background to the new background in an instant, it will create a visual sense of abruptness. So we need to add transitional animation between the two. Android provides Transition Drawable to do this. When the latest background image is obtained, the following method is implemented to achieve background transition. mBgView is the background View. Transition Drawable can set up a Drawable array to store the images we want to convert. Transition Drawable Start Transition will gradually change from the images in the array.
Get the background of the end of the View where the last focus was located as the starting background of the View where the current focus was located, and the blurred image as the ending background.
private void updateMainBg(Drawable drawable) {
if (drawable == null) {
return;
}
Drawable oldDrawable;
//When the background is not set for the first time, current BgDrawable = null
Drawable currentBgDrawable = mBgView.getDrawable();
if (currentBgDrawable instanceof TransitionDrawable) {
oldDrawable = ((TransitionDrawable) currentBgDrawable).getDrawable(1);
mTransitionDrawable.setDrawable(0, oldDrawable);
mTransitionDrawable.setDrawable(1, drawable);
} else {
//Setting Background for the First Time
oldDrawable = getDrawable(R.drawable.launcher_bg_new);
mTransitionDrawable = new TransitionDrawable(new Drawable[]{oldDrawable, drawable});
mTransitionDrawable.setCrossFadeEnabled(true);
mBgView.setImageDrawable(mTransitionDrawable);
}
mTransitionDrawable.startTransition(500);//Conversion time
focusView.destroyDrawingCache();
focusView.setDrawingCacheEnabled(false);
}
handler code section
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;
}
}
};