系统截屏分析(AndroidO)

1 概述

848方案系统自带的截屏功能主要两种,一种是截取全屏,一种是区域截屏。两种触发方式都是在 PhoneWindowManager 监听按键使用 Handler 来分发消息进行处理。

截屏功能的具体实现主要在 SystemUI,通过接收消息调用截屏服务 TakeScreenshotService 来对两种方式进行,具体的功能全部逻辑在 GlobalScreenshot 这个类中,主要做了几件事:

  1. 加载截屏布局页面
  2. 初始化相关功能的handler、listener
  3. 截取全屏、区域截屏(动画、保存、删除)

2 时序图

android 后台service截屏 android 截屏实现_全屏

3 流程分析

3.1 按键监听

监听按键 KeyEvent.KEYCODE_SYSRQ,handler post 一个截屏请求任务, TAKE_SCREENSHOT_SELECTED_REGION 为区域截屏, TAKE_SCREENSHOT_FULLSCREEN 为全屏截屏。

// frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
@Override
public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
    final int keyCode = event.getKeyCode();
    final int repeatCount = event.getRepeatCount();
    final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
    ...
} else if (keyCode == KeyEvent.KEYCODE_SYSRQ) {
    if (down && repeatCount == 0) {
        mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); // 截屏类型
        mHandler.post(mScreenshotRunnable);
    }
    return -1;
}
}

截屏任务调用 takeScreenshot 方法

private final ScreenshotRunnable mScreenshotRunnable = new ScreenshotRunnable();
	private class ScreenshotRunnable implements Runnable {
        private int mScreenshotType = TAKE_SCREENSHOT_FULLSCREEN;
        public void setScreenshotType(int screenshotType) {
            mScreenshotType = screenshotType;
        }
        @Override
        public void run() {
            takeScreenshot(mScreenshotType); // 
        }
    }

takeScreenshot 方法主要用于截屏服务的绑定及超时的错误通知。

private static final String SYSUI_PACKAGE = "com.android.systemui";
    private static final String SYSUI_SCREENSHOT_SERVICE =
            "com.android.systemui.screenshot.TakeScreenshotService";
    private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
            "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";

	private void takeScreenshot(final int screenshotType) {
        synchronized (mScreenshotLock) {
            if (mScreenshotConnection != null) {
                return;
            }
            // com.android.systemui.screenshot.TakeScreenshotService
            final ComponentName serviceComponent = new ComponentName(SYSUI_PACKAGE,
                    SYSUI_SCREENSHOT_SERVICE);
            final Intent serviceIntent = new Intent();
            serviceIntent.setComponent(serviceComponent);
            
            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, screenshotType);
                        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) {
                    ...
                }
            };
            
            // 绑定截屏服务 TakeScreenshotService
            if (mContext.bindServiceAsUser(serviceIntent, conn,
                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, UserHandle.CURRENT)) {
                mScreenshotConnection = conn;
                // 10s超时通知
                mHandler.postDelayed(mScreenshotTimeout, 10000);
            }
        }
    }

3.2 截屏服务

截屏服务内调用 GlobalScreenshot 类处理截屏逻辑

// frameworks\base\packages\SystemUI\src\com\android\systemui\screenshot\TakeScreenshotService.java
public class TakeScreenshotService extends Service {
    private static final String TAG = "TakeScreenshotService";
    private static GlobalScreenshot mScreenshot;	// 全局截屏处理类
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            final Messenger callback = msg.replyTo;
            // finisher是为了当截屏不可用/完成时,回调给PhoneWindowManager处理相关逻辑
            Runnable finisher = new Runnable() {
                @Override
                public void run() {
                    Message reply = Message.obtain(null, 1);
                    try {
                        callback.send(reply);
                    } catch (RemoteException e) {
                    }
                }
            };

            // 如果该用户的存储空间被锁定,则无法存储截图, 
            if (!getSystemService(UserManager.class).isUserUnlocked()) {
                Log.w(TAG, "Skipping screenshot because storage is locked!");
                post(finisher);
                return;
            }

