Desenfoque de imagen de Android y mapa de bits relacionado

Desenfoque de imagen de Android y mapa de bits relacionado

Introducción

Tarea: el fondo de la ventana emergente está borroso

Principio de implementación: antes de que se muestre el contenido de la ventana emergente, primero tome una captura de pantalla de la pantalla del teléfono móvil y luego desenfoque.Después de que aparezca la ventana emergente, adapte la captura de pantalla de acuerdo con el tamaño de la ventana emergente ventana emergente y configúrelo como fondo, para lograr el efecto de difuminar el fondo de la ventana emergente.

dificultad:

  1. Dado que nuestra aplicación no tiene actividad residente y es una aplicación del sistema, es diferente de la captura de pantalla de las aplicaciones ordinarias;
  2. Cuando se usa el método de reflexión para llamar a la API del sistema para tomar una captura de pantalla, hay un problema para obtener la altura de la pantalla, lo que requiere atención especial;
  3. Usar la API nativa directamente para difuminar, establecer el radio en un máximo de 25 no cumple con los requisitos comerciales, por lo que la imagen debe procesarse además.

captura de pantalla

Hay muchas maneras de tomar capturas de pantalla, aquí hay algunas de las más utilizadas:

  1. captura de pantalla del comando adb

    El comando adb para tomar capturas de pantalla requiere privilegios de root y, debido a que involucra io, requiere mucho tiempo.

       private Bitmap getScreenByAdb(String localImgPath) {
    //       adb命令获取屏幕截图,与实际屏幕尺寸相同,但是涉及IO所以耗时
           Process process = null;
           try {
               process = Runtime.getRuntime().exec("screencap " +  localImgPath);
               process.waitFor();//比较耗时,尤其屏幕色彩比较复杂时,耗时甚至会到3s
          } catch (Exception e) {
               e.printStackTrace();
          } finally {
               if (process != null) {
                   process.destroy();
              }
          }
           FileInputStream fis = null;
           try {
               fis = new FileInputStream(localImgPath);
          } catch (FileNotFoundException e) {
               e.printStackTrace();
          }
           Bitmap originalBgBitmap = BitmapFactory.decodeStream(fis);
           return originalBgBitmap;
      }
    
  2. Captura de pantalla de la API del sistema de llamadas de reflexión

    Al usar este método para tomar capturas de pantalla, debe ser una aplicación del sistema

       private Bitmap getScreenBySysMethod() {
           DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
           Bitmap originalBgBitmap = null;
           try {
               Class<?> mClassType = Class.forName("android.view.SurfaceControl");
               Method nativeScreenshotMethod = mClassType.getDeclaredMethod("screenshot", Rect.class, int.class, int.class, int.class);
               nativeScreenshotMethod.setAccessible(true);
               Bitmap sBitmap = (Bitmap) nativeScreenshotMethod.invoke(mClassType, new Rect(), dm.widthPixels,
                       dm.heightPixels, Surface.ROTATION_0);
               originalBgBitmap = sBitmap.copy(Bitmap.Config.ARGB_8888, true);
          } catch (NoSuchMethodException e) {
               e.printStackTrace();
          } catch (ClassNotFoundException e) {
               e.printStackTrace();
          } catch (InvocationTargetException e) {
               e.printStackTrace();
          } catch (IllegalAccessException e) {
               e.printStackTrace();
          }
    ​
           return originalBgBitmap;
      }
    

    Hubo un pozo cuando usé este método para tomar capturas de pantalla. Me tomó mucho tiempo tirar. La altura de la pantalla obtenida al usar dm.heightPixels directamente en algunos teléfonos móviles de pantalla completa no incluye la barra de estado. Mira la fuente código de dm.heightPixels, y el comentario se escribe como La altura absoluta del tamaño de visualización disponible en píxeles, por lo que está bien no incluir la barra de estado. En este momento, hay otra forma de obtener la altura de la pantalla:

      /**
        * 在activity中获取屏幕的真实高度,由于在部分全面屏手机上,
        * 直接使用DisplayMetrics heightPixels获取屏幕高度没有包含状态栏或者虚拟按键
        * 所以会比实际的小几十个像素
        * @param context
        * @return
        */
       public static int getScreenHeight(Activity context) {
           DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
           context.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
           return displayMetrics.heightPixels;
      }
    
  3. Capturas de pantalla de aplicaciones normales

    Este método generalmente se llama en una actividad o fragmento, porque necesita llamar a getWindow().getDecorView() para obtener el diseño raíz. Por lo tanto, si necesita tomar una captura de pantalla, si no hay actividad, no puede usar este método.

       private Bitmap getScreenshotView() {
           View view = getWindow().getDecorView();
           view.setDrawingCacheEnabled(true); // 设置缓存,可用于实时截图
           Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
           Canvas canvas = new Canvas(bitmap);
           view.draw(canvas);
           view.setDrawingCacheEnabled(false); // 清空缓存,可用于实时截图
           return bitmap;
      }
    

    Al mismo tiempo, dado que este método toma capturas de pantalla a través de la vista, también es posible tomar una captura de pantalla parcial de una determinada vista, siempre que el objeto de vista en el método se reemplace con la vista que necesita captura de pantalla.

