导航抽屉的模糊背景

9

如何在导航抽屉上创建实时模糊效果,使得当你拉出抽屉时,列表的背景会模糊显示在它后面的碎片的背景上?

我已经搜索过了,主要是找到了BitMap模糊,但没有实时模糊的内容。


你真的想要模糊背景而不只是使其半透明吗?模糊是一项昂贵的操作,如果没有技巧地即时处理可能会太过昂贵! - LukaCiko
1
你能否创建一个带有一些模糊的透明 .png 图像?然后将抽屉背景设置为该图像? - Phoenix
你不能使用一个模糊的.png文件,因为要创建模糊效果需要对一张图片进行采样和模糊处理。 - SquiresSquire
1个回答

14
该流程包括以下几个阶段:
  1. 获取整个抽屉布局的快照。
  2. 将其裁剪到您菜单的确切大小。
  3. 将其缩小相当多(8的倍数效果很好)。
  4. 模糊该图像。
  5. 使用自定义视图,在菜单出现的位置后面显示图像的一部分。
  6. 在滑动抽屉时,显示该图像的更大部分。

以下是代码示例(所有操作都在BlurActionBarDrawerToggle和BlurAndDimView中完成):

public class BlurActionBarDrawerToggle extends ActionBarDrawerToggle {

    private static final int DOWNSAMPLING = 8;

    private final DrawerLayout drawerLayout;
    private final BlurAndDimView blurrer;
    private Bitmap drawerSnapshot;
    private final ColorDrawable imageBackgroundDrawable;

    public BlurActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, int drawerImageRes, int openDrawerContentDescRes, int closeDrawerContentDescRes, BlurAndDimView blurrer) {
        super(activity, drawerLayout, drawerImageRes, openDrawerContentDescRes, closeDrawerContentDescRes);

        //this should be roughly the same color as your window background
        imageBackgroundDrawable = new ColorDrawable(activity.getResources().getColor(R.color.dark_blue_2));
        this.drawerLayout = drawerLayout;
        this.blurrer = blurrer;
    }

    @Override
    public void onDrawerSlide(final View drawerView, final float slideOffset) {
        super.onDrawerSlide(drawerView, slideOffset);
        if (slideOffset > 0.0f) {
            setBlurAlpha(slideOffset);
        } else {
            clearBlurImage();
        }
    }

    @Override
    public void onDrawerClosed(View view) {
        clearBlurImage();
    }

    private void setBlurAlpha(float slideOffset) {
        if (!blurrer.hasImage()) {
            setBlurImage();
        }
        blurrer.handleScroll(slideOffset);
    }

    public void setBlurImage() {
        blurrer.setVisibility(View.VISIBLE);
        drawerSnapshot = drawViewToBitmap(drawerSnapshot, drawerLayout, DOWNSAMPLING, imageBackgroundDrawable);
        blurrer.setImage(drawerSnapshot, DOWNSAMPLING);
    }

    public void clearBlurImage() {
        blurrer.clearImage();
        blurrer.setVisibility(View.INVISIBLE);
    }

    private Bitmap drawViewToBitmap(Bitmap dest, View view, int downSampling, Drawable background) {
        float scale = 1f / downSampling;
        int viewWidth = view.getWidth();
        int viewHeight = view.getHeight();
        int bmpWidth = (int) (viewWidth * scale);
        int bmpHeight = (int) (viewHeight * scale);
        if (dest == null || dest.getWidth() != bmpWidth || dest.getHeight() != bmpHeight) {
            dest = Bitmap.createBitmap(bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888);
        }
        Canvas c = new Canvas(dest);
        background.setBounds(new Rect(0, 0, viewWidth, viewHeight));
        background.draw(c);
        if (downSampling > 1) {
            c.scale(scale, scale);
        }

        view.draw(c);
        view.layout(0, 0, viewWidth, viewHeight);
        return dest;
    }
}

