美文网首页
Android实现截屏分享

Android实现截屏分享

作者: white_or_black | 来源:发表于2019-07-22 14:19 被阅读0次

    首先需要添加权限,同时动态检测读写权限(如果未开启读写权限,则不会触发截屏的监听):

      <!-- 用于监听裁剪之后的图片库的动态 -->
        <uses-permission android:name="MediaStore.Images.Media.INTERNAL_CONTENT_URI" />
        <uses-permission android:name="MediaStore.Images.Media.EXTERNAL_CONTENT_URI" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    

    一、核心类:ScreenShotManager

    public class ScreenShotManager {
    
        private Context mContext;
        public static final String TAG = "ScreenShotManager";
    
        private ContentObserver mInternalObserver;     //内部存储器内容观察者
        private ContentObserver mExternalObserver;     //外部存储器内容观察者
    
    
        //匹配各个手机截屏路径的关键字
        private static final String[] KEYWORDS = {
                "screenshot", "screenshots", "screen_shot", "screen-shot", "screen shot",
                "screencapture", "screen_capture", "screen-capture", "screen capture",
                "screencap", "screen_cap", "screen-cap", "screen cap", "截屏"
        };
    
        //读取媒体数据库时需要读取的列
        private static final String[] MEDIA_PROJECTIONS = {
                MediaStore.Images.ImageColumns.DATA,
                MediaStore.Images.ImageColumns.DATE_TAKEN,
        };
    
    
        public ScreenShotManager(final Context context) {
            mContext = context;
            initManager();
        }
    
        /**
         * 初始化
         */
        private void initManager() {
            if (mContext == null) {
                return;
            }
            final Handler handler = new Handler(mContext.getMainLooper());
            mInternalObserver = new MediaContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, handler);
            mExternalObserver = new MediaContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, handler);
        }
    
        /**
         * 添加监听
         */
        public void startListener() {
            if (mContext == null) {
                return;
            }
            mContext.getContentResolver().registerContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, false, mInternalObserver);
            mContext.getContentResolver().registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false, mExternalObserver);
        }
    
        /**
         * 注销监听
         */
        public void stopListener() {
            if (mContext == null) {
                return;
            }
            mContext.getContentResolver().unregisterContentObserver(mInternalObserver);
            mContext.getContentResolver().unregisterContentObserver(mExternalObserver);
        }
    
        /**
         * 检查是否大于当前时间五秒(兼容小米),是则舍弃,反之亦然
         *
         * @param dateTime 图片保存时间
         * @return true 符合预期
         */
        private boolean checkTime(final long dateTime) {
            return System.currentTimeMillis() - dateTime < 5 * 1000;
        }
    
    
        /**
         * 判断是否是截屏
         */
        private boolean checkScreenShot(String data) {
            data = data.toLowerCase();
            // 判断图片路径是否含有指定的关键字之一, 如果有, 则认为当前截屏了
            for (String keyWork : KEYWORDS) {
                if (data.contains(keyWork)) {
                    return true;
                }
            }
            return false;
        }
    
        /**
         * 处理监听到的事件(当图片库发生变化是会触发)
         *
         * @param contentUri contentUri
         */
        private void handleMediaContentChange(Uri contentUri) {
            Cursor cursor = null;
            try {
                // 数据改变时查询数据库中最后加入的一条数据
                cursor = mContext.getContentResolver().query(
                        contentUri,
                        MEDIA_PROJECTIONS,
                        null,
                        null,
                        MediaStore.Images.ImageColumns.DATE_ADDED + " desc limit 1"
                );
    
                if (cursor == null) {
                    return;
                }
                if (!cursor.moveToFirst()) {
                    return;
                }
    
                // 获取各列的索引
                int dataIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                int dataData = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN);
                // 获取行数据
                String data = cursor.getString(dataIndex);
                long dateTime = cursor.getLong(dataData);
                // 处理获取到的第一行数据
                if (checkTime(dateTime)) {
                    handleMediaRowData(data);
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (cursor != null && !cursor.isClosed()) {
                    cursor.close();
                }
            }
        }
    
    
        /**
         * 处理监听到的资源
         */
        private void handleMediaRowData(String data) {
            if (checkScreenShot(data)) {
                if (TextUtils.isEmpty(data)) {
                    return;
                }
    
                Intent intent = new Intent(mContext, ShotShareActivity.class);
                intent.putExtra("snapshot_path", data);
                mContext.startActivity(intent);
            } else {
                Log.e(TAG, "Not screenshot event:" + data);
            }
        }
    
    
        /**
         * 媒体内容观察者(观察媒体数据库的改变)
         */
        private class MediaContentObserver extends ContentObserver {
    
            private Uri mContentUri;
    
            MediaContentObserver(Uri contentUri, Handler handler) {
                super(handler);
                mContentUri = contentUri;
            }
    
            @Override
            public void onChange(boolean selfChange) {
                super.onChange(selfChange);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        handleMediaContentChange(mContentUri);
                    }
                }).start();
            }
        }
    
    }
    

    二、截屏展示界面

    public class ShotShareActivity extends AppCompatActivity {
    
        private ImageView ivContent;
    
        private int count;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_shot_share);
            String originPath = getIntent().getStringExtra("snapshot_path");
            initIvContent(originPath);
        }
    
        private void initIvContent(String path) {
            ivContent = findViewById(R.id.iv_content);
            //状态栏的高度
            int statusHeight = Utils.getStatusBarHeight(this);
            //虚拟导航栏的高度
            int navHeight = Utils.getNavigationBarHeight(this);
            float width = Utils.getScreenWidth(this) - Utils.dp2px(this, 116);
            float ratio = (float) Utils.div(Utils.getScreenWidth(this), Utils.getScreenHeight(this) - statusHeight - navHeight, 2);
            float height = width / ratio;
            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
            layoutParams.height = (int) height;
            ivContent.setLayoutParams(layoutParams);
            //魅族手机生成的截图文件带有 "-" 的命名,会导致获取bitmap为null。利用Glide生成的bitmap
            loadImage(path, statusHeight, navHeight);
        }
    
        /**
         * 加载截屏文件,为了防止加载失败,可以重复加载(最多5次)
         *
         * @param path         截屏文件路径
         * @param statusHeight 状态栏高度
         * @param navHeight    虚拟导航栏高度
         */
        private void loadImage(final String path, final int statusHeight, final int navHeight) {
            Glide.with(this).asBitmap().load(path).into(new SimpleTarget<Bitmap>() {
                @Override
                public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
                    //源文件生成的bitmap
                    //裁剪bitmap,去掉状态栏和底部的菜单栏(x+width must be < bitmap.width())
                    try {
                        Bitmap resultBitmap = Bitmap.createBitmap(resource, 0, statusHeight, resource.getWidth(), resource.getHeight() - statusHeight - navHeight);
                        ivContent.setImageBitmap(resultBitmap);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
    
                @Override
                public void onLoadFailed(Drawable errorDrawable) {
                    super.onLoadFailed(errorDrawable);
                    //加载失败的情况下重新加载一次,最多加载五次
                    if (count <= 5) {
                        count = count + 1;
                        loadImage(path, statusHeight, navHeight);
                    }
    
                }
            });
        }
    }
    

    三、Util类

    public class Utils {
    
        /**
         * Return the width of screen, in pixel.
         *
         * @return the width of screen, in pixel
         */
        public static int getScreenWidth(Context context) {
            WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            if (wm == null) {
                return context.getResources().getDisplayMetrics().widthPixels;
            }
            Point point = new Point();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                wm.getDefaultDisplay().getRealSize(point);
            } else {
                wm.getDefaultDisplay().getSize(point);
            }
            return point.x;
        }
    
        /**
         * Return the height of screen, in pixel.
         *
         * @return the height of screen, in pixel
         */
        public static int getScreenHeight(Context context) {
            WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            if (wm == null) {
                return context.getResources().getDisplayMetrics().heightPixels;
            }
            Point point = new Point();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                wm.getDefaultDisplay().getRealSize(point);
            } else {
                wm.getDefaultDisplay().getSize(point);
            }
            return point.y;
        }
    
    
        /**
         * 获取状态栏高度
         *
         * @param context context
         * @return 状态栏高度
         */
        public static int getStatusBarHeight(Context context) {
            // 获得状态栏高度
            int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
            return context.getResources().getDimensionPixelSize(resourceId);
        }
    
        /**
         * 虚拟导航栏是否显示
         *
         * @param activity activity
         * @return boolean
         */
        private static boolean isNavigationBarShow(Activity activity) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                Display display = activity.getWindowManager().getDefaultDisplay();
                Point size = new Point();
                Point realSize = new Point();
                display.getSize(size);
                display.getRealSize(realSize);
                return realSize.y != size.y;
            } else {
                boolean menu = ViewConfiguration.get(activity).hasPermanentMenuKey();
                boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
                if (menu || back) {
                    return false;
                } else {
                    return true;
                }
            }
        }
    
        /**
         * 获取虚拟导航栏的高度
         *
         * @param activity activity
         * @return int
         */
        public static int getNavigationBarHeight(Activity activity) {
            if (!isNavigationBarShow(activity)) {
                return 0;
            }
            Resources resources = activity.getResources();
            int resourceId = resources.getIdentifier("navigation_bar_height",
                    "dimen", "android");
            //获取NavigationBar的高度
            int height = resources.getDimensionPixelSize(resourceId);
            return height;
        }
    
        /**
         * 精确的除法
         *
         * @param var1
         * @param var2
         * @param scale 保留的小数
         * @return
         */
        public static double div(double var1, double var2, int scale) {
            if (scale < 0) {
                throw new IllegalArgumentException("The scale must be a positive integer or zero");
            }
            BigDecimal b1 = new BigDecimal(Double.toString(var1));
            BigDecimal b2 = new BigDecimal(Double.toString(var2));
            return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    
        }
    
        /**
         * dp to px
         *
         * @param dpValue
         * @return
         */
        public static int dp2px(Context context, float dpValue) {
            float scale = context.getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5F);
        }
    
    }
    

    四、使用

     @Override
        protected void onResume() {
            super.onResume();
            //开启监听截屏
            if (screenShotManager != null) {
                screenShotManager.startListener();
            }
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            //关闭截屏监听(防止触发)
            if (screenShotManager != null) {
                screenShotManager.stopListener();
            }
        }
    
    

    Github地址:https://github.com/lucklyperson/ShotScreenShareProject

    如果问题,欢迎指教。

    相关文章

      网友评论

          本文标题:Android实现截屏分享

          本文链接:https://www.haomeiwen.com/subject/nxxwlctx.html