Android 截图功能实现

简介

在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>

猜你喜欢

转载自blog.csdn.net/tracydragonlxy/article/details/131901893