接近尾期了,自然就要用到截图和分享了,在网上查阅了不少时间,最后把得到的一些东西拿出来溜溜,
private void shot(){ View view = getWindow().getDecorView(); // 允许当前窗口保存缓存信息 view.setDrawingCacheEnabled(true); view.buildDrawingCache(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { new MediaActionSound().play(MediaActionSound.SHUTTER_CLICK); } Rect rect = new Rect(); view.getWindowVisibleDisplayFrame(rect); int statusBarHeights = rect.top; Display display = activity.getWindowManager().getDefaultDisplay(); //display.getHeight()与display.getWidth()已过时,使用Point替代 Point size = new Point(); display.getSize(size); int widths = size.x; int heights = size.y; Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache(), 0, statusBarHeights, widths, heights - statusBarHeights); view.destroyDrawingCache();//释放缓存占用的资源 File file = null; if (bitmap != null) { try { file =ScreenShot.getInstance() .saveScreenshotToPicturesFolder(activity, bitmap, "weekly"); } catch (Exception e) { e.printStackTrace(); } } if (file != null) showToast(activity, "截图已保存至Picture目录"); } /** * Save screenshot to pictures folder. * * @param context the context * @param image the image * @param filename the filename * @return the bitmap file object * @throws Exception the exception */public File saveScreenshotToPicturesFolder(Context context, Bitmap image, String filename) throws Exception { File bitmapFile = getOutputMediaFile(filename); if (bitmapFile == null) { throw new NullPointerException("Error creating media file, check storage permissions!"); } FileOutputStream fos = new FileOutputStream(bitmapFile); //90是压缩级别,如果图片有问题,请用原图100,文件生成后再去压缩 image.compress(Bitmap.CompressFormat.PNG, 90, fos); fos.close(); // Initiate media scanning to make the image available in gallery apps MediaScannerConnection.scanFile(context, new String[]{bitmapFile.getPath()}, new String[]{"image/jpeg"}, null); return bitmapFile; } private File getOutputMediaFile(String filename) { // To be safe, you should check that the SDCard is mounted // using Environment.getExternalStorageState() before doing this. File mediaStorageDirectory = new File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator); // Create the storage directory if it does not exist if (!mediaStorageDirectory.exists()) { if (!mediaStorageDirectory.mkdirs()) { return null; } } // Create a media file name String timeStamp = new SimpleDateFormat("yyyy_MM_dd_HHmmss").format(new Date()); File mediaFile; String mImageName = filename + timeStamp + ".jpg"; mediaFile = new File(mediaStorageDirectory.getPath() + File.separator + mImageName); return mediaFile; }
decorView是window中的最顶层view,我们把他拿下来,然后去掉状态栏就好,用下面的代码
Rect rect = new Rect(); view.getWindowVisibleDisplayFrame(rect); int statusBarHeights = rect.top;
然后在生成bitmap的时候把状态栏的高度去掉,剩下的就是activity的内容截图了。但是在某些界面截的图会有些问题,暂时未考虑接近,或许和我的过度绘制以及图表的onDraw有关,毕竟是一直刷新的。
这里再提供2种其他的办法:
(1)通过系统尺寸资源获取
状态栏高度定义在Android系统尺寸资源中status_bar_height,但这并不是公开可直接使用的,例如像通常使用系统资源那样android.R.dimen.status_bar_height。但是系统给我们提供了一个Resource类,通过这个类可以获取资源文件,借此可以获取到status_bar_height:
int statusBarHeight = 0; //获取status_bar_height资源的ID int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { //根据资源ID获取响应的尺寸值 statusBarHeight = getResources().getDimensionPixelSize(resourceId); } Log.d(“info", statusBarHeight);
另外一种:
(2)通过R类的反射
大家都知道Android的所有资源都会有惟一标识在R类中作为引用。我们也可以通过反射获取R类的实例域,然后找status_bar_height:
int statusBarHeight = 0; try { Class<?> clazz = Class.forName("com.android.internal.R$dimen"); Object object = clazz.newInstance(); int height = Integer.parseInt(clazz.getField("status_bar_height") .get(object).toString()); statusBarHeight = getResources().getDimensionPixelSize(height); } catch (Exception e) { e.printStackTrace(); } Log.e("WangJ", statusBarHeight2);
关于这个这里一篇文章很详细:Android完美获取状态栏高度、标题栏高度、编辑区域高度的获取
再提供一些截图方法:getDrawingCache()方法截取部分屏幕:
有了图片,要分享怎么办?这里的话不考虑三方平台的接入,单纯简单实现,就使用Intent进行内容分享吧,但是在7.0以上的会有一个隐私权限问题,官方是这样称呼的:
Android6.0引入了动态权限控制,7.0使用了私有目录被限制访问
,Strict Mode API 政策
。
先来段申请权限的代码,在6.0以上的系统中,需要动态的去判断是否已经授予,大多数情况下,都是默认禁止的
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // android 6.0及以上版本 if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED || checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // 有权限没有授予 ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.READ_PHONE_STATE,Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_REQUEST_PERMISSION); } }
但是每个地方这样来一次,不是很麻烦吗?这里有篇比较好的实例: Android 6.0: 动态权限管理的解决方案
最后由于一些什么原因之类的我选择了这个库:AndPermission
很详细,也很好用。不多说了,看README吧。
我咋就还没这么厉害呢,唉,还需努力啊。。。
权限有了,但是Intent的操作并没你想得那么简单了,以前可以使用
Uri.fromFile(file)
但是现在需要使用FileProvider,主要记录下使用:
1,先在manifest里的application里注册provider
<provider android:name="android.support.v4.content.FileProvider" android:authorities="你的包名.fileprovider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/> </provider>
exported必须要求为false,为true则会报安全异常。grantUriPermissions为true,表示授予URI临时访问权限。
2,指定共享目录
为了指定共享的目录我们需要在资源目录下(res)创建一个xml目录,然后创建一个名为“file_paths”(名字可以随便起,只要和在manifest里注册的provider所引用的resource保持一致即可)的资源文件
<?xml version="1.0" encoding="utf-8"?> <resources> <paths> <external-path path="" name="camera_photos" /> </paths> </resources>
<files-path/>代表的根目录: Context.getFilesDir()
<external-path/>代表的根目录: Environment.getExternalStorageDirectory()
<cache-path/>代表的根目录: getCacheDir()
path=””是有意义的,它代表根目录,你可以向其他的应用共享根目录及其子目录下的任何一个文件。若设置path=”pictures”,它代表着根目录下的pictures目录,那么你想向其他应用共享pictures目录范围之外的文件是不可行的。
3.
使用FileProvider
Intent shareIntent = new Intent(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { shareIntent.setAction(Intent.ACTION_SEND); shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时授权该Uri所代表的文件 Uri uri=FileProvider.getUriForFile(this,"com.lckiss.screenshotmaster.fileprovider",file); Log.i("info", "saveScreenshot: "+uri); shareIntent.setDataAndType(uri, "image/jpeg"); } else { shareIntent.setDataAndType(Uri.fromFile(file), "image/jpeg"); shareIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } startActivity(Intent.createChooser(shareIntent, "image/jpeg"));
之前Uri的scheme类型为file的Uri改成了有FileProvider创建一个content类型的Uri。
添加了intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);来对目标应用临时授权该Uri所代表的文件。
通过FileProvider的getUriForFile(Context context, String authority, File file)静态方法来获取URI,方法中的authority就是manifest里注册provider使用的authority。
其实以上的分享方法是带不过去图片的,只能跳转到那个app,具体为什么,应该和app写的接收方法有关,所以像QQ,微信这些都有他自己的jar包,赶时间的话,可以考虑友盟。
然后分享的类型可以有很多:Android实现分享和接收分享内容
好晚了,休息了该,留下一篇明天拓展的文章:Android Notification的使用
2019.2.11更新:
如果你需要授权外部访问,比如接入的U盘等,需要将file_path.xml的内容改为以下:
<?xml version="1.0" encoding="utf-8"?> <resources> <paths> <root-path path="" name="root_path" /> </paths> </resources>
具体分析看这:FileProvider无法获取外置SD卡问题解决方案 | Failed to find configured root that contains
另外关于在安卓7.0以上使用uri方式引导apk安装闪退的问题:
需要加上权限:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
大概就这些吧,没什么其他要注意的了。
本站由以下主机服务商提供服务支持:
0条评论