            // 实例化 GlobalScreenshot
            if (mScreenshot == null) {
                mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
            }
            switch (msg.what) {
                case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: // 全屏截屏处理
                    mScreenshot.takeScreenshot(finisher, msg.arg1 > 0, msg.arg2 > 0);
                    break;
                case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:	//区域截屏处理
                 mScreenshot.takeScreenshotPartial(finisher, msg.arg1 > 0, msg.arg2 > 0);
                    break;
            }
        }
    };
	...
}

3.3 截取全屏/区域

3.3.1 takeScreenshot()

区域截屏的逻辑比全屏的多一点,其实都是差不多的。

// frameworks\base\packages\SystemUI\src\com\android\systemui\screenshot\GlobalScreenshot.java
// 全屏截图
void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
    mDisplay.getRealMetrics(mDisplayMetrics);
    takeScreenshot(finisher, statusBarVisible, navBarVisible, 0, 0, mDisplayMetrics.widthPixels, 				                                                                  mDisplayMetrics.heightPixels);
}
// 区域截图
void takeScreenshotPartial(final Runnable finisher, final boolean statusBarVisible,
                           final boolean navBarVisible) {
    // addView添加区域截屏布局
    mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
    // 处理手势滑动选择的区域
    mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            ScreenshotSelectorView view = (ScreenshotSelectorView) v;
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    view.startSelection((int) event.getX(), (int) event.getY());
                    return true;
                case MotionEvent.ACTION_MOVE:
                    view.updateSelection((int) event.getX(), (int) event.getY());
                    return true;
                case MotionEvent.ACTION_UP:	// 抬起时代表选择完成
                    // 移除区域选择界面
                    view.setVisibility(View.GONE);
                    mWindowManager.removeView(mScreenshotLayout);
                    final Rect rect = view.getSelectionRect();
                    if (rect != null) {
                        if (rect.width() != 0 && rect.height() != 0) {
                            mScreenshotLayout.post(new Runnable() {
                                public void run() {
                                    // 最终都是调用这个方法, 只不过这里的参数时区域选择后得到的参数rect
                                    takeScreenshot(finisher, statusBarVisible, navBarVisible,
                                                   rect.left, rect.top, rect.width(), rect.height());
                                }
                            });
                        }
                    }
                    view.stopSelection();
                    return true;
            }
            return false;
        }
    });
    mScreenshotLayout.post(new Runnable() {
        @Override
        public void run() {
            //显示选择视图
            mScreenshotSelectorView.setVisibility(View.VISIBLE);
            mScreenshotSelectorView.requestFocus();
        }
    });
}
void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible,
            int x, int y, int width, int height) {
        mDisplay.getRealMetrics(mDisplayMetrics);
        float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
        // 获取当前屏幕旋转度数
        float degrees = getDegreesForRotation(mDisplay.getRotation());
        boolean requiresRotation = (degrees > 0);
        if (requiresRotation) {	// 如果屏幕处于已旋转状态, 则获取当前设备的原始尺寸并重新设置
            mDisplayMatrix.reset();
            mDisplayMatrix.preRotate(-degrees);
            mDisplayMatrix.mapPoints(dims);
            dims[0] = Math.abs(dims[0]);
            dims[1] = Math.abs(dims[1]);
        }

        // 截屏,这里直接调用了 SurfaceControl 类的静态方法 screenshot, 再往下就是 native 方法
        mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
        if (mScreenBitmap == null) {
            notifyScreenshotError(mContext, mNotificationManager,
                    R.string.screenshot_failed_to_capture_text);
            finisher.run(); // 回调
            return;
        }

        if (requiresRotation) {	// 如果屏幕处于已旋转状态
            Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
                    mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
            Canvas c = new Canvas(ss);
            c.translate(ss.getWidth() / 2, ss.getHeight() / 2);//截图宽高分别折为屏幕高宽的一半
            c.rotate(degrees);		// 截图旋转指定度数
            c.translate(-dims[0] / 2, -dims[1] / 2);
            c.drawBitmap(mScreenBitmap, 0, 0, null);
            c.setBitmap(null);
            // 回收以前的位图
            mScreenBitmap.recycle();
            mScreenBitmap = ss;
        }

        if (width != mDisplayMetrics.widthPixels || height != mDisplayMetrics.heightPixels) {
            //区域截屏判断, 截取指定位置宽高的Bitmap
            Bitmap cropped = Bitmap.createBitmap(mScreenBitmap, x, y, width, height);
            mScreenBitmap.recycle();
            mScreenBitmap = cropped;
        }

        // Optimizations
        mScreenBitmap.setHasAlpha(false);
        mScreenBitmap.prepareToDraw();

        // 开始截图动画
        startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
                statusBarVisible, navBarVisible);
    }