desenfoque de mapa de bits

Hay controles difusos listos para usar en GitHub, que pueden adaptarse a muchos escenarios, como github.com/mmin18/Real…etc . Desafortunadamente, no cubre mis necesidades, solo necesito desenfocar el bitmap que obtengo aquí, y puedo usar Android nativo directamente, el código es el siguiente:

/**
* 
* @param context 上下文
* @param src 原图
* @param radius 模糊半径,有效范围0-25
* @return
*/
private Bitmap blur(Context context, Bitmap src, float radius) {
  RenderScript rs = RenderScript.create(context);
       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;
}

如果这个方法模糊度不够的话,还可以这样来做:先将bitmap缩小,再进行模糊,然后再放大,这样得到的模糊度一般是可以满足需求的。

缩小bitmap

BitmapTransformUtil.scaleBitmap(originalBgBitmap, 0.2f, 0.2f, true);

放大bitmap

BitmapTransformUtil.scaleBitmap(blurredBgBitmap, 5.0f, 5.0f, true);

其中scaleBitmap方法如下所示:

   /**
    * 根据指定的宽度比例值和高度比例值进行缩放
    *
    * @param srcBitmap
    * @param scaleWidth
    * @param scaleHeight
    * @param recycleSrc 是否回收Bitmap
    * @return
    */
   public static Bitmap scaleBitmap(Bitmap srcBitmap, float scaleWidth, float scaleHeight, boolean recycleSrc) {
       int width = srcBitmap.getWidth();
       int height = srcBitmap.getHeight();
       Matrix matrix = new Matrix();
       matrix.postScale(scaleWidth, scaleHeight);
       Bitmap bitmap = Bitmap.createBitmap(srcBitmap, 0, 0, width, height, matrix, true);
       if (bitmap != null) {
           /**回收*/
           if (recycleSrc && srcBitmap != null && !srcBitmap.equals(bitmap) && !srcBitmap.isRecycled()) {
               GlideBitmapPool.putBitmap(srcBitmap);
          }
           return bitmap;
      } else {
           return srcBitmap;
      }
  }

Bitmap截取

拿到整个手机屏幕的截图后,需要借去部分作为背景,截取bitmap方法如下:

   /**
    * 裁剪一定高度保留上半部分
    *
    * @param srcBitmap 原图
    * @param x         起始坐标x
    * @param y         起始坐标y
    * @param width     目标宽度
    * @param height     目标高度
    * @param recycleSrc 是否回收原图
    * @return
    */
   public static Bitmap cropBitmapTop(Bitmap srcBitmap, int x, int y, int width, int height, boolean recycleSrc) {
​
       /**裁剪关键步骤*/
       Bitmap cropBitmap = Bitmap.createBitmap(srcBitmap, x, y, width, height);
​
       /**回收之前的Bitmap*/
       if (recycleSrc && srcBitmap != null && !srcBitmap.equals(cropBitmap) && !srcBitmap.isRecycled()) {
           GlideBitmapPool.putBitmap(srcBitmap);
      }
       return cropBitmap;
  }

Bitmap圆角设置

   /**
    * 设置圆角
    *
    * @param srcBitmap
    * @param recycleSrc 是否回收原图
    * @return
    */
   public static Bitmap getRoundedCornerBitmap(Bitmap srcBitmap, float radius, boolean recycleSrc) {
       Bitmap output = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), Bitmap.Config.ARGB_8888);
       Paint paint = new Paint();
       Rect rect = new Rect(0, 0, srcBitmap.getWidth(), srcBitmap.getHeight());
       RectF rectF = new RectF(rect);
       paint.setAntiAlias(true);
       Canvas canvas = new Canvas(output);
       canvas.save();
       canvas.drawARGB(0, 0, 0, 0);
       canvas.drawRoundRect(rectF, radius, radius, paint);
       paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
       canvas.drawBitmap(srcBitmap, rect, rect, paint);
       canvas.restore();
       /**回收之前的Bitmap*/
       if (recycleSrc && srcBitmap != null && !srcBitmap.equals(output) && !srcBitmap.isRecycled()) {
           GlideBitmapPool.putBitmap(srcBitmap);
      }
       return output;
  }

Bitmap保存

    /**
    * 保存bitmap到本地
    * @param bitmap 
    * @param filePath 
    */
   private void saveBitmap(Bitmap bitmap, String filePath){
       FileOutputStream fos = null;
       try {
           fos = new FileOutputStream(filePath);
           bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
           fos.flush();
      } catch (IOException e) {
           e.printStackTrace();
      } finally {
           try {
               if (fos != null) {
                   fos.close();
              }
          } catch (IOException e) {
               e.printStackTrace();
          }
      }
  }

推荐一个bitmap复用池:github.com/amitshekhar…,毕竟bitmap是内存消耗大户,合理使用bitmap可以避免内存抖动和OOM。具体使用方法直接点进去看吧。

最后贴一个实现效果图吧

image.png

Supongo que te gusta

Origin juejin.im/post/7116432182448488479
Recomendado
Clasificación