Android 截图功能实现

news2024/10/7 16:27:28

Android 截图功能实现

  • 简介
  • 效果图
  • 功能实现
    • 1. 截取当前可见范围屏幕
    • 2. 截取当前可见范围屏幕(不包含状态栏)
    • 3. 截取某个控件
    • 4. 截取ScrollView
    • 5. 长截图
    • 6. 截屏动画效果
    • 7. 显示截屏结果,自动消失
    • 6. 完整代码

简介

在Android应用中开发截图功能涉及到以下几个步骤:获取屏幕内容、处理截图、保存截图等。

效果图

在这里插入图片描述
在这里插入图片描述

功能实现

1. 截取当前可见范围屏幕

/**
 * 截取当前可见范围屏幕
 */
private void screenCapture() {
//        View decorView = getWindow().getDecorView();
//        decorView.setDrawingCacheEnabled(true);// 清空缓存,可用于实时截图
//        decorView.buildDrawingCache();
//        Bitmap screenBitmap = Bitmap.createBitmap(decorView.getDrawingCache());
//        decorView.setDrawingCacheEnabled(false); // 清空缓存,可用于实时截图

    View decorView = getWindow().getDecorView();
    Bitmap screenBitmap = Bitmap.createBitmap(decorView.getWidth(), decorView.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(screenBitmap);
    decorView.draw(canvas);

    updateImageCapture(screenBitmap);
}

2. 截取当前可见范围屏幕(不包含状态栏)

/**
 * 截取当前可见范围屏幕(不包含状态栏)
 */
private void screenCaptureNoStatusBar() {
    View view = getWindow().getDecorView();
    view.setDrawingCacheEnabled(true);
    view.buildDrawingCache();

    // 获取状态栏高度
    Rect rect = new Rect();
    view.getWindowVisibleDisplayFrame(rect);
    int statusBarH = rect.top;
    // 获取屏幕宽高
    int w = view.getWidth();
    int h = view.getHeight();

    // 去掉状态栏
    Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache(), 0, statusBarH, w, h - statusBarH);
    // 销毁缓存信息
    view.destroyDrawingCache();

    updateImageCapture(bitmap);
}

3. 截取某个控件

/**
 * 截取某个控件
 * @param view
 */
private void screenCapture(View view) {
    Bitmap screenBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(screenBitmap);
    view.draw(canvas);

    updateImageCapture(screenBitmap);
}

4. 截取ScrollView

/**
 * 截取ScrollView
 * @param view
 */
private void screenCapture(ScrollView view) {
    int h = 0;
    for (int i = 0; i < view.getChildCount(); i++) {
        h += view.getChildAt(i).getHeight();
        view.getChildAt(i).setBackgroundColor(Color.parseColor("#FFFFFF"));
    }

    Bitmap screenBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), h, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(screenBitmap);
    view.draw(canvas);

    updateImageCapture(screenBitmap);
}

5. 长截图

/**
 * 滚屏截长图
 */
private Runnable scrollRunnable = new Runnable() {

    @SuppressLint("NewApi")
    @Override
    public void run() {

        boolean isToBottom = isScrollToEnd();

        if (isToBottom) {
            Log.i(TAG, "run: to bottom");
            Thread.currentThread().interrupt();
            mHandler.removeCallbacks(scrollRunnable);

            screenCapture(scrollView);

        } else {

            // 未滑动到底部
            int off = linearLayout.getMeasuredHeight() - scrollView.getHeight(); // 判断高度
            if (off > 0) {
                scrollView.scrollBy(0, 6);
                if (scrollView.getScaleY() >= off) {
                    Thread.currentThread().interrupt();
                    mHandler.removeCallbacks(scrollRunnable);
                } else {
                    mHandler.postDelayed(this, 10);
                }
            }

        }
    }
};

6. 截屏动画效果

截屏时有一个缩放的动画效果,缩放到右上角。

  1. 动画效果文件,/res/anim/scale_animation.xml文件。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <scale
        android:duration="500"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="90%"
        android:pivotY="15%"
        android:toXScale="0.35"
        android:toYScale="0.35"/>

    <alpha
        android:duration="200"
        android:fromAlpha="0.5"
        android:toAlpha="1.0"/>

