Android实现将View转化为图片并保存到本地

一、概述

app中有需求需要将View转化为图片并保存到本地,这里分两种情况: 
1.View本身已经显示在界面上 
2.View还没有添加到界面上或者没有显示(绘制)过

二、实现方法

对于上述的第一种情况我使用下面代码即可:

private void viewSaveToImage(View view) {
        view.setDrawingCacheEnabled(true);
        view.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
        view.setDrawingCacheBackgroundColor(Color.WHITE);

        // 把一个View转换成图片
        Bitmap cachebmp = loadBitmapFromView(view);

        FileOutputStream fos;
        String imagePath = "";
        try {
            // 判断手机设备是否有SD卡
            boolean isHasSDCard = Environment.getExternalStorageState().equals(
                    android.os.Environment.MEDIA_MOUNTED);
            if (isHasSDCard) {
                // SD卡根目录
                File sdRoot = Environment.getExternalStorageDirectory();
                File file = new File(sdRoot, Calendar.getInstance().getTimeInMillis()+".png");
                fos = new FileOutputStream(file);
                imagePath = file.getAbsolutePath();
            } else
                throw new Exception("创建文件失败!");

            cachebmp.compress(Bitmap.CompressFormat.PNG, 90, fos);

            fos.flush();
            fos.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
        LogUtil.e("imagePath="+imagePath);

        view.destroyDrawingCache();
    }

    private Bitmap loadBitmapFromView(View v) {
        int w = v.getWidth();
        int h = v.getHeight();

        Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bmp);

        c.drawColor(Color.WHITE);
        /** 如果不设置canvas画布为白色,则生成透明 */

        v.layout(0, 0, w, h);
        v.draw(c);

        return bmp;
    }

也可以按下面做:

public static Bitmap getBitmapByView(ScrollView scrollView) {
        int h = 0;
        Bitmap bitmap = null;
        for (int i = 0; i < scrollView.getChildCount(); i++) {
            h += scrollView.getChildAt(i).getHeight();
        }
        bitmap = Bitmap.createBitmap(scrollView.getWidth(), h,
                Bitmap.Config.RGB_565);
        final Canvas canvas = new Canvas(bitmap);
        scrollView.draw(canvas);
        return bitmap;
}

满足layout生成bitmap,然后bitmap可以再生成图片

public static void savePhotoToSDCard(Bitmap photoBitmap, String path, String photoName) {
        if (checkSDCardAvailable()) {
            File dir = new File(path);
            if (!dir.exists()) {
                dir.mkdirs();
            }

            File photoFile = new File(path, photoName + ".png");
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(photoFile);
                if (photoBitmap != null) {
                    if (photoBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)) {
                        fileOutputStream.flush();
                    }
                }
            } catch (FileNotFoundException e) {
                photoFile.delete();
                e.printStackTrace();
            } catch (IOException e) {
                photoFile.delete();
                e.printStackTrace();
            } finally {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

检查是否有SD卡

 

public static boolean checkSDCardAvailable() {
        return android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
}



用例:

 

Bitmap bitmap = getBitmapByView(contentLly);//contentLly是布局文件                   
 ImageUtils.savePhotoToSDCard(bitmap, "/sdcard/test", "test");

 

如果是第二种,还这样使用的话,就会报错了,因为View在添加到容器中之前并没有得到实际的大小,所以首先需要指定View的大小:

DisplayMetrics metric = new DisplayMetrics();
                    getWindowManager().getDefaultDisplay().getMetrics(metric);
                    int width = metric.widthPixels;     // 屏幕宽度(像素)
                    int height = metric.heightPixels;   // 屏幕高度(像素)
                    View mingpianView = LayoutInflater.from(this).inflate(R.layout.view_team_mingpian, null, false);
                    layoutView(mingpianView, width, height);



//然后View和其内部的子View都具有了实际大小,也就是完成了布局,相当与添加到了界面上。接着就可以创建位图并在上面绘制了:

private void layoutView(View v, int width, int height) {  
        // 指定整个View的大小 参数是左上角 和右下角的坐标
        v.layout(0, 0, width, height);
        int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
        int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST); 
        /** 当然,measure完后,并不会实际改变View的尺寸,需要调用View.layout方法去进行布局。
         * 按示例调用layout函数后,View的大小将会变成你想要设置成的大小。
        */
        v.measure(measuredWidth, measuredHeight);  
        v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
}

在int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST); 

这里我有点不懂后面函数的取值。在自定义view里onMeasure()里有根据MeasureSpec.getMode()的类型来准确得到设置view的长宽

.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);却貌似取了 自适应和前一个int参数的最小值。

