Android进阶之路 - 毛玻璃遮罩层

1,527 阅读2分钟

小知识,大挑战!本文正在参与 “程序员必备小知识” 创作活动

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

Hopefully we’re growing all the time ~

blurkit三方框架

记得去年做项目的时候,有涉及到毛玻璃遮罩层的功能,但是因为项目时间太紧,所以放弃了毛玻璃的效果,索性最近有时间就学习记录一波 ~ 文中细节并没有去处理,只是用最简单的方式,实现了我的需求 ~

半成品 - Demo 效果
注 :个人感觉,更适合整体遮罩层
在这里插入图片描述

前期提要

要实现效果可直接跳到使用方式开始看!

关于更具体的详情,请查看文末的借鉴文章之处 > <~

  • 模糊方案

1.Java实现,一般都是采用Stack模糊算法 (亲测:频繁invalidate(),模糊效果消失)
2.RenderScript实现 (亲测:频繁invalidate(),模糊效果消失)
3.Native实现 (未尝试)
4.OpenCV或者OpenGL实现(未尝试)

  • BlurLayout自定义属性

注:只用到了blurkit:blk_fps属性,其他属性一用就报错,崩溃的那种,具体问题没有去排查,因为懒 ~

看别人的博客也存在这样的问题,原话是这样的:在blurkit-android的目前的代码中(包括最新版本),BlurLayout的blk_alpha属性并不能使用。因为在代码中存在类型转换错误。英语好的同学可以去提issue

当然他说的是blk_alpha属性 - - 有机会我在重新尝试

    //每过1000/fps的时间重新绘制一次BlurLayout,
    blurkit:blk_fps="60"
    //透明度
    blurkit:blk_alpha="0.5"
    //模糊半径
    blurkit:blk_blurRadius="15"
    //BlurLayout的圆角半径
    blurkit:blk_cornerRadius="30dp"
    //缩放大小,是先放大再缩小,所以值太大则有可能OOM
    blurkit:blk_downscaleFactor="0.12"
  • 直接对View及Bitmap进行高斯模糊的使用(测量TextView会崩溃,提示宽高测不到,为0)
  //进行BlurKit初始化,在Application中初始化
  BlurKit.init(this);
  //通过RenderScript进行高斯模糊并返回一个bitmap,iv1可以是一个View,也可以是一个ViewGroup,25是模糊半径
  Bitmap bt=BlurKit.getInstance().blur(iv1, 25);
  //通过RenderScript进行高斯模糊并返回一个bitmap,传入的是一个bitmap,25是模糊半径
  Bitmap bt=BlurKit.getInstance().blur(bitmap, 25);
  //通过RenderScript进行高斯模糊并返回一个bitmap,iv1可以是一个View,也可以是一个ViewGroup,25是模糊半径,2代表缩放比例,如果值太大可能会出现OOM
  Bitmap bt=BlurKit.getInstance().fastBlur(iv1,25,2)
  • blurkit-android的毛玻璃实现原理(Me no look)
  //在类BlurKit中
  public Bitmap blur(Bitmap src, int radius) {
      final Allocation input = Allocation.createFromBitmap(rs, src);
      final Allocation output = Allocation.createTyped(rs, input.getType());
      final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
      script.setRadius(radius);
      script.setInput(input);
      script.forEach(output);
      output.copyTo(src);
      return src;
  }

使用方式

  • bulid.app

注 :要使用blurkit-android的1.1.1版本(目前最新版本),不要使用1.1.0版本(虽然GitHub上的使用文档还是1.1.0)
因为使用1.1.1版本时minSdkVersion的值可以是17,而使用1.1.0版本时minSdkVersion的值必须是21

  implementation 'io.alterac.blurkit:blurkit:1.1.1'
  • xml
    <io.alterac.blurkit.BlurLayout
        android:id="@+id/blurLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        blurkit:blk_fps="0"/>
  • Activity、Fragment等使用处
  @Override
  protected void onStart() {
      super.onStart();
      blurLayout.startBlur();
      blurLayout.lockView();
  }

  @Override
  protected void onStop() {
      super.onStop();
      blurLayout.pauseBlur();
  }

完整代码

  • MainActivity
package nkwl.com.glassdemo;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import io.alterac.blurkit.BlurLayout;

public class MainActivity extends AppCompatActivity {