</set>
  1. 代码中使用动画效果
mCardView.startAnimation(animation);

7. 显示截屏结果,自动消失

截屏完成,会将截取的图片显示在界面中,显示截屏3s后会自动消失。

/**
 * 显示截图3s后自动消失
 */
private Runnable captureViewRunnable = new Runnable() {

    @SuppressLint("NewApi")
    @Override
    public void run() {
        mCardView.clearAnimation();

        mCardView.setVisibility(View.INVISIBLE);

    }
};

6. 完整代码

  1. 布局文件:activity_screenshot.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ScreenshotActivity">

    <Button
        android:id="@+id/btn_screenshot"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="60dp"
        android:text="截图"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"/>

    <Button
        android:id="@+id/bnt_long"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="长截图"
        android:layout_marginStart="20dp"
        app:layout_constraintStart_toEndOf="@id/btn_screenshot"
        app:layout_constraintTop_toTopOf="@id/btn_screenshot"
        app:layout_constraintBottom_toBottomOf="@id/btn_screenshot"/>

    <ScrollView
        android:id="@+id/scroll_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/btn_screenshot">

        <LinearLayout
            android:id="@+id/linear_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"/>

    </ScrollView>

    <androidx.cardview.widget.CardView
        android:id="@+id/cardView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent"
        app:contentPadding="3dp"
        app:cardCornerRadius="15dp"
        app:cardElevation="20dp"
        app:cardPreventCornerOverlap="true"
        app:cardUseCompatPadding="true"
        android:visibility="invisible"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent">

        <ImageView
            android:id="@+id/iv_capture"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>

    </androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>
  1. Activity文件:ScreenshotActivity.java
public class ScreenshotActivity extends AppCompatActivity {

    private static final String TAG = "ScreenshotActivity";

    private ScrollView scrollView;
    private LinearLayout linearLayout;
    private ImageView ivScreenshots;
    private CardView mCardView;

    private Handler mHandler = new Handler();

    // 截图动画
    private Animation animation;

    // 截图显示的时间,超时后消失
    private static final int CAPTURE_SHOW_TIMEOUT = 3000;

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

        scrollView = findViewById(R.id.scroll_view);
        linearLayout = findViewById(R.id.linear_layout);
        ivScreenshots = findViewById(R.id.iv_capture);
        mCardView = findViewById(R.id.cardView);
        Button btnScreenshots = findViewById(R.id.btn_screenshot);
        Button btnLong = findViewById(R.id.bnt_long);

        animation = AnimationUtils.loadAnimation(this, R.anim.scale_animation);
        animation.setFillAfter(true);

        // 动态添加textview
        for (int i = 0; i < 50; i++) {
            TextView textView = new TextView(this);
            textView.setText("item-" + (i + 1));
            textView.setGravity(Gravity.CENTER);
            textView.setTextSize(16);
            linearLayout.addView(textView);
        }

