實際開發中常常會遇到應用內截圖的相關問題,若是是普通View
的話咱們可使用View
的繪圖緩存來獲取截圖,可是RecyclerView
和ScrollView
呢就稍微有點不一樣了。ScrollView
還好,只有一個子View
,而RecyclerView
中會有itemView
重用的問題,只會繪製在屏幕上顯示出來的itemView
,所以咱們能夠依次獲取每一個itemView
的視圖存儲到LruCache中,最後在進行拼裝。這種方案有個問題就是若是數據過多可能會產生OOM問題,不過我測試的幾百條item都還好,沒有遇到。思路參考自這裏java
普通View截圖就是獲取View的繪圖緩存,這裏的代碼很簡單,先看看吧:git
//開啓繪圖緩存
view.setDrawingCacheEnabled(true);
//這個方法可調可不調,由於在getDrawingCache()裏會自動判斷有沒有緩存有沒有準備好,
//若是沒有,會自動調用buildDrawingCache()
view.buildDrawingCache();
//獲取繪圖緩存 這裏直接建立了一個新的bitmap
//由於咱們在最後須要釋放緩存資源,會釋放掉緩存中建立的bitmap對象
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache(), 0, 0, view.getMeasuredWidth(),
view.getMeasuredHeight());
//清理繪圖緩存,釋放資源
view.destroyDrawingCache();
複製代碼
能夠看下destroyDrawingCache()
方法的源碼:github
public void destroyDrawingCache() {
if (mDrawingCache != null) {
mDrawingCache.recycle();
mDrawingCache = null;
}
if (mUnscaledDrawingCache != null) {
mUnscaledDrawingCache.recycle();
mUnscaledDrawingCache = null;
}
}
複製代碼
若是你須要整個屏幕的截圖,使用了DecorView
來獲取截圖的話頂部會有狀態欄的一條,能夠經過獲取狀態欄高度,在從新建立Bitmap時截取掉。canvas
RecyclerView的截圖稍微複雜些。咱們能夠先獲取到rv的adapter對象,經過adapter對象手動調用建立和綁定ViewHolder等方法,模擬rv的加載過程,再依次保存每一個itemView的繪圖緩存,最後拼裝成一個完整的Bitmap。看看代碼吧~緩存
//獲取設置的adapter
RecyclerView.Adapter adapter = view.getAdapter();
if (adapter == null) {
return null;
}
//建立保存截圖的bitmap
Bitmap bigBitmap = null;
//獲取item的數量
int size = adapter.getItemCount();
//recycler的完整高度 用於建立bitmap時使用
int height = 0;
//獲取最大可用內存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// 使用1/8的緩存
final int cacheSize = maxMemory / 8;
//把每一個item的繪圖緩存存儲在LruCache中
LruCache<String, Bitmap> bitmapCache = new LruCache<>(cacheSize);
for (int i = 0; i < size; i++) {
//手動調用建立和綁定ViewHolder方法,
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) {
bitmapCache.put(String.valueOf(i), drawingCache);
}
//獲取itemView的實際高度並累加
height += holder.itemView.getMeasuredHeight();
}
複製代碼
//根據計算出的recyclerView高度建立bitmap
bigBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), height, Bitmap.Config.RGB_565);
//建立一個canvas畫板
Canvas canvas = new Canvas(bigBitmap);
//獲取recyclerView的背景顏色
Drawable background = view.getBackground();
//畫出recyclerView的背景色 這裏只用了color一種 有須要也能夠本身擴展
if (background instanceof ColorDrawable) {
ColorDrawable colorDrawable = (ColorDrawable) background;
int color = colorDrawable.getColor();
canvas.drawColor(color);
}
//當前bitmap的高度
int top = 0;
//畫筆
Paint paint = new Paint();
for (int i = 0; i < size; i++) {
Bitmap bitmap = bitmapCache.get(String.valueOf(i));
canvas.drawBitmap(bitmap, 0f, top, paint);
top += bitmap.getHeight();
}
複製代碼
ScrollView的截圖也比較簡單,獲取到子View的高度就好,而ScrollView只容許又一個子View。eclipse
int height = 0;
//理論上scrollView只會有一個子View啦
for (int i = 0; i < view.getChildCount(); i++) {
height += view.getChildAt(i).getHeight();
}
//建立保存緩存的bitmap
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), height, Bitmap.Config.RGB_565);
//能夠簡單的把Canvas理解爲一個畫板 而bitmap就是塊畫布
Canvas canvas = new Canvas(bitmap);
//獲取ScrollView的背景顏色
Drawable background = view.getBackground();
//畫出ScrollView的背景色 這裏只用了color一種 有須要也能夠本身擴展 也能夠本身直接指定一種背景色
if (background instanceof ColorDrawable) {
ColorDrawable colorDrawable = (ColorDrawable) background;
int color = colorDrawable.getColor();
canvas.drawColor(color);
}
//把view的內容都畫到指定的畫板Canvas上
view.draw(canvas);
return bitmap;
複製代碼
recycle()
方法不少人覺得這個釋放資源的方法是必須調用的。其實否則,在2.3
以後能夠徹底交由GC管理。具體的解釋能夠參見官方註釋佈局
Free the native object associated with this bitmap, and clear the reference to the pixel data. This will not free the pixel data synchronously; it simply allows it to be garbage collected if there are no other references. The bitmap is marked as "dead", meaning it will throw an exception if getPixels() or setPixels() is called, and will draw nothing. This operation cannot be reversed, so it should only be called if you are sure there are no further uses for the bitmap. This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.測試
或者醫生的這篇博客Bitmap.recycle引起的血案ui
下載源碼點擊這裏 若有錯誤或不當的地方還請大佬們指出this