關於View轉化成bitmap保存成圖片


產品今天說項目分享時要分享出一張  封面圖片 + 幾行文字 + 二維碼圖片 的圖片。

思索了一下 封面圖片和二維碼圖片讓后台給接口得到地址, 主要還是找個方式得到一個包含這些內容的圖片。於是就想能不能將View轉化成bitmap對象

 

然后就走了一遍各個前輩的路 整理了下原理和思路。       

根據產品的需求  我要實現的步驟  把所有需要的集合在一個View里 —— View轉化成bitmap —— 分享出去(第三方分享bitmap對象)

 

 

接着搞個demo 實驗一下

要轉化的view 大致長這樣 

view_photo.xml

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <ScrollView  
  8.         android:id="@+id/textView"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="match_parent" >  
  11.   
  12.         <LinearLayout  
  13.             android:layout_width="match_parent"  
  14.             android:layout_height="match_parent"  
  15.             android:orientation="vertical" >  
  16.   
  17.             <!--封面圖片  -->  
  18.             <ImageView  
  19.                 android:layout_width="400dp"  
  20.                 android:layout_height="400dp"  
  21.                 android:background="@drawable/ic_launcher"  
  22.                 android:layout_gravity="center_horizontal" />  
  23.   
  24.             <TextView  
  25.                 android:layout_width="wrap_content"  
  26.                 android:layout_height="wrap_content"  
  27.                 android:text="文字 文字"  
  28.                 android:layout_gravity="center_horizontal" />  
  29.   
  30.             <Button  
  31.                 android:id="@+id/button"  
  32.                 android:layout_width="wrap_content"  
  33.                 android:layout_height="wrap_content"  
  34.                 android:text="假裝是二維碼圖片"  
  35.                 android:layout_gravity="center_horizontal" />  
  36.   
  37.         </LinearLayout>  
  38.     </ScrollView>  
  39.   
  40. </LinearLayout>  


最關鍵的View轉bitmap 

好像是有兩種方法   

 

DrawingCache方法:
如果使用DrawingCache,則對要截圖的View有一個要求:View本身已經顯示在界面上。如果View沒有添加到界面上或者沒有顯示(繪制)過,則buildDrawingCache會失敗。這種方式比較適合對應用界面或者某一部分的截圖。步驟很簡單:
[java] view plain copy
  1. view.setDrawingCacheEnabled(true);    
  2. view.buildDrawingCache();  //啟用DrawingCache並創建位圖    
  3. Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache()); //創建一個DrawingCache的拷貝,因為DrawingCache得到的位圖在禁用后會被回收    
  4. view.setDrawingCacheEnabled(false);  //禁用DrawingCahce否則會影響性能    

 

 

View.draw方法:
這個也很簡單 view對象傳進去.draw()就行
[java] view plain copy
  1. private Bitmap loadBitmapFromView(View v) {    
  2.         int w = v.getWidth();    
  3.         int h = v.getHeight();    
  4.         Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);    
  5.         Canvas c = new Canvas(bmp);    
  6.     
  7.         c.drawColor(Color.WHITE);    
  8.         /** 如果不設置canvas畫布為白色,則生成透明 */    
  9.     
  10.         v.layout(0, 0, w, h);    
  11.         v.draw(c);    
  12.     
  13.         return bmp;    
  14.     }    
然而此時適用於需要截圖的View並沒有添加到界面上,
可能是通過java代碼創建的或者inflate創建的,(我需求的情況就適用於這種。分享的時候生成bitmap對象但不顯示在界面中)
此時調用DrawingCache方法是獲取不到位圖的。
因為View在添加到容器中之前並沒有得到實際的大小(如果LayoutWidth是MatchParent,它還沒有Parent…)
,所以首先需要指定View的大小:
先得到
[java] view plain copy
  1. DisplayMetrics metric = new DisplayMetrics();    
  2.         getWindowManager().getDefaultDisplay().getMetrics(metric);    
  3.         int width = metric.widthPixels;     // 屏幕寬度(像素)    
  4.         int height = metric.heightPixels;   // 屏幕高度(像素)    
  5.         View view = LayoutInflater.from(this).inflate(R.layout.view_photo, null, false);  
  6.         layoutView(view, width, height);//去到指定view大小的函數  
[java] view plain copy
  1. //然后View和其內部的子View都具有了實際大小,也就是完成了布局,相當與添加到了界面上。接着就可以創建位圖並在上面繪制了:  
  2.     private void layoutView(View v, int width, int height) {    
  3.         // 指定整個View的大小 參數是左上角 和右下角的坐標  
  4.         v.layout(0, 0, width, height);  
  5.         int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);  
  6.         int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);   
  7.         /** 當然,measure完后,並不會實際改變View的尺寸,需要調用View.layout方法去進行布局。 
  8.          * 按示例調用layout函數后,View的大小將會變成你想要設置成的大小。 
  9.         */  
  10.         v.measure(measuredWidth, measuredHeight);    
  11.         v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());  
  12.     }    
在int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST); 
這里我有點不懂后面函數的取值。在自定義view里 onMeasure()里有根據MeasureSpec.getMode()的類型來准確得到設置view的長寬
.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);卻貌似取了 自適應和前一個int參數的最小值。
后面我發現有可能合成出超出屏幕的長圖,就直接吧前一個int參數賦值成一個很大的數字。。。
 
 
上整個demo的代碼
activity_main.xml
[html] view plain copy
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     tools:context="com.example.aa.MainActivity" >  
  6.     <Button  
  7.         android:id="@+id/button"  
  8.         android:layout_width="wrap_content"  
  9.         android:layout_height="wrap_content"  
  10.         android:text=" button" />  
  11.     <ImageView   
  12.         android:id="@+id/aaa"  
  13.         android:layout_below="@+id/button"  
  14.         android:layout_width="wrap_content"  
  15.         android:layout_height="wrap_content"  
  16.         />  
  17. </RelativeLayout>  
