【Android】多种方式实现截图(屏幕截图、View截图、长图)

一、截图原理

在这里插入图片描述
我们的手机一般同时按下音量-键和电源键就会将当前屏幕显示的内容截取下来,那里面具体经过哪些流程呢?

Android中每一个页面都是一个Activity,通过Window对象实现页面的显示,每个Window对象实际上都是PhoneWindow的实例,当我们在Activity页面点击屏幕的时候,会触发点击事件,这个事件会一层层分发到处理它的view上,大致会经过这些view:
在这里插入图片描述
先会调PhoneWindowManager中的dispatchUnhandledKey方法,一层层往下,这里不详细展开,我们往下找会发现最终会调用一个takeScreenshot截屏的方法:

private void takeScreenshot() {
        synchronized (mScreenshotLock) {
            if (mScreenshotConnection != null) {
                return;
            }
            ComponentName cn = new ComponentName("com.android.systemui",
                    "com.android.systemui.screenshot.TakeScreenshotService");
            Intent intent = new Intent();
            intent.setComponent(cn);
            ServiceConnection conn = new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    synchronized (mScreenshotLock) {
                        if (mScreenshotConnection != this) {
                            return;
                        }
                        Messenger messenger = new Messenger(service);
                        Message msg = Message.obtain(null, 1);
                        final ServiceConnection myConn = this;
                        Handler h = new Handler(mHandler.getLooper()) {
                            @Override
                            public void handleMessage(Message msg) {
                                synchronized (mScreenshotLock) {
                                    if (mScreenshotConnection == myConn) {
                                        mContext.unbindService(mScreenshotConnection);
                                        mScreenshotConnection = null;
                                        mHandler.removeCallbacks(mScreenshotTimeout);
                                    }
                                }
                            }
                        };
                        msg.replyTo = new Messenger(h);
                        msg.arg1 = msg.arg2 = 0;
                        if (mStatusBar != null && mStatusBar.isVisibleLw())
                            msg.arg1 = 1;
                        if (mNavigationBar != null && mNavigationBar.isVisibleLw())
                            msg.arg2 = 1;
                        try {
                            messenger.send(msg);
                        } catch (RemoteException e) {
                        }
                    }
                }
                @Override
                public void onServiceDisconnected(ComponentName name) {}
            };
            if (mContext.bindServiceAsUser(
                    intent, conn, Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
                mScreenshotConnection = conn;
                mHandler.postDelayed(mScreenshotTimeout, 10000);
            }
        }
    }

这里通过反射机制调用了TakeScreenshotService的bindServiceAsUser方法,创建TakeScreenshotService服务,再通过其内部的SurfaceControl.screenshot 生成 bitmap,生成图片成功会给系统发送通知。

系统截图的大致流程就是这样,在里面截图原理大致就是:获取需要截屏的区域的宽高,创建一个画布,然后区域内的内容绘制在画布上,最后生成bitmap图片。

二、实现方式

Android 截图主要为四种:View 截图、WebView 截图、屏幕截图、系统截图和 adb 截图。后两种截图不常用,不详细展开。

1. View截图

可以截取到View不可见的部分,生成长图,状态栏和导航栏无法截到
在这里插入图片描述

fun screenshotView(view: ViewGroup):Bitmap?{
    var h = 0
    var bitmap:Bitmap?=null
    for(i in 0 until view.childCount){
        h += view.getChildAt(i).height
        view.getChildAt(i).setBackgroundColor(Color.parseColor("#6CC287"))
    }
    bitmap = Bitmap.createBitmap(view.width, h, Bitmap.Config.RGB_565)
    val canvas = Canvas(bitmap)
    view.draw(canvas)
    //重新赋色
    for(i in 0 until view.childCount){
        view.getChildAt(i).setBackgroundDrawable(null)
    }
    return bitmap
}

2. WebView截图

WebView 作为一种特殊的控件,不能像其他系统 View 或者截屏的方式来截图,有特定的Api
在这里插入图片描述

// 1.capturePicture方法废弃
// 2.getScale方法废弃

// 3.getDrawingCache方法
private static byte[] screenshotWebView() {
  Bitmap bitmap = webView.getDrawingCache();
  byte[] drawByte = getBitmapByte(bmp);
  return drawByte;
}

// 4.draw方法
private static byte[] screenshotWebView() {
  // webView.setDrawingCacheEnabled(true); 设置缓存
  Bitmap bitmap = Bitmap.createBitmap(webView.getWidth(), webView.getHeight(), Bitmap.Config.ARGB_8888);
  Canvas canvas = new Canvas(bitmap);
  webView.draw(canvas);
  webView.destroyDrawingCache();
  byte[] drawByte = getBitmapByte(bitmap);
  return drawByte;
}

可能会截取不到 cavans 元素,原因是开启了硬件加速(关闭硬件加速可能导致页面异常),可在 AndroidManifest.xml 中设置:

android:hardwareAccelerated="false"

截长图的话需要配置:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  WebView.enableSlowWholeDocumentDraw();
}
setContentView(R.layout.webview);

3. 屏幕截图