3.3.2 SurfaceControl.screenshot()

如何获取当前屏幕的Bitmap

// frameworks\base\core\java\android\view\SurfaceControl.java
public static Bitmap screenshot(int width, int height) {
    IBinder displayToken = SurfaceControl.getBuiltInDisplay(
        SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
    return nativeScreenshot(displayToken, new Rect(), width, height, 0, 0, true,
                            false, Surface.ROTATION_0);
}

// 调用 native 方法
private static native Bitmap nativeScreenshot(IBinder displayToken,
            Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
            boolean allLayers, boolean useIdentityTransform, int rotation);
3.3.3 startAnimation()

再来看一下截图后的动画,这里做了一些比较重要的事:

  1. 设置动画
  2. 播放截屏提示声音
  3. 播放动画
  4. 动画完成后保存截图
  5. 截图成功通知
private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,
            boolean navBarVisible) {
        // 设置动画视图
        mScreenshotView.setImageBitmap(mScreenBitmap);
        mScreenshotLayout.requestFocus();

        if (mScreenshotAnimation != null) {
            if (mScreenshotAnimation.isStarted()) {
                mScreenshotAnimation.end();
            }
            mScreenshotAnimation.removeAllListeners();
        }

        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
        // 可在这里自定义截图动画
        ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
        ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h,
                statusBarVisible, navBarVisible);
        mScreenshotAnimation = new AnimatorSet();
        mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
        mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                // 动画结束后,保存屏幕截图 (截图通知,保存成功通知)
                saveScreenshotInWorkerThread(finisher);
                
                mWindowManager.removeView(mScreenshotLayout);
                mScreenBitmap = null;
                mScreenshotView.setImageBitmap(null);
            }
        });
        mScreenshotLayout.post(new Runnable() {
            @Override
            public void run() {
                //播放快门音以通知用户已截屏
                mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
                mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                mScreenshotView.buildLayer();
                mScreenshotAnimation.start(); 
            }
        });
    }

3.4 保存截图

使用 SaveImageInBackgroundTask保存截图

private void saveScreenshotInWorkerThread(Runnable finisher) {
        SaveImageInBackgroundData data = new SaveImageInBackgroundData();
        data.context = mContext;
        data.image = mScreenBitmap;
        data.iconSize = mNotificationIconSize;
        data.finisher = finisher;
        data.previewWidth = mPreviewWidth;
        data.previewheight = mPreviewHeight;
        if (mSaveInBgTask != null) {
            mSaveInBgTask.cancel(false);
        }
        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data, mNotificationManager).execute();
    }

SaveImageInBackgroundTask 这个类是 GlobalScreenshot 的内部类,用于在后台保存屏幕截图

构造方法 SaveImageInBackgroundTask():

  1. 初始化保存截图需要的数据
  2. 构造截图相关的通知
SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data,
            NotificationManager nManager) {
        Resources r = context.getResources();
 
        mParams = data;
        mImageTime = System.currentTimeMillis();
        String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime));
    	// 截图文件名
        mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);

        File dirFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
    	// 是否有外部磁盘可用 
        MStorageManager storageManager = MStorageManager.getInstance(context);
        String[] volumes = storageManager.getVolumePaths();
        if(volumes != null && volumes.length > 1 ){
            dirFile = new File(volumes[1]+"/Pictures");
        }
        mScreenshotDir = new File(dirFile, SCREENSHOTS_DIR_NAME);
    	// 截图全路径
        mImageFilePath = new File(mScreenshotDir, mImageFileName).getAbsolutePath();
        mImageWidth = data.image.getWidth();
        mImageHeight = data.image.getHeight();
        int iconSize = data.iconSize;
        int previewWidth = data.previewWidth;
        int previewHeight = data.previewheight;
	    ...
        // 通知截图正在保存...
        mNotificationManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT,mNotificationBuilder.build());
    }