view_photo.xml 在上面
MainActivity
[html] view plain copy
  1. public class MainActivity extends Activity {    
  2.     ImageView aaa ;  
  3.     @Override    
  4.     protected void onCreate(Bundle savedInstanceState) {    
  5.         super.onCreate(savedInstanceState);    
  6.         setContentView(R.layout.activity_main);    
  7.         DisplayMetrics metric = new DisplayMetrics();    
  8.         getWindowManager().getDefaultDisplay().getMetrics(metric);    
  9.         int width = metric.widthPixels;     // 屏幕寬度(像素)    
  10.         int height = metric.heightPixels;   // 屏幕高度(像素)    
  11.         View view = LayoutInflater.from(this).inflate(R.layout.view_photo, null, false);  
  12.         layoutView(view, width, height);  
  13.         final ScrollView tv = (ScrollView) view.findViewById(R.id.textView);    
  14.         aaa = (ImageView) findViewById(R.id.aaa);  
  15.   
  16.         final Runnable runnable = new Runnable() {    
  17.             @Override    
  18.             public void run() {   
  19.                 viewSaveToImage(tv);    
  20.             }    
  21.         };    
  22.   
  23.         Button button = (Button) findViewById(R.id.button);    
  24.         button.setOnClickListener(new View.OnClickListener() {    
  25.   
  26.             @Override    
  27.             public void onClick(View v) {    
  28.                 new Handler().post(runnable);    
  29.             }    
  30.         });    
  31.   
  32.     }   
  33.     //然后View和其內部的子View都具有了實際大小,也就是完成了布局,相當與添加到了界面上。接着就可以創建位圖並在上面繪制了:  
  34.     private void layoutView(View v, int width, int height) {    
  35.         // 整個View的大小 參數是左上角 和右下角的坐標  
  36.         v.layout(0, 0, width, height);  
  37.         int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);  
  38.         int measuredHeight = View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.AT_MOST);    
  39.         /** 當然,measure完后,並不會實際改變View的尺寸,需要調用View.layout方法去進行布局。  
  40.          * 按示例調用layout函數后,View的大小將會變成你想要設置成的大小。  
  41.          */  
  42.         v.measure(measuredWidth, measuredHeight);    
  43.         v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());  
  44.     }    
  45.   
  46.     public void viewSaveToImage(View view) {    
  47.         Log.e("ssh","a");  
  48.   
  49.         /**  
  50.          * View組件顯示的內容可以通過cache機制保存為bitmap  
  51.          * 我們要獲取它的cache先要通過setDrawingCacheEnable方法把cache開啟,  
  52.          * 然后再調用getDrawingCache方法就可 以獲得view的cache圖片了  
  53.          * 。buildDrawingCache方法可以不用調用,因為調用getDrawingCache方法時,  
  54.          * 若果 cache沒有建立,系統會自動調用buildDrawingCache方法生成cache。  
  55.          * 若果要更新cache, 必須要調用destoryDrawingCache方法把舊的cache銷毀,才能建立新的。  
  56.          */  
  57.         //        view.setDrawingCacheEnabled(true);    
  58.         //        view.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);    
  59.         //設置繪制緩存背景顏色  
  60.         //        view.setDrawingCacheBackgroundColor(Color.WHITE);    
  61.   
  62.         // 把一個View轉換成圖片    
  63.         Bitmap cachebmp = loadBitmapFromView(view);    
  64.   
  65.         aaa.setImageBitmap(cachebmp);//直接展示轉化的bitmap  
  66.   
  67.         //保存在本地 產品還沒決定要不要保存在本地  
  68.         FileOutputStream fos;    
  69.         try {    
  70.             // 判斷手機設備是否有SD卡    
  71.             boolean isHasSDCard = Environment.getExternalStorageState().equals(    
  72.                     android.os.Environment.MEDIA_MOUNTED);    
  73.             if (isHasSDCard) {    
  74.                 // SD卡根目錄    
  75.                 File sdRoot = Environment.getExternalStorageDirectory();   
  76.                 Log.e("ssh",sdRoot.toString());  
  77.                 File file = new File(sdRoot, "test.png");    
  78.                 fos = new FileOutputStream(file);    
  79.             } else    
  80.                 throw new Exception("創建文件失敗!");    
  81.             //壓縮圖片 30 是壓縮率,表示壓縮70%; 如果不壓縮是100,表示壓縮率為0    
  82.             cachebmp.compress(Bitmap.CompressFormat.PNG, 90, fos);    
  83.   
  84.             fos.flush();    
  85.             fos.close();    
  86.   
  87.         } catch (Exception e) {    
  88.             e.printStackTrace();    
  89.         }    
  90.   
  91.         view.destroyDrawingCache();    
  92.     }    
  93.   
  94.     private Bitmap loadBitmapFromView(View v) {    
  95.         int w = v.getWidth();    
  96.         int h = v.getHeight();    
  97.         Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);    
  98.         Canvas c = new Canvas(bmp);    
  99.   
  100.         c.drawColor(Color.WHITE);    
  101.         /** 如果不設置canvas畫布為白色,則生成透明 */    
  102.   
  103.         v.layout(0, 0, w, h);    
  104.         v.draw(c);    
  105.   
  106.         return bmp;    
  107.     }   
  108. }  

demo轉化成結果的bitmap和圖片


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM