关于View转化成bitmap保存成图片

产品今天说项目分享时要分享出一张  封面图片 + 几行文字 + 二维码图片 的图片。

思索了一下 封面图片和二维码图片让后台给接口得到地址, 主要还是找个方式得到一个包含这些内容的图片。于是就想能不能将View转化成bitmap对象

 

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

根据产品的需求  我要实现的步骤  把所有需要的集合在一个View里 —— View转化成bitmap —— 分享出去(第三方分享bitmap对象)

 

 

接着搞个demo 实验一下

要转化的view 大致长这样 

view_photo.xml

  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会失败。这种方式比较适合对应用界面或者某一部分的截图。步骤很简单:
  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()就行
  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的大小:
先得到
  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大小的函数  
  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
  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
  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和图片

posted @ 2018-04-24 16:17  brave-sailor  阅读(2236)  评论(0编辑  收藏  举报