保存 canvas,带有绘制圆圈的位图
saving a canvas, bitmap with drawn circles
我有以下 class 下面的内容,它扩展了 View 并覆盖了 onDraw 方法以在已加载的位图上绘制圆圈。
位图加载成功并显示在屏幕上。当用户触摸屏幕时,会绘制一个圆圈。
我有一个将位图压缩成字节 [] 的浮动操作按钮。这个 byte[] 然后被发送到另一个 Activity 来显示。不幸的是,生成的位图中没有任何圆圈。
canvas 是 onDraw 中的局部对象,mCBitmap 和 tCanvas 是全局变量,以便方法 saveImage 可以访问数据。
谁能告诉我为什么 none 个圆被复制到生成的位图中?
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Environment;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import org.apache.commons.io.IOUtils;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.graphics.PointF;
/**
* Created by MatthewW on 14/06/2017.
*/
public class TouchView extends View {
private static final String TAG = TouchView.class.getSimpleName();
Bitmap bgr;
File tempFile;
private byte[] imageArray;
private Paint pTouch;
Context context;
private SparseArray<ColouredCircle> mPointers;
public int x;
public int y;
int circleCount;
int radius;
protected byte [] data;
Bitmap mCBitmap;
Canvas tCanvas;
public TouchView(Context context) {
super(context);
this.context = context;
}
public TouchView(Context context, AttributeSet attr) {
super(context,attr);
Log.e(TAG, "inside touchview constructor");
this.context = context;
radius = 70;
circleCount = 0;
copyReadAssets();
imageArray = new byte[(int)tempFile.length()];
Log.e(TAG, "imageArray has length = " + imageArray.length);
try{
InputStream is = new FileInputStream(tempFile);
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);
int i = 0;
while (dis.available() > 0 ) {
imageArray[i] = dis.readByte();
i++;
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
}
Bitmap bm = BitmapFactory.decodeByteArray(imageArray, 0, imageArray.length);
if(bm == null){
Log.e(TAG, "bm = null");
}else{
Log.e(TAG, "bm = not null");
}
mPointers = new SparseArray<ColouredCircle>();
bgr = bm.copy(bm.getConfig(), true);
bm.recycle();
pTouch = new Paint(Paint.ANTI_ALIAS_FLAG);
// pTouch.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));
pTouch.setColor(Color.RED);
pTouch.setStyle(Paint.Style.STROKE);
pTouch.setStrokeWidth(5);
}// end of touchView constructor
private void copyReadAssets() {
AssetManager assetManager = context.getAssets();
InputStream in = null;
OutputStream out = null;
tempFile = new File(context.getFilesDir(), "bodymap.jfif");
try {
in = assetManager.open("bodymap.jfif");
out = context.openFileOutput(tempFile.getName(), Context.MODE_WORLD_READABLE);
IOUtils.copy(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
Log.e(TAG, "about to draw bgr ");
// canvas.drawBitmap(bgr, 0, 0, null);
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
Rect frameToDraw = new Rect(0, 0, width, height);
RectF whereToDraw = new RectF(0, 0, width, height - 300);
canvas.drawBitmap(bgr,frameToDraw,whereToDraw, null);
if(mPointers != null) {
Log.e(TAG, "mPointers.size() = " + mPointers.size());
for (int i = 0; i < mPointers.size(); i++) {
PointF p = mPointers.get(i).getPointF();
x = (int) p.x;
y = (int) p.y;
pTouch.setColor(mPointers.get(i).getColour());
canvas.drawCircle(x, y, radius, pTouch);
mCBitmap = Bitmap.createBitmap(bgr.getWidth(), bgr.getHeight(), bgr.getConfig());
tCanvas = new Canvas(mCBitmap);
tCanvas.drawBitmap(bgr, 0, 0, null);
tCanvas.drawCircle(x, y, radius, pTouch);
tCanvas.drawBitmap(mCBitmap, 0, 0, null);
}
}
}//end of onDraw
@Override
public boolean onTouchEvent(MotionEvent me) {
switch (me.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN: {
/*int ai = me.getActionIndex();
PointF pt = new PointF(me.getX(ai), me.getY(ai));
mPointers.put(me.getPointerId(ai), pt);
Log.e(TAG, "mPointers.size() = " + mPointers.size() + "me.getPointerId(ai) = "
+ me.getPointerId(ai) + " me.getX(ai) = " + me.getX(ai) + " me.getY(ai) = " + me.getY(ai));*/
int ai = me.getActionIndex();
PointF pt = new PointF(me.getX(ai), me.getY(ai));
ColouredCircle cc = new ColouredCircle(pTouch.getColor(),pt);
mPointers.put(circleCount, cc);
circleCount++;
invalidate();
return true;
}
case MotionEvent.ACTION_UP: {
}
case MotionEvent.ACTION_POINTER_UP: {
/*int pid = me.getPointerId(me.getActionIndex());
mPointers.remove(pid);*/
return true;
}
case MotionEvent.ACTION_MOVE: {
/*for (int i = 0; i < me.getPointerCount(); ++i) {
PointF pt = mPointers.get(me.getPointerId(i));
pt.set(me.getX(i), me.getY(i));
invalidate();
}*/
return true;
}
}
return false;
}
public void showToastMessage(String mess){
Toast.makeText(TouchView.this.getContext(), mess.toString(), Toast.LENGTH_SHORT).show();
}
public int getRadius() {
return radius;
}
public void setRadius(int r) {
radius = r;
invalidate();
}
public void setCircleColour(String colourMode){
if(colourMode.equalsIgnoreCase("RED")){
pTouch.setColor(Color.RED);
}else if(colourMode.equalsIgnoreCase("BLUE")){
pTouch.setColor(Color.BLUE);
}else if(colourMode.equalsIgnoreCase("GREY")){
pTouch.setColor(Color.GRAY);
}
}
public byte[] saveImage(){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
mCBitmap.compress(Bitmap.CompressFormat.JPEG, 100 /*ignored for PNG*/, bos);
data = bos.toByteArray();
try {
bos.flush();
bos.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
bos.flush();
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if ( data == null){
Log.e(TAG, "data in touchview before save clicked is null");
}else{
Log.e(TAG, "data in touchview before saved clicked is not null and has length + " + data.length);
}
return data;
}
}//end of class
[编辑 1]
我已将我的 onDraw() 和 saveImage() 方法更改为以下内容。图像现在与圆圈一起保存。
然而,bgr 图像非常小,位于屏幕的左上角,(屏幕的其余部分是黑色的)。圆圈在正确的位置,因为它们在原始位置,只是背景图像不是全屏尺寸。
如何将 bg 图像以完整尺寸复制到生成的位图中?
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
Log.e(TAG, "about to draw bgr ");
// canvas.drawBitmap(bgr, 0, 0, null);
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
Rect frameToDraw = new Rect(0, 0, width, height);
RectF whereToDraw = new RectF(0, 0, width, height - 300);
canvas.drawBitmap(bgr,frameToDraw,whereToDraw, null);
if(mPointers != null) {
Log.e(TAG, "mPointers.size() = " + mPointers.size());
for (int i = 0; i < mPointers.size(); i++) {
PointF p = mPointers.get(i).getPointF();
x = (int) p.x;
y = (int) p.y;
pTouch.setColor(mPointers.get(i).getColour());
canvas.drawCircle(x, y, radius, pTouch);
}
}
}//end of onDraw
.
public byte[] saveImage(){
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels - 300;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
//Bitmap bitmap = Bitmap.createBitmap(bgr.getWidth(), bgr.getHeight(), bgr.getConfig());
Canvas canvas = new Canvas(bitmap);
this.draw(canvas);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100 /*ignored for PNG*/, bos);
data = bos.toByteArray();
try {
bos.flush();
bos.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
bos.flush();
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if ( data == null){
Log.e(TAG, "data in touchview before save clicked is null");
}else{
Log.e(TAG, "data in touchview before saved clicked is not null and has length + " + data.length);
}
return data;
}
.
这是一张 Activity 的图片,允许用户在图片上放置圆圈。 bg图片是全屏的。
下面是下一个 Activity 中的输出图片,其中带圆圈的图像被转换为 byte[] 并显示。如您所见,背景图像不是全屏,但圆圈在正确的位置。
在onDraw(...)
中,更改:
Rect frameToDraw = new Rect(0, 0, width, height);
收件人:
Rect frameToDraw = new Rect(0, 0, bgr.getWidth(), bgr.getHeight());
当框架调用 onDraw(...)
时,在 onDraw(...)
中显示图像时没有出现此问题的一个原因可能是 Canvas
object 框架提供的默认情况下是硬件加速的,如果你有一个目标 sdk >= 14(source,第二段,第一句)
正如所看到的 here,当 canvas 是硬件加速时,它的密度似乎设置为 0。但是,如果你在你的字段上调用 Bitmap.getDensity()
TouchView#bgr
,你会看到它不会是 0。那是因为位图在创建时具有当前设备屏幕的密度。
接下来,您在 0 ppi 上绘制高密度位图(在我的设备上密度为 320 ppi)Canvas --> 它会被绘制得更大。
但是,在 saveImage 中,您从头开始创建一个 Bitmap
,然后从该位图中创建一个 Canvas
,然后提供此 canvas 以调用 draw(...)
.您刚刚创建的这个新 canvas 不会进行硬件加速,并且还将具有您新创建的位图的密度(在我的例子中为 320 ppi)。
当您在 onDraw(...)
中绘制 body 地图图像时,您将以 320dp 将其绘制在此 Canvas 上,与您的图像相同。
这仍然可以正常工作,但是您指定要绘制屏幕宽度 x 屏幕高度的区域,假设 1920x1080,从您的 body 地图,这很可能更小,让我们假设 1000x600,用 frameToDraw
.
在这种情况下 Android 会做什么?那么,Canvas#drawBitmap(...)
的 Javadoc 告诉我们以下内容:
Note: if the paint contains a maskfilter that generates a mask which
extends beyond the bitmap's original width/height (e.g.
BlurMaskFilter), then the bitmap will be drawn as if it were in a
Shader with CLAMP mode. Thus the color outside of the original
width/height will be the edge color replicated.
这基本上意味着:我将采用您的 1000x600 并按照应绘制的方式绘制像素,其余像素直到 1920x1080 我将填充我选择的一些颜色(可能是黑色或白色,还是透明的?)
现在,您通过 whereToDraw
说您希望将 1920x1080 区域映射到 1620x1080 区域。为此,android 将进一步缩小位图,使 bodymap
变得更小。
最简单的解决方案是在创建 frameToDraw
区域时在 onDraw(...)
中指定位图的实际尺寸。
此外,如果这些代码片段是测试代码,那很好,但是请务必遵循 here 中提到的准则,在使用 onDraw(...)
时,当您清理代码时.
最重要的一点是:
你真的不应该在你的 onDraw(...)
方法中分配 object,因为它们可能会被经常调用,这会杀死你的垃圾收集器。 (即 frameToDraw 和 whereToDraw - 您可以在构造函数中预先分配它们)
您可以利用视图的绘图缓存更轻松地获取屏幕的位图。
只需将您的 saveImage
更改为:
public byte[] saveImage() {
//reder the view once, in software.
buildDrawingCache();
//get the rendered bitmap
Bitmap bitmap = getDrawingCache();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100 /*ignored for PNG*/, bos);
byte[] data = bos.toByteArray();
//destroy the drawing cache, to clear up the memory
destroyDrawingCache();
try {
bos.flush();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
if (data == null) {
Log.e(TAG, "data in touchview before save clicked is null");
} else {
Log.e(TAG, "data in touchview before saved clicked is not null and has length + " + data.length);
}
return data;
}
参见:
希望对您有所帮助
我有以下 class 下面的内容,它扩展了 View 并覆盖了 onDraw 方法以在已加载的位图上绘制圆圈。
位图加载成功并显示在屏幕上。当用户触摸屏幕时,会绘制一个圆圈。
我有一个将位图压缩成字节 [] 的浮动操作按钮。这个 byte[] 然后被发送到另一个 Activity 来显示。不幸的是,生成的位图中没有任何圆圈。
canvas 是 onDraw 中的局部对象,mCBitmap 和 tCanvas 是全局变量,以便方法 saveImage 可以访问数据。
谁能告诉我为什么 none 个圆被复制到生成的位图中?
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Environment;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import org.apache.commons.io.IOUtils;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.graphics.PointF;
/**
* Created by MatthewW on 14/06/2017.
*/
public class TouchView extends View {
private static final String TAG = TouchView.class.getSimpleName();
Bitmap bgr;
File tempFile;
private byte[] imageArray;
private Paint pTouch;
Context context;
private SparseArray<ColouredCircle> mPointers;
public int x;
public int y;
int circleCount;
int radius;
protected byte [] data;
Bitmap mCBitmap;
Canvas tCanvas;
public TouchView(Context context) {
super(context);
this.context = context;
}
public TouchView(Context context, AttributeSet attr) {
super(context,attr);
Log.e(TAG, "inside touchview constructor");
this.context = context;
radius = 70;
circleCount = 0;
copyReadAssets();
imageArray = new byte[(int)tempFile.length()];
Log.e(TAG, "imageArray has length = " + imageArray.length);
try{
InputStream is = new FileInputStream(tempFile);
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);
int i = 0;
while (dis.available() > 0 ) {
imageArray[i] = dis.readByte();
i++;
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
}
Bitmap bm = BitmapFactory.decodeByteArray(imageArray, 0, imageArray.length);
if(bm == null){
Log.e(TAG, "bm = null");
}else{
Log.e(TAG, "bm = not null");
}
mPointers = new SparseArray<ColouredCircle>();
bgr = bm.copy(bm.getConfig(), true);
bm.recycle();
pTouch = new Paint(Paint.ANTI_ALIAS_FLAG);
// pTouch.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));
pTouch.setColor(Color.RED);
pTouch.setStyle(Paint.Style.STROKE);
pTouch.setStrokeWidth(5);
}// end of touchView constructor
private void copyReadAssets() {
AssetManager assetManager = context.getAssets();
InputStream in = null;
OutputStream out = null;
tempFile = new File(context.getFilesDir(), "bodymap.jfif");
try {
in = assetManager.open("bodymap.jfif");
out = context.openFileOutput(tempFile.getName(), Context.MODE_WORLD_READABLE);
IOUtils.copy(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
Log.e(TAG, "about to draw bgr ");
// canvas.drawBitmap(bgr, 0, 0, null);
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
Rect frameToDraw = new Rect(0, 0, width, height);
RectF whereToDraw = new RectF(0, 0, width, height - 300);
canvas.drawBitmap(bgr,frameToDraw,whereToDraw, null);
if(mPointers != null) {
Log.e(TAG, "mPointers.size() = " + mPointers.size());
for (int i = 0; i < mPointers.size(); i++) {
PointF p = mPointers.get(i).getPointF();
x = (int) p.x;
y = (int) p.y;
pTouch.setColor(mPointers.get(i).getColour());
canvas.drawCircle(x, y, radius, pTouch);
mCBitmap = Bitmap.createBitmap(bgr.getWidth(), bgr.getHeight(), bgr.getConfig());
tCanvas = new Canvas(mCBitmap);
tCanvas.drawBitmap(bgr, 0, 0, null);
tCanvas.drawCircle(x, y, radius, pTouch);
tCanvas.drawBitmap(mCBitmap, 0, 0, null);
}
}
}//end of onDraw
@Override
public boolean onTouchEvent(MotionEvent me) {
switch (me.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN: {
/*int ai = me.getActionIndex();
PointF pt = new PointF(me.getX(ai), me.getY(ai));
mPointers.put(me.getPointerId(ai), pt);
Log.e(TAG, "mPointers.size() = " + mPointers.size() + "me.getPointerId(ai) = "
+ me.getPointerId(ai) + " me.getX(ai) = " + me.getX(ai) + " me.getY(ai) = " + me.getY(ai));*/
int ai = me.getActionIndex();
PointF pt = new PointF(me.getX(ai), me.getY(ai));
ColouredCircle cc = new ColouredCircle(pTouch.getColor(),pt);
mPointers.put(circleCount, cc);
circleCount++;
invalidate();
return true;
}
case MotionEvent.ACTION_UP: {
}
case MotionEvent.ACTION_POINTER_UP: {
/*int pid = me.getPointerId(me.getActionIndex());
mPointers.remove(pid);*/
return true;
}
case MotionEvent.ACTION_MOVE: {
/*for (int i = 0; i < me.getPointerCount(); ++i) {
PointF pt = mPointers.get(me.getPointerId(i));
pt.set(me.getX(i), me.getY(i));
invalidate();
}*/
return true;
}
}
return false;
}
public void showToastMessage(String mess){
Toast.makeText(TouchView.this.getContext(), mess.toString(), Toast.LENGTH_SHORT).show();
}
public int getRadius() {
return radius;
}
public void setRadius(int r) {
radius = r;
invalidate();
}
public void setCircleColour(String colourMode){
if(colourMode.equalsIgnoreCase("RED")){
pTouch.setColor(Color.RED);
}else if(colourMode.equalsIgnoreCase("BLUE")){
pTouch.setColor(Color.BLUE);
}else if(colourMode.equalsIgnoreCase("GREY")){
pTouch.setColor(Color.GRAY);
}
}
public byte[] saveImage(){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
mCBitmap.compress(Bitmap.CompressFormat.JPEG, 100 /*ignored for PNG*/, bos);
data = bos.toByteArray();
try {
bos.flush();
bos.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
bos.flush();
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if ( data == null){
Log.e(TAG, "data in touchview before save clicked is null");
}else{
Log.e(TAG, "data in touchview before saved clicked is not null and has length + " + data.length);
}
return data;
}
}//end of class
[编辑 1]
我已将我的 onDraw() 和 saveImage() 方法更改为以下内容。图像现在与圆圈一起保存。
然而,bgr 图像非常小,位于屏幕的左上角,(屏幕的其余部分是黑色的)。圆圈在正确的位置,因为它们在原始位置,只是背景图像不是全屏尺寸。
如何将 bg 图像以完整尺寸复制到生成的位图中?
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
Log.e(TAG, "about to draw bgr ");
// canvas.drawBitmap(bgr, 0, 0, null);
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
Rect frameToDraw = new Rect(0, 0, width, height);
RectF whereToDraw = new RectF(0, 0, width, height - 300);
canvas.drawBitmap(bgr,frameToDraw,whereToDraw, null);
if(mPointers != null) {
Log.e(TAG, "mPointers.size() = " + mPointers.size());
for (int i = 0; i < mPointers.size(); i++) {
PointF p = mPointers.get(i).getPointF();
x = (int) p.x;
y = (int) p.y;
pTouch.setColor(mPointers.get(i).getColour());
canvas.drawCircle(x, y, radius, pTouch);
}
}
}//end of onDraw
.
public byte[] saveImage(){
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels - 300;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
//Bitmap bitmap = Bitmap.createBitmap(bgr.getWidth(), bgr.getHeight(), bgr.getConfig());
Canvas canvas = new Canvas(bitmap);
this.draw(canvas);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100 /*ignored for PNG*/, bos);
data = bos.toByteArray();
try {
bos.flush();
bos.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
bos.flush();
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if ( data == null){
Log.e(TAG, "data in touchview before save clicked is null");
}else{
Log.e(TAG, "data in touchview before saved clicked is not null and has length + " + data.length);
}
return data;
}
.
这是一张 Activity 的图片,允许用户在图片上放置圆圈。 bg图片是全屏的。
下面是下一个 Activity 中的输出图片,其中带圆圈的图像被转换为 byte[] 并显示。如您所见,背景图像不是全屏,但圆圈在正确的位置。
在onDraw(...)
中,更改:
Rect frameToDraw = new Rect(0, 0, width, height);
收件人:
Rect frameToDraw = new Rect(0, 0, bgr.getWidth(), bgr.getHeight());
当框架调用 onDraw(...)
时,在 onDraw(...)
中显示图像时没有出现此问题的一个原因可能是 Canvas
object 框架提供的默认情况下是硬件加速的,如果你有一个目标 sdk >= 14(source,第二段,第一句)
正如所看到的 here,当 canvas 是硬件加速时,它的密度似乎设置为 0。但是,如果你在你的字段上调用 Bitmap.getDensity()
TouchView#bgr
,你会看到它不会是 0。那是因为位图在创建时具有当前设备屏幕的密度。
接下来,您在 0 ppi 上绘制高密度位图(在我的设备上密度为 320 ppi)Canvas --> 它会被绘制得更大。
但是,在 saveImage 中,您从头开始创建一个 Bitmap
,然后从该位图中创建一个 Canvas
,然后提供此 canvas 以调用 draw(...)
.您刚刚创建的这个新 canvas 不会进行硬件加速,并且还将具有您新创建的位图的密度(在我的例子中为 320 ppi)。
当您在 onDraw(...)
中绘制 body 地图图像时,您将以 320dp 将其绘制在此 Canvas 上,与您的图像相同。
这仍然可以正常工作,但是您指定要绘制屏幕宽度 x 屏幕高度的区域,假设 1920x1080,从您的 body 地图,这很可能更小,让我们假设 1000x600,用 frameToDraw
.
在这种情况下 Android 会做什么?那么,Canvas#drawBitmap(...)
的 Javadoc 告诉我们以下内容:
Note: if the paint contains a maskfilter that generates a mask which extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), then the bitmap will be drawn as if it were in a Shader with CLAMP mode. Thus the color outside of the original width/height will be the edge color replicated.
这基本上意味着:我将采用您的 1000x600 并按照应绘制的方式绘制像素,其余像素直到 1920x1080 我将填充我选择的一些颜色(可能是黑色或白色,还是透明的?)
现在,您通过 whereToDraw
说您希望将 1920x1080 区域映射到 1620x1080 区域。为此,android 将进一步缩小位图,使 bodymap
变得更小。
最简单的解决方案是在创建 frameToDraw
区域时在 onDraw(...)
中指定位图的实际尺寸。
此外,如果这些代码片段是测试代码,那很好,但是请务必遵循 here 中提到的准则,在使用 onDraw(...)
时,当您清理代码时.
最重要的一点是:
你真的不应该在你的
onDraw(...)
方法中分配 object,因为它们可能会被经常调用,这会杀死你的垃圾收集器。 (即 frameToDraw 和 whereToDraw - 您可以在构造函数中预先分配它们)您可以利用视图的绘图缓存更轻松地获取屏幕的位图。
只需将您的 saveImage
更改为:
public byte[] saveImage() {
//reder the view once, in software.
buildDrawingCache();
//get the rendered bitmap
Bitmap bitmap = getDrawingCache();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100 /*ignored for PNG*/, bos);
byte[] data = bos.toByteArray();
//destroy the drawing cache, to clear up the memory
destroyDrawingCache();
try {
bos.flush();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
if (data == null) {
Log.e(TAG, "data in touchview before save clicked is null");
} else {
Log.e(TAG, "data in touchview before saved clicked is not null and has length + " + data.length);
}
return data;
}
参见:
希望对您有所帮助