    private BlurLayout blurLayout;
    private TextView mStart;
    private TextView mStop;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mStart = findViewById(R.id.tv_start);
        mStop = findViewById(R.id.tv_stop);
        blurLayout = findViewById(R.id.blurLayout);

        mStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "开始模糊", Toast.LENGTH_LONG).show();
                blurLayout.setVisibility(View.VISIBLE);

            }
        });

        mStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "停止模糊", Toast.LENGTH_LONG).show();
                blurLayout.setVisibility(View.GONE);

            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        blurLayout.startBlur();
        blurLayout.lockView();
    }

    @Override
    protected void onStop() {
        super.onStop();
        blurLayout.pauseBlur();
    }
}
  • activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:blurkit="http://schemas.android.com/apk/res-auto"
    android:id="@+id/ll_parent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@mipmap/bg"
    android:orientation="vertical">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_start"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="开始模糊" />

        <TextView
            android:id="@+id/tv_stop"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="停止模糊" />

    </LinearLayout>

    <io.alterac.blurkit.BlurLayout
        android:id="@+id/blurLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:visibility="gone"
        blurkit:blk_fps="0">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_marginTop="266dp"
            android:background="#fff"
            android:gravity="center"
            android:text="开始的开始,我们只是孩子"
            android:textColor="#f96" />

    </io.alterac.blurkit.BlurLayout>

</LinearLayout>

blurry三方框架

半成品 - Demo 效果 (模糊效果有点丑 - -)

注 :个人感觉,更适合单一控件
在这里插入图片描述

使用方式

 //for ViewGroup 亲测无效,可能是个人原因
  Blurry.with(context)
    .radius(10)//模糊半径
    .sampling(8)//缩放大小,先缩小再放大
    .color(Color.argb(66, 255, 255, 0))//颜色
    .async()//是否异步
    .animate(500)//显示动画,目前仅支持淡入淡出,默认时间是300毫秒,仅支持传入控件为ViewGroup
    .onto(viewGroup);
    
  //for view
  Blurry.with(context) 亲测无效,可能是个人原因
    .radius(10)//模糊半径
    .sampling(8)//缩放大小,先缩小再放大
    .color(Color.argb(66, 255, 255, 0))//颜色
    .async()//是否异步
    .capture(view)//传入View
    .into(view);//显示View
    
  //for bitmap 亲测有效
  Blurry.with(context)
    .radius(10)//模糊半径
    .sampling(8)//缩放大小,先缩小再放大
    .color(Color.argb(66, 255, 255, 0))//颜色
    .async()//是否异步
    .from(bitmap)//传入bitmap
    .into(view);//显示View

实际使用

注:只实现了第三种使用方法

  • build.app
 implementation 'jp.wasabeef:blurry:3.0.0'
  • MainActivity
package nkwl.com.glassdemo;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import jp.wasabeef.blurry.Blurry;

public class MainActivity extends AppCompatActivity {
    private ImageView mAlineView;
    private Bitmap bitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mAlineView = findViewById(R.id.tv_aline_view);
        //本地图片转为bitmap进行数据传入
        bitmap = BitmapFactory.decodeResource(this.getResources(), R.mipmap.bg);

        findViewById(R.id.tv_start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "开始模糊", Toast.LENGTH_LONG).show();
                Blurry.with(MainActivity.this)
                        .radius(10)//模糊半径
                        .sampling(8)//缩放大小,先缩小再放大
                        .color(Color.argb(66, 255, 255, 0))//颜色
                        .async()//是否异步
                        .from(bitmap)//传入View
                        .into(mAlineView);//显示View
            }
        });

        findViewById(R.id.tv_stop).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "停止模糊", Toast.LENGTH_LONG).show();
                Blurry.with(MainActivity.this)
                        .radius(10)//模糊半径
                        .sampling(8)//缩放大小,先缩小再放大
                        .color(Color.argb(0, 0, 0, 0))//颜色
                        .async()//是否异步
                        .from(bitmap)//传入View
                        .into(mAlineView);//显示View
            }
        });
    }
}
  • activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@mipmap/bg"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_start"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="开始模糊" />

        <TextView
            android:id="@+id/tv_stop"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="停止模糊" />

    </LinearLayout>

    <ImageView
        android:id="@+id/tv_aline_view"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginTop="266dp"
        android:contentDescription="@null"
        android:gravity="center"
        android:scaleType="fitXY"
        android:src="@drawable/bg"
        android:visibility="visible" />

</LinearLayout>

借鉴文章