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")@Overridepublic 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. 截屏动画效果
截屏时有一个缩放的动画效果,缩放到右上角。
- 动画效果文件,/res/anim/scale_animation.xml文件。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><scaleandroid:duration="500"android:fromXScale="1.0"android:fromYScale="1.0"android:pivotX="90%"android:pivotY="15%"android:toXScale="0.35"android:toYScale="0.35"/><alphaandroid:duration="200"android:fromAlpha="0.5"android:toAlpha="1.0"/></set>
- 代码中使用动画效果
mCardView.startAnimation(animation);
7. 显示截屏结果,自动消失
截屏完成,会将截取的图片显示在界面中,显示截屏3s后会自动消失。
/*** 显示截图3s后自动消失*/
private Runnable captureViewRunnable = new Runnable() {@SuppressLint("NewApi")@Overridepublic void run() {mCardView.clearAnimation();mCardView.setVisibility(View.INVISIBLE);}
};
6. 完整代码
- 布局文件:
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"><Buttonandroid: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"/><Buttonandroid: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"/><ScrollViewandroid:id="@+id/scroll_view"android:layout_width="match_parent"android:layout_height="wrap_content"app:layout_constraintTop_toBottomOf="@id/btn_screenshot"><LinearLayoutandroid:id="@+id/linear_layout"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"/></ScrollView><androidx.cardview.widget.CardViewandroid: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"><ImageViewandroid: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>
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;@Overrideprotected 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);// 动态添加textviewfor (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() {@Overridepublic void onClick(View v) {
// screenCapture();
// screenCapture(scrollView);screenCapture(scrollView);
// screenCaptureNoStatusBar();}});btnLong.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mHandler.post(scrollRunnable);}});animation.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {}@Overridepublic void onAnimationEnd(Animation animation) {mHandler.postDelayed(captureViewRunnable, CAPTURE_SHOW_TIMEOUT);}@Overridepublic void onAnimationRepeat(Animation animation) {}});}@Overrideprotected void onPause() {super.onPause();mHandler.removeCallbacks(scrollRunnable);mHandler.removeCallbacks(captureViewRunnable);}/*** 滚屏的线程*/private Runnable scrollRunnable = new Runnable() {@SuppressLint("NewApi")@Overridepublic 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")@Overridepublic 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() {@Overridepublic 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 的子ViewView 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;}}}
- 截图动画文件:
scale_animation.xml
。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><scaleandroid:duration="500"android:fromXScale="1.0"android:fromYScale="1.0"android:pivotX="90%"android:pivotY="15%"android:toXScale="0.35"android:toYScale="0.35"/><alphaandroid:duration="200"android:fromAlpha="0.5"android:toAlpha="1.0"/></set>