后面我发现有可能合成出超出屏幕的长图,就直接吧前一个int参数赋值成一个很大的数字。。。

MainActivity

public class MainActivity extends Activity {  
	ImageView aaa ;
	@Override  
	protected void onCreate(Bundle savedInstanceState) {  
		super.onCreate(savedInstanceState);  
		setContentView(R.layout.activity_main);  
		DisplayMetrics metric = new DisplayMetrics();  
		getWindowManager().getDefaultDisplay().getMetrics(metric);  
		int width = metric.widthPixels;     // 屏幕宽度(像素)  
		int height = metric.heightPixels;   // 屏幕高度(像素)  
		View view = LayoutInflater.from(this).inflate(R.layout.view_photo, null, false);
		layoutView(view, width, height);
		final ScrollView tv = (ScrollView) view.findViewById(R.id.textView);  
		aaa = (ImageView) findViewById(R.id.aaa);
 
		final Runnable runnable = new Runnable() {  
			@Override  
			public void run() { 
				viewSaveToImage(tv);  
			}  
		};  
 
		Button button = (Button) findViewById(R.id.button);  
		button.setOnClickListener(new View.OnClickListener() {  
 
			@Override  
			public void onClick(View v) {  
				new Handler().post(runnable);  
			}  
		});  
 
	} 
	//然后View和其内部的子View都具有了实际大小,也就是完成了布局,相当与添加到了界面上。接着就可以创建位图并在上面绘制了:
	private void layoutView(View v, int width, int height) {  
		// 整个View的大小 参数是左上角 和右下角的坐标
		v.layout(0, 0, width, height);
		int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
		int measuredHeight = View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.AT_MOST);  
		/** 当然,measure完后,并不会实际改变View的尺寸,需要调用View.layout方法去进行布局。
		 * 按示例调用layout函数后,View的大小将会变成你想要设置成的大小。
		 */
		v.measure(measuredWidth, measuredHeight);  
		v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
	}  
 
	public void viewSaveToImage(View view) {  
		Log.e("ssh","a");
 
		/**
		 * View组件显示的内容可以通过cache机制保存为bitmap
		 * 我们要获取它的cache先要通过setDrawingCacheEnable方法把cache开启,
		 * 然后再调用getDrawingCache方法就可 以获得view的cache图片了
		 * 。buildDrawingCache方法可以不用调用,因为调用getDrawingCache方法时,
		 * 若果 cache没有建立,系统会自动调用buildDrawingCache方法生成cache。
		 * 若果要更新cache, 必须要调用destoryDrawingCache方法把旧的cache销毁,才能建立新的。
		 */
		//        view.setDrawingCacheEnabled(true);  
		//        view.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);  
		//设置绘制缓存背景颜色
		//        view.setDrawingCacheBackgroundColor(Color.WHITE);  
 
		// 把一个View转换成图片  
		Bitmap cachebmp = loadBitmapFromView(view);  
 
		aaa.setImageBitmap(cachebmp);//直接展示转化的bitmap
 
		//保存在本地 产品还没决定要不要保存在本地
		FileOutputStream fos;  
		try {  
			// 判断手机设备是否有SD卡  
			boolean isHasSDCard = Environment.getExternalStorageState().equals(  
					android.os.Environment.MEDIA_MOUNTED);  
			if (isHasSDCard) {  
				// SD卡根目录  
				File sdRoot = Environment.getExternalStorageDirectory(); 
				Log.e("ssh",sdRoot.toString());
				File file = new File(sdRoot, "test.png");  
				fos = new FileOutputStream(file);  
			} else  
				throw new Exception("创建文件失败!");  
			//压缩图片 30 是压缩率,表示压缩70%; 如果不压缩是100,表示压缩率为0  
			cachebmp.compress(Bitmap.CompressFormat.PNG, 90, fos);  
 
			fos.flush();  
			fos.close();  
 
		} catch (Exception e) {  
			e.printStackTrace();  
		}  
 
		view.destroyDrawingCache();  
	}  
 
	private Bitmap loadBitmapFromView(View v) {  
		int w = v.getWidth();  
		int h = v.getHeight();  
		Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);  
		Canvas c = new Canvas(bmp);  
 
		c.drawColor(Color.WHITE);  
		/** 如果不设置canvas画布为白色,则生成透明 */  
 
		v.layout(0, 0, w, h);  
		v.draw(c);  
 
		return bmp;  
	} 
}

参考:

 

https://blog.csdn.net/qq_16247851/article/details/72772370

https://blog.csdn.net/shallcheek/article/details/46998643

https://blog.csdn.net/a450479378/article/details/53081814

https://blog.csdn.net/chenzheng8975/article/details/76206290

 

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值