        btnScreenshots.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                screenCapture();
//                screenCapture(scrollView);
                screenCapture(scrollView);
//                screenCaptureNoStatusBar();
            }
        });

        btnLong.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mHandler.post(scrollRunnable);
            }
        });

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                mHandler.postDelayed(captureViewRunnable, CAPTURE_SHOW_TIMEOUT);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

    }

    @Override
    protected void onPause() {
        super.onPause();
        mHandler.removeCallbacks(scrollRunnable);
        mHandler.removeCallbacks(captureViewRunnable);
    }

    /**
     * 滚屏的线程
     */
    private Runnable scrollRunnable = new Runnable() {

        @SuppressLint("NewApi")
        @Override
        public void run() {

            boolean isToBottom = isScrollToEnd();

            if (isToBottom) {
                Log.i(TAG, "run: to bottom");
                Thread.currentThread().interrupt();
                mHandler.removeCallbacks(scrollRunnable);

                screenCapture(scrollView);

            } else {

                // 未滑动到底部
                int off = linearLayout.getMeasuredHeight() - scrollView.getHeight(); // 判断高度
                if (off > 0) {
                    scrollView.scrollBy(0, 6);
                    if (scrollView.getScaleY() >= off) {
                        Thread.currentThread().interrupt();
                        mHandler.removeCallbacks(scrollRunnable);
                    } else {
                        mHandler.postDelayed(this, 10);
                    }
                }

            }
        }
    };

    /**
     * 显示截图3s后自动消失
     */
    private Runnable captureViewRunnable = new Runnable() {

        @SuppressLint("NewApi")
        @Override
        public void run() {
            mCardView.clearAnimation();

            mCardView.setVisibility(View.INVISIBLE);

        }
    };

    /**
     * 截取当前可见范围屏幕
     */
    private void screenCapture() {
//        View decorView = getWindow().getDecorView();
//        decorView.setDrawingCacheEnabled(true);// 清空缓存,可用于实时截图
//        decorView.buildDrawingCache();
//        Bitmap screenBitmap = Bitmap.createBitmap(decorView.getDrawingCache());
//        decorView.setDrawingCacheEnabled(false); // 清空缓存,可用于实时截图

        View decorView = getWindow().getDecorView();
        Bitmap screenBitmap = Bitmap.createBitmap(decorView.getWidth(), decorView.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(screenBitmap);
        decorView.draw(canvas);

        updateImageCapture(screenBitmap);
    }

    /**
     * 截取某个控件
     * @param view
     */
    private void screenCapture(View view) {
        Bitmap screenBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(screenBitmap);
        view.draw(canvas);

        updateImageCapture(screenBitmap);
    }

    /**
     * 截取ScrollView
     * @param view
     */
    private void screenCapture(ScrollView view) {
        int h = 0;
        for (int i = 0; i < view.getChildCount(); i++) {
            h += view.getChildAt(i).getHeight();
            view.getChildAt(i).setBackgroundColor(Color.parseColor("#FFFFFF"));
        }

        Bitmap screenBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), h, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(screenBitmap);
        view.draw(canvas);

        updateImageCapture(screenBitmap);
    }

    /**
     * 截取当前可见范围屏幕(不包含状态栏)
     */
    private void screenCaptureNoStatusBar() {
        View view = getWindow().getDecorView();
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();

        // 获取状态栏高度
        Rect rect = new Rect();
        view.getWindowVisibleDisplayFrame(rect);
        int statusBarH = rect.top;
        // 获取屏幕宽高
        int w = view.getWidth();
        int h = view.getHeight();

        // 去掉状态栏
        Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache(), 0, statusBarH, w, h - statusBarH);
        // 销毁缓存信息
        view.destroyDrawingCache();

        updateImageCapture(bitmap);
    }

    private void updateImageCapture(final Bitmap screenBitmap) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mCardView.setVisibility(View.VISIBLE);
                ivScreenshots.setImageBitmap(screenBitmap);
            }
        });
        mCardView.startAnimation(animation);
    }

    /**
     * scrollview是否已经滑到底部
     * @return
     */
    private boolean isScrollToEnd() {
        // 获取 ScrollView 的可视高度
        int visibleHeight = scrollView.getHeight() - scrollView.getPaddingTop() - scrollView.getPaddingBottom();
        // 获取 ScrollView 的子View
        View lastChild = scrollView.getChildAt(scrollView.getChildCount() - 1);
        // 获取 ScrollView 可以滑动的范围
        int scrollRange = scrollView.getChildAt(0).getHeight() - scrollView.getHeight();
        // 获取 ScrollView 的滚动位置
        int scrollY = scrollView.getScrollY();
        // 计算ScrollView底部位置
        int scrollViewBottom = scrollY + visibleHeight;
        // 获取ScrollView的子View的底部位置
        int lastChildBottom = lastChild.getBottom();

        // 判断 ScrollView 是否滚动到底部
        if (scrollY == scrollRange) {
            // 已滑动到底部
            return true;
        } else if (scrollViewBottom >= lastChildBottom) {
            // scrollTo 和 scrollTo 不是同时回调,所以添加两个逻辑都可(可根据需要决定是否需要使用两个逻辑组合)
            // 已滑动到底部
            return true;
        } else {
            return false;
        }
    }

}
  1. 截图动画文件:scale_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <scale
        android:duration="500"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="90%"
        android:pivotY="15%"
        android:toXScale="0.35"
        android:toYScale="0.35"/>

    <alpha
        android:duration="200"
        android:fromAlpha="0.5"
        android:toAlpha="1.0"/>