doInBackground():

  1. 创建屏幕截图目录,保存截图
  2. 将屏幕截图保存到 MediaStore
  3. 为通知创建共享、删除操作
protected Void doInBackground(Void... params) {
        if (isCancelled()) {
            return null;
        }

        // AsyncTask 将工作线程设置为具有后台线程优先级
        Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);

        Context context = mParams.context;
        Bitmap image = mParams.image;
        Resources r = context.getResources();

        try {
            // 创建屏幕截图目录
            mScreenshotDir.mkdirs();
            long dateSeconds = mImageTime / 1000;

            // 保存截图
            OutputStream out = new FileOutputStream(mImageFilePath);
            image.compress(Bitmap.CompressFormat.PNG, 100, out);
            out.flush();
            out.close();

            // 将屏幕截图保存到 MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            ...
            Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

            // 为通知创建共享操作, TargetChosenReceiver 删除屏幕快照的通知
            ...
            mNotificationBuilder.addAction(shareActionBuilder.build());

            // 为通知创建删除操作,详见 DeleteScreenshotReceiver
            ...
            mNotificationBuilder.addAction(deleteActionBuilder.build());
            mParams.imageUri = uri;
            mParams.image = null;
            mParams.errorMsgResId = 0;
        } catch (Exception e) {
            //如果未安装外部存储,则可能引发IOExceptionUnsupportedOperationException
            mParams.clearImage();
            mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
        }
        // 回收位图数据
        if (image != null) {
            image.recycle();
        }
        return null;
    }

onPostExecute():

通知屏幕截图已保存

protected void onPostExecute(Void params) {
        if (mParams.errorMsgResId != 0) { // 在 doInBackground 保存截图失败时设置的标志
            // 提示无法将图像保存到磁盘
            GlobalScreenshot.notifyScreenshotError(mParams.context, mNotificationManager,mParams.errorMsgResId);
        } else {
            ...
            mNotificationManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT,
                    mNotificationBuilder.build());
        }
        mParams.finisher.run();
        mParams.clearContext();
    }

以上就是系统截图的全部流程,调用链比较简单。

4 客制化

截图后将当前界面作为浮窗缩放到屏幕右下角停留几秒,停留期间可响应触摸事件(自行定义:滑动、点击、长按),无操作后在指定时间浮窗进行动画消失。

  1. 截图动画

在 startAnimation() 方法中替换之前截图后动画

private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,boolean navBarVisible) {
    // 直接在这里修改获取动画的方法
    ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation(w, h); 
    //ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h, statusBarVisible, navBarVisible);
    mScreenshotAnimation = new AnimatorSet();
    mScreenshotAnimation.playSequentially(screenshotDropInAnim);
}

修改后的动画:

// frameworks\base\packages\SystemUI\src\com\android\systemui\screenshot\GlobalScreenshot.java
private ValueAnimator createScreenshotDropInAnimation(int w, int h) {
        final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)  / SCREENSHOT_DROP_IN_DURATION);
        final float flashDurationPct = 2f * flashPeakDurationPct;
        final Interpolator flashAlphaInterpolator = new Interpolator() {
            @Override
            public float getInterpolation(float x) {
                if (x <= flashDurationPct) {
                    return (float) Math.sin(Math.PI * (x / flashDurationPct));
                }
                return 0;
            }
        };
        final Interpolator scaleInterpolator = new Interpolator() {
            @Override
            public float getInterpolation(float x) {
                if (x < flashPeakDurationPct) {
                    return 0;
                }
                return (x - flashDurationPct) / (1f - flashDurationPct);
            }
        };
        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
        anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
        anim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                mScreenshotView.setAlpha(0f);
                mScreenshotView.setTranslationX(0f);
                mScreenshotView.setTranslationY(0f);
                mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);
                mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);
                mScreenshotView.setVisibility(View.VISIBLE);
                mScreenshotFlash.setAlpha(0f);
                mScreenshotFlash.setVisibility(View.VISIBLE);
            }
            @Override
            public void onAnimationEnd(android.animation.Animator animation) {
                mScreenshotFlash.setVisibility(View.GONE);
                mHandler.removeMessages(MSG_CLOSE_SCREENSHOT);
                // 截图缩小、停留至右下角,无操作5s后截图向右淡出
                mHandler.sendEmptyMessageDelayed(MSG_CLOSE_SCREENSHOT, TIME_CLOSE_SCREENSHOT);
            }
        });

        float halfScreenWidth = (w - 2f * mBgPadding) / 2f;
        float halfScreenHeight = (h - 2f * mBgPadding) / 2f;
        final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET;
        final PointF finalPos = new PointF(
                -halfScreenWidth + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
                -halfScreenHeight + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);
        anim.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float t = (Float) animation.getAnimatedValue();

                float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
                        - scaleInterpolator.getInterpolation(t)
                        * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
                mScreenshotView.setAlpha(t);
                mScreenshotView.setScaleX(scaleT * 0.5f);
                mScreenshotView.setScaleY(scaleT * 0.5f);
                mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t));
                mScreenshotView.setTranslationX(t * finalPos.x);
                mScreenshotView.setTranslationY(- t * finalPos.y);
            }
        });
        return anim;
    }
  1. 自定义触摸事件

