안드로이드 스크린샷 기능 구현

소개

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. 스크린샷 결과를 표시하고 자동으로 사라집니다.

스크린샷이 완료되면 캡처한 사진이 인터페이스에 표시되며, 스크린샷 표시 후 3초가 지나면 자동으로 사라집니다.

/**
 * 显示截图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