</set>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/800186.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kibana+Prometheus+node_exporter 监控告警部署

下载好三个软件包 一、prometheus安装部署 1、解压 linxxubuntu:~/module$ tar -xvf prometheus-2.45.0-rc.0.linux-amd64.tar.gz 2、修改配置文件的IP地址 # my global config global:scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is ever…

xshell连接Windows中通过wsl安装的linux子系统-Ubuntu 22.04

xshell连接Windows中通过wsl安装的linux子系统-Ubuntu 22.04 一、安装linux子系统 1.1、 启动或关闭Windows功能-适用于Linux的Windows子系统 1.2 WSL 官方文档 使用 WSL 在 Windows 上安装 Linux //1-安装 WSL 命令 wsl --install//2-检查正在运行的 WSL 版本&#xff1a;…

LeetCode221.Maximal-Square<最大正方形>

题目&#xff1a; 思路&#xff1a; 这题是动态规划&#xff0c;但是不会写。想着判断dp的 <上&#xff0c;左&#xff0c;左上> 去了。发现只能这样最大只能判断面积为4的正方形因为只会判断另外三个方格。而要想判断更大的正方形那就需要递归操作了。那肯定会超时了。…

乌班图安装MySQL5.7时的故障求解

wget https://dev.mysql.com/get/mysql-apt-config_0.8.12-1_all.deb dpkg -i mysql-apt-config_0.8.12-1_all.deb apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 apt update

微信小程序云开发快速入门

什么是云开发&#xff1f; 云开发是可以帮助我们快速成为全栈的一种后端云服务&#xff0c;采用的是Serverless的架构。开发者无须搭建服务器&#xff0c;可直接使用其中的云数据库、云存储、云函数等云服务基础功能。 那么这个时候你可能会想&#xff0c;这和现在的传统开发…

Educational Codeforces Round 152 (Rated for Div. 2) D题 1849D Array Painting

传送门 https://codeforces.com/contest/1849/problem/D D. Array Painting 题意 题解 分类讨论 这题的精髓在于欠债 用一个a0表示当前有没有负债 当前欠债最多为1 首先我们讨论, 在不欠债的情况下遇到0是怎么模拟的: 前面有能够产生贡献的12连通块 例如: 12110 很明显答案…

vue element ui el-tree 通过子节点反向递归查找父节点

今天做了一个项目采用的是element tree组件&#xff0c;要求子父节点不强关联&#xff0c;但是当我点击子节点时&#xff0c;会反向的选择所有的父节点&#xff0c;如下图&#xff1a; 当我点击电话时&#xff0c;往上一层的“电话”和“我的”均为父级以上的节点&#xff0c;全…

Practice1|1207. 独一无二的出现次数、1365. 有多少小于当前数字的数字、941. 有效的山脉数组

1207. 独一无二的出现次数 1.题目&#xff1a; 给你一个整数数组 arr&#xff0c;请你帮忙统计数组中每个数的出现次数。 如果每个数的出现次数都是独一无二的&#xff0c;就返回 true&#xff1b;否则返回 false。 示例 1&#xff1a; 输入&#xff1a;arr [1,2,2,1,1,3…

安卓的播放器对比与选型(vlc,EXOplayer,Ijkplayer,GSYVideoPlayer)详细过程

安卓的播放器对比与选型&#xff08;vlc&#xff0c;EXOplayer&#xff0c;Ijkplayer&#xff0c;GSYVideoPlayer&#xff09;&#x1f4fa;详细过程 前言一、vlc二、EXOplayer三、Ijkplayer四、GSYVideoPlayer&#x1f525;&#x1f525;&#x1f525;五、其他的开源播放器jia…

婚庆服务小程序app开发方案详解

开发一款婚庆行业服务小程序有哪些功能呢&#xff1f; 1、选择分类 选择婚庆、婚车、婚宴、司仪、彩妆、婚庆用品、跟拍、摄影等&#xff0c;筛选出对应的商家 2、选择商家 选择分类后&#xff0c;可以选择商家&#xff0c;查看各个商家的详细介绍情况。 3、选择服务套餐 各…

Linux虚拟机(lvm)报Unmount and run xfs_repair