BlurAndDim视图会使菜单下方模糊,并使其余部分变暗(您可以调整数字以获得所需效果):

public class BlurAndDimView extends View {

    private static final int BLUR_RADIUS = 4;
    private static final int BLUE_ALPHA = 178;
    private static final int MAX_DIM_ALPHA = 127;
    private Paint bitmapPaint;
    private Paint dimPaint;
    private Paint blueSemiTransparentPaint;
    private int menuWidth;
    private int titleHeight;

    private Bitmap image;
    private Rect rectDst;
    private Rect rectSrc;
    private int downSampling;
    private Rect rectRest;

    public BlurAndDimView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        postConstruct();
    }

    public BlurAndDimView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        postConstruct();
    }

    public BlurAndDimView(Context context) {
        this(context, null);
        postConstruct();
    }

    private void postConstruct() {
        bitmapPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        dimPaint = new Paint();
        blueSemiTransparentPaint = new Paint();
        //You might want to also have a semitransparent overlay over your blurred image, since you can't control what's behind your menu.
        blueSemiTransparentPaint.setColor(getResources().getColor(R.color.dark_blue));
        blueSemiTransparentPaint.setAlpha(BLUE_ALPHA);
        menuWidth = getResources().getDimensionPixelSize(R.dimen.browse_menu_width);
    }
}

    @Override
    protected void onDraw(Canvas canvas) {
        if (image != null) {
            canvas.drawBitmap(image, rectSrc, rectDst, bitmapPaint);
            canvas.drawRect(rectDst, blueSemiTransparentPaint);
            canvas.drawRect(rectRest, dimPaint);
        }
    }

    public void handleScroll(float xOffset) {
        if(image != null) {
            rectSrc.right = (int) (image.getWidth() * xOffset);
            rectDst.right = rectSrc.right * downSampling;
            rectRest.left = rectDst.right;
            dimPaint.setAlpha((int) (xOffset * MAX_DIM_ALPHA));
            invalidate();
        }
    }

    public void setImage(Bitmap bmp, int downSampling) {
        Bitmap cropped = Bitmap.createBitmap(bmp, 0, 0, menuWidth / downSampling, getHeight() / downSampling);
        this.image = Blur.blur(getContext(), cropped, BLUR_RADIUS);
        rectSrc = new Rect(0, 0, bmp.getWidth(), bmp.getHeight());
        rectDst = new Rect(0, 0, menuWidth, getHeight());
        rectRest = new Rect(menuWidth, 0, getWidth(), getHeight());
        this.downSampling = downSampling;
        invalidate();
    }

    public boolean hasImage() {
        return image != null;
    }

    public void clearImage() {
        image = null;
    }
}

要实现一个好的模糊算法(尽可能使用Renderscript),您可以使用这个

布局应该像这样:

<android.support.v4.widget.DrawerLayout
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <android.support.v4.widget.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.bla.bla.BlurAndDimView
        android:id="@+id/blurrer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="?android:attr/actionBarSize"
        android:visibility="invisible" />

    <ListView
        android:id="@+id/menu_list"
        android:layout_width="@dimen/browse_menu_width"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice" />
</android.support.v4.widget.DrawerLayout>

请注意,根据您进行的缩小/模糊程度,某些设备可能需要超过40-50毫秒的时间。但是,如果您坚持使用我给出的数字,大多数情况下应该没有任何问题。 - Tas Morf
你从哪里得到了"Blur.blur",能否指定你使用的库或类?谢谢。 - Cjames
我已经在答案中提供了链接:https://github.com/PomepuyN/BlurEffectForAndroidDesign/blob/master/BlurEffect/src/com/npi/blureffect/Blur.java - Tas Morf
如何在右侧打开导航抽屉时使用这个? - Sanjeet A
将菜单Listview的重力设置为end,然后确保BlurAndDimView中的Rect从右侧开始。我的原始答案中应该有足够的代码让你自己解决这个问题。 - Tas Morf
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接