Android8.1 SystemUI 下拉通知栏添加高斯模糊

Android8.1 SystemUI 下拉通知栏添加高斯模糊

这个功能以前在6.0上做过,现在移植到8.1;顺便写个博客记录下,有不好的地方,还请各位大神不吝赐教

1:把qspanel的背景去掉

res/layout/qs_panel.xml

<com.android.systemui.qs.QSContainerImpl
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/quick_settings_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    //删除 begin
    android:background="@drawable/qs_background_primary"
    android:elevation="4dp"
   //删除 end
    android:clipToPadding="false"
    android:clipChildren="false">

    <com.android.systemui.qs.QSPanel
        android:id="@+id/quick_settings_panel"
        android:layout_marginTop="28dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="48dp" />

    "@layout/quick_status_bar_expanded_header" />

    "@layout/qs_footer_impl" />

    "@+id/qs_detail" layout="@layout/qs_detail" />

    "@+id/qs_customize" layout="@layout/qs_customize_panel"
        android:visibility="gone" />

com.android.systemui.qs.QSContainerImpl>

我这里顺便把投影也干掉了。

2:添加一个ImageView,放高斯模糊的图片

我这里还添加了一个半透明的view,因为只有一个高斯模糊会很难看
res/layout/status_bar_expanded.xml :

<com.android.systemui.statusbar.phone.NotificationPanelView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/notification_panel"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent" >

    
    <ImageView
            android:id="@+id/blur_view"
            android:alpha="0"
            android:layout_width="match_parent"
            android:scaleType="fitXY"
            android:layout_height="match_parent"/>

    <View
            android:id="@+id/alpha_view"
            android:alpha="0"
            android:background="#666666"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    

    <include
        layout="@layout/keyguard_status_view"
        android:visibility="gone" />

    ......

3:最后就是把高斯模糊展示出来就行了

先增加几个工具类
截图:

public class ScreenShotUtil {

    public static Bitmap takeScreenShot(Context context) {
        Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        DisplayMetrics displayMetrics = new DisplayMetrics();
        display.getRealMetrics(displayMetrics);
        final float[] dims = {displayMetrics.widthPixels, displayMetrics.heightPixels};
        Bitmap bitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
        if (bitmap != null) {
            return bitmap;
        }
        bitmap = SurfaceControl.screenshot((int) dims[1], (int) dims[0]);
        Matrix matrix = new Matrix();
        matrix.setRotate(-90);
        if (bitmap == null) {
            return null;
        }
        Bitmap newBM = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
        bitmap.recycle();
        return newBM;
    }

}

图片处理:

public class BitmapUtils {

    public static void recycleImageView(View view) {
        if (view == null) return;
        if (view instanceof ImageView) {
            Drawable drawable = ((ImageView) view).getDrawable();
            if (drawable instanceof BitmapDrawable) {
                Bitmap bmp = ((BitmapDrawable) drawable).getBitmap();
                if (bmp != null && !bmp.isRecycled()) {
                    ((ImageView) view).setImageBitmap(null);
                    bmp.recycle();
                    bmp = null;
                }
            }
        }
    }
}
高斯模糊处理:

public class BlurUtil {

private static final float BITMAP_SCALE = 0.4f;
private static final int BLUR_RADIUS = 7;
public static final int BLUR_RADIUS_MAX = 25;

public static Bitmap blur(Context context, Bitmap bitmap) {
    return blur(context, bitmap, BITMAP_SCALE, BLUR_RADIUS);
}

public static Bitmap blur(Context context, Bitmap bitmap, float bitmap_scale) {
    return blur(context, bitmap, bitmap_scale, BLUR_RADIUS);
}

public static Bitmap blur(Context context, Bitmap bitmap, int blur_radius) {
    return blur(context, bitmap, BITMAP_SCALE, blur_radius);
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static Bitmap blur(Context context, Bitmap bitmap, float bitmap_scale, int blur_radius) {
    Bitmap inputBitmap = Bitmap.createScaledBitmap(bitmap, Math.round(bitmap.getWidth() * bitmap_scale),
            Math.round(bitmap.getHeight() * bitmap_scale), false);
    Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
    RenderScript rs = RenderScript.create(context);
    ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
    Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
    Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
    theIntrinsic.setRadius(blur_radius);
    theIntrinsic.setInput(tmpIn);
    theIntrinsic.forEach(tmpOut);
    tmpOut.copyTo(outputBitmap);
    rs.destroy();
    bitmap.recycle();

    return outputBitmap;
}

}

再就是展示了
src/com/android/systemui/statusbar/phone/NotificationPanelView.java -> onTouchEvent():

先定义变量:

    private static final int BLUR_START = 200;
    private static final int BLUR_END = 700;
    private View mAlphaView;
    private ImageView mBlurView;
public boolean onTouchEvent(MotionEvent event) {
        if (mBlockTouches || (mQs != null && mQs.isCustomizing())) {
            return false;
        }
        initDownStates(event);
        if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
                && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
            mIsExpansionFromHeadsUp = true;
            MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1);
        }
        boolean handled = false;
        if ((!mIsExpanding || mHintAnimationRunning)
                && !mQsExpanded
                && mStatusBar.getBarState() != StatusBarState.SHADE
                && !mDozing) {
            handled |= mAffordanceHelper.onTouchEvent(event);
        }
        if (mOnlyAffordanceInThisMotion) {
            return true;
        }
        handled |= mHeadsUpTouchHelper.onTouchEvent(event);

        if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) {
            return true;
        }
        if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
            MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
            updateVerticalPanelPosition(event.getX());
            handled = true;
        }
    //添加 begin
        float y = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (!mPanelExpanded) {
                    setBlurBackground();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (y <= BLUR_END) {
                    float alpha = (y - BLUR_START) / (BLUR_END - BLUR_START);
                    if (alpha < 0) {
                        alpha = 0;
                    }
                    mAlphaView.setAlpha(alpha/2);
                    mBlurView.setAlpha(alpha);
                }
                break;
            case MotionEvent.ACTION_UP:
                float a = mBlurView.getAlpha();
                startAlphaAnimation(a, 1.0f);
                break;
        }
    // 添加 end
        handled |= super.onTouchEvent(event);
        return mDozing ? handled : true;
    }

再添加几个方法:

private void startAlphaAnimation(float start, float end) {
        ValueAnimator va = ValueAnimator.ofFloat(start, end);
        va.setDuration((long) (Math.abs(end - start) * 500));
        va.start();
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float alpha = (float) animation.getAnimatedValue();
                mAlphaView.setAlpha(alpha/2);
                mBlurView.setAlpha(alpha);
            }
        });
    }

    private void setBlurBackground() {
        Bitmap bitmap = ScreenShotUtil.takeScreenShot(getContext());
        if (bitmap == null) {
            LogUtil.d(TAG, "setBlurBackground bitmap == null");
            return;
        }
        Bitmap blurBitmap = BlurUtil.blur(getContext(), BlurUtil.blur(getContext(), bitmap, BlurUtil.BLUR_RADIUS_MAX), BlurUtil.BLUR_RADIUS_MAX);
        BitmapUtils.recycleImageView(mBlurView);
        mBlurView.setImageBitmap(blurBitmap);
    }

到这里就可以了!

但是仍然有一个问题,就是在Systemui启动后的锁屏是没有问题,但是解锁后下拉通知栏,再锁屏,这个时候会发现,锁屏界面也出现了高斯模糊。这个是因为锁屏和下拉通知栏共用了布局。我们再添加一个判断:
src/com/android/systemui/statusbar/phone/NotificationPanelView.java -> setBarState():

public void setBarState(int statusBarState, boolean keyguardFadingAway,
            boolean goingToFullShade) {

        ......

        resetVerticalPanelPosition();
        updateQsState();
        /* 添加判断 begin */
        updateBlurVisibility(keyguardShowing);
        /* 添加判断 end */
    }
private void updateBlurVisibility(boolean keyguardShowing){
        if (keyguardShowing) {
            mBlurView.setVisibility(View.GONE);
            mAlphaView.setVisibility(View.GONE);
            BitmapUtils.recycleImageView(mBlurView);
        }else {
            mBlurView.setVisibility(View.VISIBLE);
            mAlphaView.setVisibility(View.VISIBLE);
        }
    }

在测试过程中,又发现一个问题,就是当headsup出现的时候,高斯模糊的view还是会出现
再次修改:
src/com/android/systemui/statusbar/phone/NotificationPanelView.java :

private boolean mIsFullClose;
@Override
    protected void onHeightUpdated(float expandedHeight) {
        if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
            positionClockAndNotifications();
        }
        if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
                && !mQsExpansionFromOverscroll) {
            float t;
            if (mKeyguardShowing) {

                // On Keyguard, interpolate the QS expansion linearly to the panel expansion
                t = expandedHeight / (getMaxPanelHeight());
            } else {

                // In Shade, interpolate linearly such that QS is closed whenever panel height is
                // minimum QS expansion + minStackHeight
                float panelHeightQsCollapsed = mNotificationStackScroller.getIntrinsicPadding()
                        + mNotificationStackScroller.getLayoutMinHeight();
                float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
                t = (expandedHeight - panelHeightQsCollapsed)
                        / (panelHeightQsExpanded - panelHeightQsCollapsed);
            }
            setQsExpansion(mQsMinExpansionHeight
                    + t * (getTempQsMaxExpansion() - mQsMinExpansionHeight));
        }
        updateExpandedHeight(expandedHeight);
        /* 修改 begin */
        updateIsFullClose(expandedHeight);
        /* 修改 end */
        updateHeader();
        updateUnlockIcon();
        updateNotificationTranslucency();
        updatePanelExpanded();
        mNotificationStackScroller.setShadeExpanded(!isFullyCollapsed());
        if (DEBUG) {
            invalidate();
        }
    }

增加一个方法

/* 增加 begin */
    private void updateIsFullClose(float height){
        boolean close = height == 0;
        if (mIsFullClose != close) {
            mIsFullClose = close;
            updateBlurVisibility(mIsFullClose || mKeyguardShowing);
        }
    }
    /* 增加 end */

现在这样应该就没什么问题了

你可能感兴趣的:(Android)