Android截屏方案實(shí)現(xiàn)原理解析
Android截屏的原理:獲取具體需要截屏的區(qū)域的Bitmap,然后繪制在畫布上,保存為圖片后進(jìn)行分享或者其它用途
在截屏功能中,有時需要截取全屏的內(nèi)容,有時需要截取超過一屏的內(nèi)容(比如:Listview,Scrollview,RecyclerView)。下面介紹各種場景獲取Bitmap的方法
普通截屏的實(shí)現(xiàn)
獲取當(dāng)前Window的DrawingCache的方式,即decorView的DrawingCache
/** * shot the current screen ,with the status but the status is trans * * * @param ctx current activity */ public static Bitmap shotActivity(Activity ctx) { View view = ctx.getWindow().getDecorView(); view.setDrawingCacheEnabled(true); view.buildDrawingCache(); Bitmap bp = Bitmap.createBitmap(view.getDrawingCache(), 0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); view.setDrawingCacheEnabled(false); view.destroyDrawingCache(); return bp; }
獲取當(dāng)前View的DrawingCache
public static Bitmap getViewBp(View v) { if (null == v) { return null; } v.setDrawingCacheEnabled(true); v.buildDrawingCache(); if (Build.VERSION.SDK_INT >= 11) { v.measure(MeasureSpec.makeMeasureSpec(v.getWidth(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec( v.getHeight(), MeasureSpec.EXACTLY)); v.layout((int) v.getX(), (int) v.getY(), (int) v.getX() + v.getMeasuredWidth(), (int) v.getY() + v.getMeasuredHeight()); } else { v.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight()); } Bitmap b = Bitmap.createBitmap(v.getDrawingCache(), 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight()); v.setDrawingCacheEnabled(false); v.destroyDrawingCache(); return b; }
開源方案
在滾動視圖中,如果當(dāng)前View并沒有在視圖中全部繪制出來,我們可以利用View的ScrollTo()和ScrollBy()方法來移動畫布,同時獲取當(dāng)前View的可視部分的DrawingCache,最后進(jìn)行拼接得到其Bitmap,參考: PGSSoft/scrollscreenshot@[Github] 。
Scrollview截屏
三個截屏中,ScrollView最簡單,因?yàn)镾crollView只有一個childView,雖然沒有全部顯示在界面上,但是已經(jīng)全部渲染繪制,因此可以直接 調(diào)用 scrollView.draw(canvas) 來完成截圖
public static Bitmap shotScrollView(ScrollView scrollView) { int h = 0; Bitmap bitmap = null; for (int i = 0; i < scrollView.getChildCount(); i++) { h += scrollView.getChildAt(i).getHeight(); scrollView.getChildAt(i).setBackgroundColor(Color.parseColor("#ffffff")); } bitmap = Bitmap.createBitmap(scrollView.getWidth(), h, Bitmap.Config.RGB_565); final Canvas canvas = new Canvas(bitmap); scrollView.draw(canvas); return bitmap; }
Scrollview截屏
而ListView就是會回收與重用Item,并且只會繪制在屏幕上顯示的ItemView,根據(jù)stackoverflow上大神的建議,采用一個List來存儲Item的視圖,這種方案依然不夠好,當(dāng)Item足夠多的時候,可能會發(fā)生oom。
public static Bitmap shotListView(ListView listview) { ListAdapter adapter = listview.getAdapter(); int itemscount = adapter.getCount(); int allitemsheight = 0; List<Bitmap> bmps = new ArrayList<Bitmap>(); for (int i = 0; i < itemscount; i++) { View childView = adapter.getView(i, null, listview); childView.measure( View.MeasureSpec.makeMeasureSpec(listview.getWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); childView.layout(0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight()); childView.setDrawingCacheEnabled(true); childView.buildDrawingCache(); bmps.add(childView.getDrawingCache()); allitemsheight += childView.getMeasuredHeight(); } Bitmap bigbitmap = Bitmap.createBitmap(listview.getMeasuredWidth(), allitemsheight, Bitmap.Config.ARGB_8888); Canvas bigcanvas = new Canvas(bigbitmap); Paint paint = new Paint(); int iHeight = 0; for (int i = 0; i < bmps.size(); i++) { Bitmap bmp = bmps.get(i); bigcanvas.drawBitmap(bmp, 0, iHeight, paint); iHeight += bmp.getHeight(); bmp.recycle(); bmp = null; } return bigbitmap; }
RecyclerView截屏
我們都知道,在新的Android版本中,已經(jīng)可以用RecyclerView來代替使用ListView的場景,相比較ListView,RecyclerView對Item View的緩存支持的更好??梢圆捎煤蚅istView相同的方案,這里也是在stackoverflow上看到的方案。
public static Bitmap shotRecyclerView(RecyclerView view) { RecyclerView.Adapter adapter = view.getAdapter(); Bitmap bigBitmap = null; if (adapter != null) { int size = adapter.getItemCount(); int height = 0; Paint paint = new Paint(); int iHeight = 0; final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // Use 1/8th of the available memory for this memory cache. final int cacheSize = maxMemory / 8; LruCache<String, Bitmap> bitmaCache = new LruCache<>(cacheSize); for (int i = 0; i < size; i++) { RecyclerView.ViewHolder holder = adapter.createViewHolder(view, adapter.getItemViewType(i)); adapter.onBindViewHolder(holder, i); holder.itemView.measure( View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); holder.itemView.layout(0, 0, holder.itemView.getMeasuredWidth(), holder.itemView.getMeasuredHeight()); holder.itemView.setDrawingCacheEnabled(true); holder.itemView.buildDrawingCache(); Bitmap drawingCache = holder.itemView.getDrawingCache(); if (drawingCache != null) { bitmaCache.put(String.valueOf(i), drawingCache); } height += holder.itemView.getMeasuredHeight(); } bigBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), height, Bitmap.Config.ARGB_8888); Canvas bigCanvas = new Canvas(bigBitmap); Drawable lBackground = view.getBackground(); if (lBackground instanceof ColorDrawable) { ColorDrawable lColorDrawable = (ColorDrawable) lBackground; int lColor = lColorDrawable.getColor(); bigCanvas.drawColor(lColor); } for (int i = 0; i < size; i++) { Bitmap bitmap = bitmaCache.get(String.valueOf(i)); bigCanvas.drawBitmap(bitmap, 0f, iHeight, paint); iHeight += bitmap.getHeight(); bitmap.recycle(); } } return bigBitmap; }
上面的方法在截取存在異步加載圖片的RecyclerView時候會出現(xiàn)加載不出圖片的情況,這里再補(bǔ)充一種滾動式截屏的方法
public static void screenShotRecycleView(final RecyclerView mRecyclerView, final RecycleViewRecCallback callBack) { if (mRecyclerView == null) { return; } BaseListFragment.MyAdapter adapter = (BaseListFragment.MyAdapter) mRecyclerView.getAdapter(); final Paint paint = new Paint(); final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // Use 1/8th of the available memory for this memory cache. final int cacheSize = maxMemory / 8; LruCache<String, Bitmap> bitmaCache = new LruCache<>(cacheSize); final int oneScreenHeight = mRecyclerView.getMeasuredHeight(); int shotHeight = 0; if (adapter != null && adapter.getData().size() > 0) { int headerSize = adapter.getHeaderLayoutCount(); int dataSize = adapter.getData().size(); for (int i = 0; i < headerSize + dataSize; i++) { BaseViewHolder holder = (BaseViewHolder) adapter.createViewHolder(mRecyclerView, adapter.getItemViewType(i)); if (i >= headerSize) adapter.startConvert(holder, adapter.getData().get(i - headerSize)); holder.itemView.measure( View.MeasureSpec.makeMeasureSpec(mRecyclerView.getWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); holder.itemView.layout(0, 0, holder.itemView.getMeasuredWidth(), holder.itemView.getMeasuredHeight()); holder.itemView.setDrawingCacheEnabled(true); holder.itemView.buildDrawingCache(); Bitmap drawingCache = holder.itemView.getDrawingCache(); //holder.itemView.destroyDrawingCache();//釋放緩存占用的資源 if (drawingCache != null) { bitmaCache.put(String.valueOf(i), drawingCache); } shotHeight += holder.itemView.getHeight(); if (shotHeight > 12000) { //設(shè)置截圖最大值 if (callBack != null) callBack.onRecFinished(null); return; } } //添加底部高度(加載更多或loading布局高度,此處為固定值:) final int footHight = Util.dip2px(mRecyclerView.getContext(), 42); shotHeight += footHight; //返回到頂部 while (mRecyclerView.canScrollVertically(-1)) { mRecyclerView.scrollBy(0, -oneScreenHeight); } //繪制截圖的背景 final Bitmap bigBitmap = Bitmap.createBitmap(mRecyclerView.getMeasuredWidth(), shotHeight, Bitmap.Config.ARGB_8888); final Canvas bigCanvas = new Canvas(bigBitmap); Drawable lBackground = mRecyclerView.getBackground(); if (lBackground instanceof ColorDrawable) { ColorDrawable lColorDrawable = (ColorDrawable) lBackground; int lColor = lColorDrawable.getColor(); bigCanvas.drawColor(lColor); } final int[] drawOffset = {0}; final Canvas canvas = new Canvas(); if (shotHeight <= oneScreenHeight) { //僅有一頁 Bitmap bitmap = Bitmap.createBitmap(mRecyclerView.getWidth(), mRecyclerView.getHeight(), Bitmap.Config.ARGB_8888); canvas.setBitmap(bitmap); mRecyclerView.draw(canvas); if (callBack != null) callBack.onRecFinished(bitmap); } else { //超過一頁 final int finalShotHeight = shotHeight; mRecyclerView.postDelayed(new Runnable() { @Override public void run() { if ((drawOffset[0] + oneScreenHeight < finalShotHeight)) { //超過一屏 Bitmap bitmap = Bitmap.createBitmap(mRecyclerView.getWidth(), mRecyclerView.getHeight(), Bitmap.Config.ARGB_8888); canvas.setBitmap(bitmap); mRecyclerView.draw(canvas); bigCanvas.drawBitmap(bitmap, 0, drawOffset[0], paint); drawOffset[0] += oneScreenHeight; mRecyclerView.scrollBy(0, oneScreenHeight); try { bitmap.recycle(); } catch (Exception ex) { ex.printStackTrace(); } mRecyclerView.postDelayed(this, 10); } else { //不足一屏?xí)r的處理 int leftHeight = finalShotHeight - drawOffset[0] - footHight; mRecyclerView.scrollBy(0, leftHeight); int top = oneScreenHeight - (finalShotHeight - drawOffset[0]); if (top > 0 && leftHeight > 0) { Bitmap bitmap = Bitmap.createBitmap(mRecyclerView.getWidth(), mRecyclerView.getHeight(), Bitmap.Config.ARGB_8888); canvas.setBitmap(bitmap); mRecyclerView.draw(canvas); //截圖,只要補(bǔ)足的那塊圖 bitmap = Bitmap.createBitmap(bitmap, 0, top, bitmap.getWidth(), leftHeight, null, false); bigCanvas.drawBitmap(bitmap, 0, drawOffset[0], paint); try { bitmap.recycle(); } catch (Exception ex) { ex.printStackTrace(); } } if (callBack != null) callBack.onRecFinished(bigBitmap); } } }, 10); } } } public interface RecycleViewRecCallback { void onRecFinished(Bitmap bitmap); }
相信有不少小伙伴用BRVH第三方庫來做recycleview的適配器的。使用這個庫的話再用上面的方法會報角標(biāo)越界的錯誤,看了BRVH的源碼
public void onBindViewHolder(ViewHolder holder, int positions) { int viewType = holder.getItemViewType(); switch(viewType) { case 0: this.convert((BaseViewHolder)holder, this.mData.get(holder.getLayoutPosition() - this.getHeaderLayoutCount())); case 273: case 819: case 1365: break; case 546: this.addLoadMore(holder); break; default: this.convert((BaseViewHolder)holder, this.mData.get(holder.getLayoutPosition() - this.getHeaderLayoutCount())); this.onBindDefViewHolder((BaseViewHolder)holder, this.mData.get(holder.getLayoutPosition() - this.getHeaderLayoutCount())); } }
在調(diào)用 adapter.onBindViewHolder 時,因?yàn)槔锩娴?position 參數(shù)未使用,里面用的計算 holder.getLayoutPosition() - this.getHeaderLayoutCount() 的值一直是-1導(dǎo)致角標(biāo)越界報錯。
本人理解,RecyclerView的截屏原理是,首先構(gòu)造每個item的ViewHolder,然后調(diào)用具體設(shè)置數(shù)據(jù)到每個item的方法,此時cache中就存有item的內(nèi)容,此時繪制就能獲取到完整的內(nèi)容。采用v7包中的 onBindViewHolder 方法即可,或者是BRVH的 convert 方法,可以看到BRVH中沒有暴露出這個方法,而且唯一暴露出的 onBindViewHolder 還會報角標(biāo)越界錯誤,此時我們就需要在BRVH的基礎(chǔ)上暴露出 convert 即可,代碼如下
public class MyAdapter extends BaseQuickAdapter<T> { public MyAdapter() { super(getItemLayoutResId(), datas); } /** * 用于對外暴露convert方法,構(gòu)造緩存視圖(截屏用) * @param viewHolder * @param t */ public void startConvert(BaseViewHolder viewHolder, T t){ convert(viewHolder,t); } @Override protected void convert(BaseViewHolder viewHolder, T t) { bindView(viewHolder, t); } }
然后將上面所述的獲取Bitmap方法修改一下
/** * 截取recycler view */ public static Bitmap getRecyclerViewScreenshot(RecyclerView view) { BaseListFragment.MyAdapter adapter = (BaseListFragment.MyAdapter) view.getAdapter(); Bitmap bigBitmap = null; if (adapter != null) { int size = adapter.getData().size(); int height = 0; Paint paint = new Paint(); int iHeight = 0; final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // Use 1/8th of the available memory for this memory cache. final int cacheSize = maxMemory / 8; LruCache<String, Bitmap> bitmaCache = new LruCache<>(cacheSize); for (int i = 0; i < size; i++) { BaseViewHolder holder = (BaseViewHolder) adapter.createViewHolder(view, adapter.getItemViewType(i)); //此處需要調(diào)用convert方法,否則繪制出來的都是空的item adapter.startConvert(holder, adapter.getData().get(i)); holder.itemView.measure( View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); holder.itemView.layout(0, 0, holder.itemView.getMeasuredWidth(), holder.itemView.getMeasuredHeight()); holder.itemView.setDrawingCacheEnabled(true); holder.itemView.buildDrawingCache(); Bitmap drawingCache = holder.itemView.getDrawingCache(); if (drawingCache != null) { bitmaCache.put(String.valueOf(i), drawingCache); } height += holder.itemView.getMeasuredHeight(); } bigBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), height, Bitmap.Config.ARGB_8888); Canvas bigCanvas = new Canvas(bigBitmap); Drawable lBackground = view.getBackground(); if (lBackground instanceof ColorDrawable) { ColorDrawable lColorDrawable = (ColorDrawable) lBackground; int lColor = lColorDrawable.getColor(); bigCanvas.drawColor(lColor); } for (int i = 0; i < size; i++) { Bitmap bitmap = bitmaCache.get(String.valueOf(i)); bigCanvas.drawBitmap(bitmap, 0f, iHeight, paint); iHeight += bitmap.getHeight(); bitmap.recycle(); } } return bigBitmap; }
合成Bitmap
比如四張合成一張
/** * 將四張圖拼成一張 * * @param pic1 圖一 * @param pic2 圖二 * @param pic3 圖三 * @param pic4 圖四 * @return only_bitmap * 詳情見說明:{@link com.bertadata.qxb.util.ScreenShotUtils} */ public static Bitmap combineBitmapsIntoOnlyOne(Bitmap pic1, Bitmap pic2, Bitmap pic3, Bitmap pic4, Activity context) { int w_total = pic2.getWidth(); int h_total = pic1.getHeight() + pic2.getHeight() + pic3.getHeight() + pic4.getHeight(); int h_pic1 = pic1.getHeight(); int h_pic4 = pic4.getHeight(); int h_pic12 = pic1.getHeight() + pic2.getHeight(); //此處為防止OOM需要對高度做限制 if (h_total > HEIGHTLIMIT) { return null; } Bitmap only_bitmap = Bitmap.createBitmap(w_total, h_total, Bitmap.Config.ARGB_4444); Canvas canvas = new Canvas(only_bitmap); canvas.drawColor(ContextCompat.getColor(context, R.color.color_content_bg)); canvas.drawBitmap(pic1, 0, 0, null); canvas.drawBitmap(pic2, 0, h_pic1, null); canvas.drawBitmap(pic3, 0, h_pic12, null); canvas.drawBitmap(pic4, 0, h_total - h_pic4, null); return only_bitmap; }
圖片后期處理
/** * 將傳入的Bitmap合理壓縮后輸出到系統(tǒng)截屏目錄下 * 命名格式為:Screenshot+時間戳+啟信寶報名.jpg * 同時通知系統(tǒng)重新掃描系統(tǒng)文件 * * @param pic1 圖一 標(biāo)題欄截圖 * @param pic2 圖二 scrollview截圖 * @param context 用于通知重新掃描文件系統(tǒng),為提升性能可去掉 * 詳情見說明:{@link com.bertadata.qxb.util.ScreenShotUtils} */ public static void savingBitmapIntoFile(final Bitmap pic1, final Bitmap pic2, final Activity context, final BitmapAndFileCallBack callBack) { if (context == null || context.isFinishing()) { return; } Thread thread = new Thread(new Runnable() { @Override public void run() { String fileReturnPath = ""; int w = pic1.getWidth(); Bitmap bottom = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_picture_combine_bottom); Bitmap top_banner = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_picture_combine_top); Bitmap bitmap_bottom = anyRatioCompressing(bottom, (float) w / bottom.getWidth(), (float) w / bottom.getWidth()); Bitmap bitmap_top = anyRatioCompressing(top_banner, (float) w / bottom.getWidth(), (float) w / bottom.getWidth()); final Bitmap only_bitmap = combineBitmapsIntoOnlyOne(bitmap_top, pic1, pic2, bitmap_bottom, context); // 獲取當(dāng)前時間 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-ms", Locale.getDefault()); String data = sdf.format(new Date()); // 獲取內(nèi)存路徑 // 設(shè)置圖片路徑+命名規(guī)范 // 聲明輸出文件 String storagePath = Environment.getExternalStorageDirectory().getAbsolutePath(); String fileTitle = "Screenshot_" + data + "_com.bertadata.qxb.biz_info.jpg"; String filePath = storagePath + "/DCIM/"; final String fileAbsolutePath = filePath + fileTitle; File file = new File(fileAbsolutePath); /** * 質(zhì)壓與比壓結(jié)合 * 分級壓縮 * 輸出文件 */ if (only_bitmap != null) { try { // 首先,對原圖進(jìn)行一步質(zhì)量壓縮,形成初步文件 FileOutputStream fos = new FileOutputStream(file); only_bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos); // 另建一個文件other_file預(yù)備輸出 String other_fileTitle = "Screenshot_" + data + "_com.bertadata.qxb.jpg"; String other_fileAbsolutePath = filePath + other_fileTitle; File other_file = new File(other_fileAbsolutePath); FileOutputStream other_fos = new FileOutputStream(other_file); // 其次,要判斷質(zhì)壓之后的文件大小,按文件大小分級進(jìn)行處理 long file_size = file.length() / 1024; // size of file(KB) if (file_size < 0 || !(file.exists())) { // 零級: 文件判空 throw new NullPointerException(); } else if (file_size > 0 && file_size <= 256) { // 一級: 直接輸出 deleteFile(other_file); // 通知刷新文件系統(tǒng),顯示最新截取的圖文件 fileReturnPath = fileAbsolutePath; context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + fileAbsolutePath))); } else if (file_size > 256 && file_size <= 768) { // 二級: 簡單壓縮:壓縮為原比例的3/4,質(zhì)壓為50% anyRatioCompressing(only_bitmap, (float) 3 / 4, (float) 3 / 4).compress(Bitmap.CompressFormat.JPEG, 40, other_fos); deleteFile(file); // 通知刷新文件系統(tǒng),顯示最新截取的圖文件 fileReturnPath = other_fileAbsolutePath; context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + other_fileAbsolutePath))); } else if (file_size > 768 && file_size <= 1280) { // 三級: 中度壓縮:壓縮為原比例的1/2,質(zhì)壓為40% anyRatioCompressing(only_bitmap, (float) 1 / 2, (float) 1 / 2).compress(Bitmap.CompressFormat.JPEG, 40, other_fos); deleteFile(file); // 通知刷新文件系統(tǒng),顯示最新截取的圖文件 fileReturnPath = other_fileAbsolutePath; context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + other_fileAbsolutePath))); } else if (file_size > 1280 && file_size <= 2048) { // 四級: 大幅壓縮:壓縮為原比例的1/3,質(zhì)壓為40% anyRatioCompressing(only_bitmap, (float) 1 / 3, (float) 1 / 3).compress(Bitmap.CompressFormat.JPEG, 40, other_fos); deleteFile(file); // 通知刷新文件系統(tǒng),顯示最新截取的圖文件 fileReturnPath = other_fileAbsolutePath; context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + other_fileAbsolutePath))); } else if (file_size > 2048) { // 五級: 中度壓縮:壓縮為原比例的1/2,質(zhì)壓為40% anyRatioCompressing(only_bitmap, (float) 1 / 2, (float) 1 / 2).compress(Bitmap.CompressFormat.JPEG, 40, other_fos); deleteFile(file); // 通知刷新文件系統(tǒng),顯示最新截取的圖文件 fileReturnPath = other_fileAbsolutePath; context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + other_fileAbsolutePath))); } // 注銷fos; fos.flush(); other_fos.flush(); other_fos.close(); fos.close(); //callback用于回傳保存成功的路徑以及Bitmap callBack.onSuccess(only_bitmap, fileReturnPath); } catch (Exception e) { e.printStackTrace(); } } else callBack.onSuccess(null, ""); } }); thread.start(); } /** * 可實(shí)現(xiàn)任意寬高比例壓縮(寬高壓比可不同)的壓縮方法(主要用于微壓) * * @param bitmap 源圖 * @param width_ratio 寬壓比(float)(0<&&<1) * @param height_ratio 高壓比(float)(0<&&<1) * @return 目標(biāo)圖片 * <p> */ public static Bitmap anyRatioCompressing(Bitmap bitmap, float width_ratio, float height_ratio) { Matrix matrix = new Matrix(); matrix.postScale(width_ratio, height_ratio); return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); }
總結(jié)
以上所述是小編給大家介紹的Android截屏方案實(shí)現(xiàn)原理解析,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
android 9.0 launcher3 去掉抽屜式顯示所有 app(代碼詳解)
本文通過實(shí)例代碼給大家介紹了android 9.0 Launcher3 去掉抽屜式,顯示所有 app,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-11-11Android開發(fā)之a(chǎn)ctivity的生命周期詳解
這篇文章主要介紹了Android開發(fā)之a(chǎn)ctivity的生命周期,詳細(xì)分析了activity的運(yùn)行原理與生命周期,需要的朋友可以參考下2016-06-06Android 監(jiān)聽手機(jī)GPS打開狀態(tài)實(shí)現(xiàn)代碼
這篇文章主要介紹了Android 監(jiān)聽手機(jī)GPS打開狀態(tài)實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-05-05Android性能優(yōu)化死鎖監(jiān)控知識點(diǎn)詳解
這篇文章主要為大家介紹了Android性能優(yōu)化死鎖監(jiān)控知識點(diǎn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10Android互聯(lián)網(wǎng)訪問圖片并在客戶端顯示的方法
這篇文章主要介紹了Android互聯(lián)網(wǎng)訪問圖片并在客戶端顯示的方法,結(jié)合實(shí)例分析了Android處理圖片的技巧,并附帶了Android的URL封裝類,網(wǎng)絡(luò)連接封裝類與輸出流封裝類,需要的朋友可以參考下2015-12-12Android自定義控件ImageView實(shí)現(xiàn)點(diǎn)擊之后出現(xiàn)陰影效果
這篇文章主要為大家詳細(xì)介紹了Android自定義控件ImageView實(shí)現(xiàn)點(diǎn)擊之后有陰影效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12Android 配置gradle實(shí)現(xiàn)VersionCode自增實(shí)例
今天小編就為大家分享一篇Android 配置gradle實(shí)現(xiàn)VersionCode自增實(shí)例,具有很好的 參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03Android的OkHttp包處理用戶認(rèn)證的代碼實(shí)例分享
OkHttp包(GitHub主頁github.com/square/okhttp)是一款高人氣安卓HTTP支持包,這里我們來看一下Android的OkHttp包處理用戶認(rèn)證的代碼實(shí)例分享:2016-07-07