Android Canvas和Bitmap结合绘图详解流程
xufei · 296浏览 · 发布于2021-11-03 +关注

在 Android Canvas 上绘图非常难,在绘图时需要理解许多不同的类和概念。这篇文章中,将介绍 Android 框架中可用的一些类,它们可以让画布使用时更轻松

Rect/RectF

存储四个值的矩形类:左侧、顶部、右侧和底部。可用于直接在画布上绘制或仅用于存储要绘制的对象的大小。Rect和RectF类之间的区别在于 RectF 存储浮点值,而Rect类存储整数。

private static Bitmap createDrawableBitmap(Drawable drawable) {
    int width = drawable.getIntrinsicWidth();
    int height = drawable.getIntrinsicHeight();
    if (width <= 0 || height <= 0) {
        return null;
    }
    float scale = Math.min(1.0f, ((float) MAX_IMAGE_SIZE) / ((float) (width * height)));
    if ((drawable instanceof BitmapDrawable) && scale == 1.0f) {
        return ((BitmapDrawable) drawable).getBitmap();
    }
    int bitmapWidth = (int) (((float) width) * scale);
    int bitmapHeight = (int) (((float) height) * scale);
    Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    Rect existingBounds = drawable.getBounds();
    int left = existingBounds.left;
    int top = existingBounds.top;
    int right = existingBounds.right;
    int bottom = existingBounds.bottom;
    drawable.setBounds(0, 0, bitmapWidth, bitmapHeight);
    drawable.draw(canvas);
    drawable.setBounds(left, top, right, bottom);
    return bitmap;
}

Matrix

一个3 x 3的矩阵,用于存储可用于转换画布的信息。矩阵可以存储以下类型的变换信息:缩放、倾斜、旋转、平移。而每种变换方式都对应着三种方法:set方法将用新值替换当前的Matrix,不管之前Matrix的值是什么。pre和post 方法将在当前Matrix包含的任何内容之前或之后应用新的转换。

Matrix m = new Matrix();
m.setRotate(90);
m.setScale(3f,1f);
m.setTranslate(200, 200);


只有平移,旋转值和缩放值被重置 

Matrix m = new Matrix();
m.preScale(3f,1f);
m.preTranslate(200f, 100f);
m.postScale(0.5f, 1f);
m.postTranslate(100f, 0f);


先进行平移(200f, 100f),然后进行缩放(3f, 1f),然后进行缩放(0.5f, 1f),最后进行平移(100f, 0f) 

Matrix m = new Matrix();
m.postTranslate(200f, 0f);
m.preScale(0.5f, 1f);
m.setScale(1f, 1f);
m.postScale(5f, 1f);
m.preTranslate(200f, 100f);


先进行平移(200f, 100f),然后进行缩放(1f, 1f),最后进行缩放(5f, 1f)。因为用了set方法所以平移(200f, 0f)和缩放(0.5f, 1f)被覆盖,不起作用 

假如先进行平移(x, y),再进行缩放(sx, sy),那么看到的平移效果等同于(x*sx, y*sy),因为缩放是将整个画布或者坐标系进行缩放的

Canvas

Canvas相当于Android的画布,可以把画布想象成一块内存空间,也就是一个Bitmap。Canvas的API提供一整套在这个Bitmap上进行绘图的操作方法。

  • drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)

使用指定的矩阵绘制位图,绘制的时候会使用矩阵进行变换,矩阵和画笔可以传入空值

  • drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint)

将传入的源图bitmap指定的矩形区域src绘制到目标矩形区域dst中,如果矩形区域src传入空值,则表示绘制整个源图到目标矩形区域dst中,绘制的时候源图或子集自动缩放/平移以填充目标矩形。如果绘制对应的画笔通过方法setMaskFilter指定了超出原始位图宽/高的掩码过滤器(如BlurMaskFilter),则会位图将继续被绘制,就像在具有CLAMP模式的着色器中一样。因此,原始宽/高之外的颜色将是复制的边缘颜色。因为源矩形区域src对应的坐标空间是相对于源图的,而目标矩形区域dst对应的坐标空间是绘制视图对应的坐标空间,因此要控制好对应的缩放因子。

  • drawBitmap (Bitmap bitmap, Rect src, RectF dst, Paint paint)

矩阵示例:

Bitmap background = Bitmap.createBitmap((int)width, (int)height, Config.ARGB_8888);
float originalWidth = originalImage.getWidth();
float originalHeight = originalImage.getHeight();
Canvas canvas = new Canvas(background);
float scale = width / originalWidth;
float xTranslation = 0.0f;
float yTranslation = (height - originalHeight * scale) / 2.0f;
Matrix transformation = new Matrix();
transformation.postTranslate(xTranslation, yTranslation);
transformation.preScale(scale, scale);

Paint paint = new Paint();
paint.setFilterBitmap(true);
canvas.drawBitmap(originalImage, transformation, paint);


矩形区域示例: 

public Bitmap cropCircle(Bitmap bitmap) {
  Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
      bitmap.getHeight(), Config.ARGB_8888);
  Canvas canvas = new Canvas(output);
  final int color = 0xff424242;
  final Paint paint = new Paint();
  final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
  paint.setAntiAlias(true);
  canvas.drawARGB(0, 0, 0, 0);
  paint.setColor(color);
  canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
      bitmap.getWidth()/2, paint);
  paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
  canvas.drawBitmap(bitmap, rect, rect, paint);
  return output;
}

Bitmap

位图,点阵图,可以理解为int[] buffer,用来存储每个像素点的容器。

  • Bitmap.createBitmap(int width, int height, Bitmap.Config config)

  • Bitmap.createBitmap(Bitmap src)

  • Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height)

  • Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter)

  • BitmapFactory.decodeByteArray(byte[] data, int offset, int length, BitmapFactory.Options opts)

  • BitmapFactory.decodeFile(String pathName, Options opts)

  • BitmapFactory.decodeStream(InputStream is, Rect outPadding,Options opts)

createBitmap生成示例:

public Bitmap transform(Bitmap source) {
  int size = Math.min(source.getWidth(), source.getHeight());
  int x = (source.getWidth() - size) / 2;
  int y = (source.getHeight() - size) / 2;
  Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
  if (squaredBitmap != source) {
    source.recycle();
  }
  Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());
  Canvas canvas = new Canvas(bitmap);
  Paint avatarPaint = new Paint();
  BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
  avatarPaint.setShader(shader);
  Paint outlinePaint = new Paint();
  outlinePaint.setColor(Color.WHITE);
  outlinePaint.setStyle(Paint.Style.STROKE);
  outlinePaint.setStrokeWidth(STROKE_WIDTH);
  outlinePaint.setAntiAlias(true);
  float r = size / 2f;
  canvas.drawCircle(r, r, r, avatarPaint);
  canvas.drawCircle(r, r, r - STROKE_WIDTH / 2, outlinePaint);
  squaredBitmap.recycle();
  return bitmap;
}


BitmapFactory生成示例: 

private static Bitmap decodeSampledBitmapFromUrl(String url, int reqWidth, int reqHeight) throws IOException {
        // First decode with inJustDecodeBounds=true to check dimensions
        final Options options = new Options();
        options.inJustDecodeBounds = true;
        InputStream stream = fetchStream(url);
        BitmapFactory.decodeStream(stream, null, options);
        stream.close();
        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        stream = fetchStream(url);
        Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
        stream.close();
        return bitmap;
    }
     
    private static InputStream fetchStream(String urlString) throws IllegalStateException, IOException {
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpGet request = new HttpGet(urlString);
        HttpResponse response = httpClient.execute(request);
        return response.getEntity().getContent();
    }
    
    private static int calculateInSampleSize(Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);

            // Choose the smallest ratio as inSampleSize value, this will guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
        return inSampleSize;
    }


注意:通过Bitmap.createBitmap生成的Bitmap对象是可变对象,可以向Bitmap上绘制内容,而通过BitmapFactory生成的Bitmap对象必须指定BitmapFactory.Options.inMutable = true,否则就是不可变对象,不能向上面绘制内容。 

感谢大家的支持,如有错误请指正,如需转载请标明原文出处!


相关推荐

android下vulkan与opengles纹理互通

talkchan · 819浏览 · 2020-11-23 10:37:39
Android 使用RecyclerView实现轮播图

奔跑的男人 · 1850浏览 · 2019-05-09 17:11:13
微软发布新命令行工具 Windows Terminal

吴振华 · 700浏览 · 2019-05-09 17:15:04
Facebook 停止屏蔽部分区块链广告

· 594浏览 · 2019-05-09 17:20:08
加载中

0评论

评论
分类专栏
小鸟云服务器
扫码进入手机网页