问题 linux系统没有正常关机&#xff0c;今天启动虚拟机无法进入系统&#xff0c;提示metadata corruption deleted at xxxx&#xff1b; Unmount and run xfs_repair 分析 主机异常掉电后里面的虚拟机无法启动&#xff0c;主要是损坏的分区 解决 看出来应该是dm-0分区损坏…

应急响应经典案例-FTP 暴力破解

应急响应经典案例-FTP 暴力破解 应急场景日志分析应急处理措施 应急场景 从昨天开始&#xff0c;网站响应速度变得缓慢&#xff0c;网站服务器登录上去非常卡&#xff0c;重启服务器就能保证一段时间的正常访问&#xff0c;网站响应状态时而飞快时而缓慢&#xff0c;多数时间是…

华为云安装MySQL后,本地工具连接MySQL失败

华为云安装MySQL后&#xff0c;本地连接失败 排查问题步骤&#xff1a; 在此之前需要在MySQL创建用户&#xff0c;并赋予权限。 1、能否ping通。 在本地命令行(Windows&#xff1a;winR)通过ping命令&#xff0c;ping服务器地址&#xff0c;看能否ping通。不能则需要检查本地…

Redis服务优化

目录 一.Rde高可用 二.Rdies持久化 2.1持久化的功能 2.2Redis 提供两种方式进行持久化 三.RDB持久化 3.1触发条件 3.1.1手动触发 3.1.2自动触发 3.1.3其他自动触发机制 3.1.4执行流程 3.1.5启动时加载 四.AOF持久化 4.1开启AOF 4.2执行流程 4.2.1命令追加(append) 4.2.2文件写…

ThinkPHP8知识详解:安装ThinkPHP8

我们在讲解前面的文章《搭建PHP8集成环境》和《给PHP8和MySQL8添加到环境变量》以后&#xff0c;现在可以正式的安装ThinkPHP8啦、 1、打开phpenv&#xff0c;启动服务&#xff0c;打开昨天新建的tp8.com的目录&#xff08;D:\phpEnv\www\tp8.com&#xff09;&#xff0c;把里…

LLM系列 | 18 : 如何用LangChain进行网页问答

简介 一夕轻雷落万丝&#xff0c;霁光浮瓦碧参差。 紧接之前LangChain专题文章&#xff1a; 15:如何用LangChain做长文档问答&#xff1f;16:如何基于LangChain打造联网版ChatGPT&#xff1f;17:ChatGPT应用框架LangChain速成大法 今天这篇小作文是LangChain实践专题的第4…

关于element ui 安装失败的问题解决方法并查看是否安装成功

报错信息&#xff1a; 解决办法&#xff1a; 使用命令&#xff1a; npm install --legacy-peer-deps element-ui --save 查看是否安装成功&#xff1a; 方法一&#xff1a;在package.json文件中查看是否有element-ui版本 方法二&#xff1a;查看node_modules目录下是否有elem…

宝塔设置云服务器mysql端口转发,实现本地电脑访问云mysql

环境&#xff1a;centos系统使用宝塔面板 实现功能&#xff1a;宝塔设置云服务器mysql端口转发&#xff0c;实现本地电脑访问mysql 1.安装mysql、PHP-7.4.33、phpMyAdmin 5.0 软件商店》搜索 mysql安装即可 软件商店》搜索 PHP安装7.4.33即可&#xff08;只需要勾选快速安装&…

Vue 3:玩一下web前端技术(一)

前言 本章内容为VUE前端环境搭建与相关前端技术讨论。 下一篇文章地址&#xff1a; &#xff08;暂无&#xff09; 一、环境搭建 1. 安装Node.js Vue是基于Node.js的&#xff0c;因此首先需要安装Node.js。官网地址&#xff1a;Node.js 2. 安装Vue CLI Vue CLI是一个用…

简要介绍 | 自编码器:神经网络中的自我复制艺术

注1&#xff1a;本文系“简要介绍”系列之一&#xff0c;仅从概念上对自编码器进行非常简要的介绍&#xff0c;不适合用于深入和详细的了解。 自编码器&#xff1a;神经网络中的自我复制艺术 Autoencoders Explained - MATLAB & Simulink 一、背景介绍 自编码器&#xff0…