在初始化监听器时为 mScreenshotView 添加相关的触摸监听器

mScreenshotView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        boolean actionUp = event.getAction() == MotionEvent.ACTION_UP;
        boolean touchEvent = mGestureDetector.onTouchEvent(event);
        if (!touchEvent && actionUp) {
            int[] locationOnScreen = mScreenshotView.getLocationOnScreen();
            int left = locationOnScreen[0];
            int dx = (int) (event.getRawX() - sx);
            if (dx < 0 && left < 0) {
                closeScreenshotView(true);
            } else {
                mHandler.sendEmptyMessageDelayed(MSG_CLOSE_SCREENSHOT, TIME_CLOSE_SCREENSHOT);
            }
        }
        return true;
    }
});
mGestureDetector = new GestureDetector(mContext, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onDown(MotionEvent e) { // 按下
                sx = (int) e.getRawX();
                mHandler.removeMessages(MSG_CLOSE_SCREENSHOT);
                return true;
            }

            @Override
            public boolean onSingleTapUp(MotionEvent e) { // 单击
                cropScreenshotImage();
                return true;
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // 拖动
                int dx = (int) (e2.getX() - e1.getX());
                int left = mScreenshotView.getLeft();
                int top = mScreenshotView.getTop();
                int right = mScreenshotView.getRight();
                int bottom = mScreenshotView.getBottom();
                Log.d("wugm", "onScroll " + "dx:" + dx);
                if (dx < 0) {
                    mScreenshotView.layout(left + dx, top, right + dx, bottom);
//                    if (mLayoutImageButton.getVisibility() == View.VISIBLE) {
//                        mLayoutBtnVisible = false;
//                        mLayoutImageButton.setVisibility(View.INVISIBLE);
//                    }
                }
                return false;
            }

            @Override
            public void onLongPress(MotionEvent e) { // 长按
                mHandler.sendEmptyMessageDelayed(MSG_CLOSE_SCREENSHOT, TIME_CLOSE_SCREENSHOT);
//                showImageButton();   todo
            }
        });

单击截图窗口打开图片浏览、裁剪界面(自行修改)

private void cropScreenshotImage() {
    closeScreenshotView(true);
    Intent intent = new Intent();
    intent.putExtra(REGION_WIDTH, mRegionWidth);
    intent.putExtra(REGION_HEIGHT, mRegionHeight);
    intent.putExtra(IMAGE_FILE_PATH, mImageFilePath);
    String packageName = "com.xxx.cropimage";
    String className = "com.xxx.cropimage.CropViewActivity";
    intent.setClassName(packageName, className);
    Uri sourceUri = null;
    try {
        sourceUri = Uri.parse(MediaStore.Images.Media.insertImage(mContext.getContentResolver(), mScreenBitmap, null,null));
    } catch (Exception e) {
        Log.e(TAG, e.getMessage());
        sourceUri = Uri.parse("file://" + mImageFilePath);
    }
    intent.setData(sourceUri);
    mContext.startActivity(intent);
}