截取应用当前屏幕的图片:

	/**
     * 获取当前屏幕截图,包含状态栏
     *
     * @param activity activity
     * @return Bitmap
     */
    public static Bitmap captureWithStatusBar(Activity activity) {
        View view = activity.getWindow().getDecorView();
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        Bitmap bmp = view.getDrawingCache();
        int width = getScreenWidth(activity);
        int height = getScreenHeight(activity);
        Bitmap ret = Bitmap.createBitmap(bmp, 0, 0, width, height);
        view.destroyDrawingCache();
        return ret;
    }

    /**
     * 获取当前屏幕截图,不包含状态栏
     *
     * @param activity activity
     * @return Bitmap
     */
    public static Bitmap captureWithoutStatusBar(Activity activity) {
        View view = activity.getWindow().getDecorView();
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        Bitmap bmp = view.getDrawingCache();
        int statusBarHeight = getStatusBarHeight(activity);
        int width = getScreenWidth(activity);
        int height = getScreenHeight(activity);
        Bitmap ret = Bitmap.createBitmap(bmp, 0, statusBarHeight, width, height - statusBarHeight);
        view.destroyDrawingCache();
        return ret;
    }

	/**
     * 得到屏幕的高
     *
     * @param context
     * @return
     */
    public static int getScreenHeight(Context context) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        int height = wm.getDefaultDisplay().getHeight();
        return height;
    }

    /**
     * 得到屏幕的宽
     *
     * @param context
     * @return
     */
    public static int getScreenWidth(Context context) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        int width = wm.getDefaultDisplay().getWidth();
        return width;
    }

	/**
     * 获取状态栏高度
     *
     * @param context 上下文
     * @return 状态栏高度
     */
    public static int getStatusBarHeight(Context context) {
        int result = 0;
        int resourceId = context.getResources()
                .getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = context.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

三、格式转换方法

下面列出了一些常用的转换方法:

// Bitmap 转 Base64
private static String getBitmapString(Bitmap bitmap) {
  String result = null;
  ByteArrayOutputStream out = null;
  try {
    if (bitmap != null) {
      out = new ByteArrayOutputStream();
      bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
​
      out.flush();
      out.close();byte[] bitmapBytes = out.toByteArray();
      result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
    }
  } catch (IOException e) {
      e.printStackTrace();
  } finally {
    try {
      if (out != null) {
          out.flush();
          out.close();
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  return result;
}// Bitmap 转 Byte
private static byte[] getBitmapByte(Bitmap bitmap){
  ByteArrayOutputStream out = new ByteArrayOutputStream();
  // 转换类型,压缩质量,字节流资源
  bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
  try {
      out.flush();
      out.close();
  } catch (IOException e) {
      e.printStackTrace();
  }
  return out.toByteArray();
}

// Drawable 转 Bitmap
public static Bitmap toBitmap(Drawable drawable) {
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    } else if (drawable instanceof ColorDrawable) {
        //color
        Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        canvas.drawColor(((ColorDrawable) drawable).getColor());
        return bitmap;
    } else if (drawable instanceof NinePatchDrawable) {
        //.9.png
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }
    return null;
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Android引导控件是一种用户界面元素,用于向用户展示应用程序的功能和特点,并引导用户完成应用程序中的特定任务或操作。它通常在应用程序第一次运行时出现,或者在应用程序进行重大更新或改进时出现,以便向用户介绍新的功能和改进。Android引导控件可以包括文本、图像、动画和按钮等元素,可以根据应用程序的需要进行自定义配置。 ### 回答2: Android引导控件是一种用户引导功能,它可以帮助用户了解和熟悉应用程序的界面和功能。通常在应用程序第一次打开时使用引导控件,它向用户展示关键的界面元素和操作流程。 Android引导控件可以通过多种方式实现,其中一种常见的方法是使用库或框架。这些库或框架提供了简单易用的API,使开发者可以快速创建和定制引导控件。 通过使用引导控件,开发人员可以在应用程序中添加用户引导,以向用户展示如何使用不同的功能和界面。例如,可以在UI上添加一个蒙版,突出显示关键的按钮、文本框或其他交互元素,并提供步骤指引。用户可以按照指引逐步完成操作,并理解应用程序的各个方面。 引导控件通常可以配置为只在首次启动应用程序时显示,或在每次启动应用程序时都显示。它们也可以根据用户的操作进度来设置触发条件,以确保用户仅在需要时才会看到引导。 通过使用Android引导控件,开发人员可以改善应用程序的用户体验,并帮助新用户更快地掌握应用程序的操作。这不仅有助于提高用户参与度和满意度,还有助于降低用户学习应用程序的难度。 总而言之,Android引导控件是一种有用的交互设计工具,可以帮助开发人员向用户展示应用程序的功能和界面,并提供操作指引,以提高用户体验和参与度。 ### 回答3: Android引导控件是一种用于在应用程序初次运行时展示使用指南或提示的控件。它可以帮助用户了解应用的功能和操作方式,提高用户体验和应用的易用性。 Android引导控件通常以一个弹窗或者一个覆盖在屏幕上方的视图形式出现。开发人员可以自定义引导控件的展示内容,包括文字、图片、按钮等。在用户第一次打开应用时,引导控件会显示相应的提示信息,指引用户完成一些关键操作或者熟悉应用界面。例如,在社交媒体应用中,引导控件可以引导用户创建账号、添加好友或者发布内容。 为了实现引导控件,开发人员需要编写相关的代码逻辑。首先,需要确定在用户初次打开应用时应该显示引导控件的条件。然后,在相应的活动或片段中创建和初始化引导控件,并设置其内容和样式。最后,将引导控件添加到应用界面中,并在用户完成操作后关闭引导控件。除了手动触发外,开发人员还可以在应用配置文件中指定引导控件的展示条件,以实现灵活的控制。 然而,需要注意的是,过多或不必要的引导控件可能会给用户带来困扰,甚至适得其反。因此,在设计引导控件时应该遵循简洁明了、关键明确的原则,确保用户能够快速理解和使用应用。 综上所述,Android引导控件是一种用于向用户展示使用指南或提示的控件。通过合理使用引导控件,开发人员可以提高应用的易用性和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

full courage

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值