• 关于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和图片

  • 相关阅读:
    fiddler模拟弱网测试环境
    3.12 获取地址定位
    3.11 第一次个人作业7
    3.10 第一次个人作业6
    3.9 第一次个人作业5
    3.8 第一次个人作业4
    3.7 第一个次个人作业3
    3.6 第一次个人作业2
    3.5 第一次个人作业
    3.4 TextClock的基本使用
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/8931370.html
Copyright © 2020-2023  润新知