利用Android中BitmapShader制作自带边框的圆形头像

作者:daisy 时间:2021-12-25 20:09:11 

效果如下:

利用Android中BitmapShader制作自带边框的圆形头像

BitmapShader 的简单介绍

关于 Shader是什么,Shader的种类有哪几种以及如何使用不属于本文范畴,对这方面不是很了解的同学,建议先去学习一下 Shader 的基本使用。

BitmapShader主要的作用就是 通过Paint对象,对 画布进行指定的Bitmap填充,实现一系列效果,可以有以下三种模式进行选择

      1.CLAMP - 拉伸,这里拉伸的是图片的最后一个元素,不断地重复,这个效果,在图片比较小,而所要画的面积比较大的时候会比较明显。

      2.REPEAT - 重复,横向纵向不断地重复,不同于上一模式,这种模式在图片比较小不能满足要求是,会在横向纵向不断重复绘制图形。

      3.MIRROR - 翻转,这种模式和 REPEAT 是类似的,只不过这里的重复是翻转着重复,和折纸的效果差不多。

而我们将要使用的是 CLAMP 模式,因为只要我们对图形的大小进行控制,就可以避免图像进行拉伸。

具体实现介绍

为了自定义 图像,边框宽度和颜色,我们首先在 res/values 目录下,新建一个 attrs.xml文件,里面要书写的内容如下所示


<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
 <attr name="mborder_color" format="color"></attr>
 <attr name="mborder_width" format="dimension"></attr>
 <attr name="msrc" format="reference"></attr>
</declare-styleable>
</resources>

当然,在这里还可以添加一些其他的特性。既然定义了我们想要使用的特性,那么我们就要在自定义View里面 解析这些属性并且加以利用,解析过程如下所示


TypedArray type = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView);
mBorderColor = type.getColor(R.styleable.MyCustomView_mborder_color,0);
mDrawable = type.getDrawable(R.styleable.MyCustomView_msrc);

//将获得的 Drawable 转换成 Bitmap
BitmapDrawable bitmapDrawable = (BitmapDrawable) mDrawable;
mBitmap = bitmapDrawable.getBitmap();

mBorderWidth = type.getDimensionPixelSize(R.styleable.MyCustomView_mborder_width, 2);

值得注意的是 mSrc 属性的解析,由于获得是 Drawable 对象,所以我们需要将其转换为 Bitmap 对象。

下面就利用我们获得的 Bitmap 对象进行圆形头像的绘制,对 BitmapShader Paint 的初始化如下所示


 mSrcBitmap = Bitmap.createScaledBitmap(mBitmap, mWidth, mHeight, false);
 mShader = new BitmapShader(mSrcBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
 mPaint = new Paint();
 mPaint.setShader(mShader);
 mRadius = (mWidth - mBorderWidth * 2 - 4) / 2;
 mCircleX = (mWidth) / 2;
 mCircleY = (mHeight) / 2;

mSrcBitmap是对获得的图像进行适当的缩小或者放大,以适应我们对图形的要求,而这里的 mWidth mHeight 又是什么呢?实际上就是我们在 定义视图在 layout_width layout_height中传递进来的值,不过在这里我对他们进行了处理,也就是选取最小值操作,这样的话就可以避免由于宽大于高或者高大于宽而造成图像填充不满指定区域的现象。值得注意的是,自定义视图,需要对 wrap_content 进行特殊处理,否则系统对该属性的视图不予以显示。至于如何进行处理,可以看看本例的源码,很简单,相信很多人一看就知道或者说早就了然于胸。

还有一点需要强调的是这里的 mRadius ,也就是将要绘制的圆的半径,为什么要减去边框的宽度 乘 2 呢? 要知道,我们的圆是根据 视图指定的宽度或者高度来画的,如果我们所画 的圆恰好是指定视图的内切圆,那么边框放在哪里呢?它肯定要被画在视图的外面,那样我们就看不到完整的边框了。所以,这么减去的意义在于为边框腾出空间。

经过以上操作,我们就已经将圆形头像绘制出来了,下面来绘制边框,其实非常简单,我只不过是又定义了一个  Paint 对象,并且利用它画了一个圆而已,画笔的初始化操作如下所示


 mBorderPaint = new Paint();
 mBorderPaint.setStyle(Paint.Style.STROKE);
 mBorderPaint.setStrokeWidth(mBorderWidth);
 mBorderPaint.setColor(mBorderColor);
 mBorderPaint.setStrokeCap(Paint.Cap.ROUND);

好了,下面就可以在onDraw()函数中,进行绘制了,如下所示


@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 canvas.drawCircle(mCircleX, mCircleY, mRadius, mPaint);
 canvas.drawCircle(mCircleX, mCircleY, mRadius, mBorderPaint);
}

这样,整个效果就算实现完毕了。下面来看看如何使用


<com.example.hwaphon.patheffecttest.MyView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_marginBottom="16dp"
  android:layout_marginRight="8dp"
  app:mborder_color="@android:color/holo_green_light"
  app:mborder_width="4dp"
  app:msrc="@drawable/myview_test"/>

注意,一定要在容器中加上这么一句


xmlns:app=http://schemas.android.com/apk/res-auto

具体实现的核心代码


package com.example.hwaphon.patheffecttest;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;

/**
* Created by Hwaphon on 2016/5/12.
*/
public class MyView extends View {

private Bitmap mBitmap;
private Drawable mDrawable;
private Bitmap mSrcBitmap;

private BitmapShader mShader;
private Paint mPaint;

private int mWidth, mHeight;

private int mRadius;
private int mCircleX, mCircleY;

private int mBorderColor;
private Paint mBorderPaint;
private int mBorderWidth;

public MyView(Context context) {
 this(context, null);
}

public MyView(Context context, AttributeSet attrs) {
 super(context, attrs);
 TypedArray type = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView);
 mBorderColor = type.getColor(R.styleable.MyCustomView_mborder_color,0);
 mDrawable = type.getDrawable(R.styleable.MyCustomView_msrc);

//将获得的 Drawable 转换成 Bitmap
 BitmapDrawable bitmapDrawable = (BitmapDrawable) mDrawable;
 mBitmap = bitmapDrawable.getBitmap();

mBorderWidth = type.getDimensionPixelSize(R.styleable.MyCustomView_mborder_width, 2);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 mWidth = measureWidth(widthMeasureSpec);
 mHeight = measureHeight(heightMeasureSpec);
 int temp = mWidth > mHeight ? mHeight : mWidth;
 mWidth = mHeight = temp;
 initView();
 setMeasuredDimension(mWidth, mHeight);
}

private int measureHeight(int heightMeasureSpec) {
 int size = MeasureSpec.getSize(heightMeasureSpec);
 int sizeMode = MeasureSpec.getMode(heightMeasureSpec);
 int result = 0;
 if (sizeMode == MeasureSpec.EXACTLY) {
  result = size;
 } else {
  result = 200;
  if (sizeMode == MeasureSpec.AT_MOST) {
   result = Math.min(result, size);
  }
 }
 return result;
}

private int measureWidth(int widthMeasureSpec) {
 int size = MeasureSpec.getSize(widthMeasureSpec);
 int sizeMode = MeasureSpec.getMode(widthMeasureSpec);
 int result = 0;
 if (sizeMode == MeasureSpec.EXACTLY) {
  result = size;
 } else {
  result = 200;
  if (sizeMode == MeasureSpec.AT_MOST) {
   result = Math.min(result, size);
  }
 }
 return result;
}

private void initView() {

mSrcBitmap = Bitmap.createScaledBitmap(mBitmap, mWidth, mHeight, false);
 mShader = new BitmapShader(mSrcBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
 mPaint = new Paint();
 mPaint.setShader(mShader);
 mRadius = (mWidth - mBorderWidth * 2) / 2;
 mCircleX = (mWidth) / 2;
 mCircleY = (mHeight) / 2;

mBorderPaint = new Paint();
 mBorderPaint.setStyle(Paint.Style.STROKE);
 mBorderPaint.setStrokeWidth(mBorderWidth);
 mBorderPaint.setColor(mBorderColor);
 mBorderPaint.setStrokeJoin(Paint.Join.ROUND);
 mBorderPaint.setStrokeCap(Paint.Cap.ROUND);
}

@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 canvas.drawCircle(mCircleX, mCircleY, mRadius, mPaint);
 canvas.drawCircle(mCircleX, mCircleY, mRadius, mBorderPaint);
}
}

总结

标签:android,bitmapshader,圆形,头像
0
投稿

猜你喜欢

  • Java并发编程之栅栏(CyclicBarrier)实例介绍

    2023-12-01 19:48:53
  • Java使用Collections工具类对List集合进行排序

    2022-10-12 23:59:12
  • Java实现简单的飞机大战游戏(控制主飞机篇)

    2023-11-14 13:52:56
  • Android5.0中多种水波纹效果的实现代码

    2023-09-23 04:42:53
  • C#实现简单文本编辑器

    2022-04-28 06:42:30
  • 教你怎么用Java获取国家法定节假日

    2021-05-24 01:16:04
  • 浅谈Java中ArrayList线程不安全怎么办

    2023-10-02 19:24:56
  • java生成随机数的方法

    2023-12-12 12:49:51
  • SpringMVC @RequestBody自动转json Http415错误的解决

    2022-09-12 13:12:34
  • C# Winform中如何绘制动画示例详解

    2022-03-28 13:26:26
  • Java遍历json字符串取值的实例

    2023-09-02 17:03:17
  • C#封装的常用文件操作类实例

    2021-08-14 10:08:03
  • 通过springboot+mybatis+druid配置动态数据源

    2023-06-20 16:48:30
  • 详解Java设计模式之桥接模式

    2022-08-05 23:27:08
  • 解析Java的Hibernate框架中的持久化类和映射文件

    2023-08-19 15:00:05
  • springcloud gateway如何实现路由和负载均衡

    2023-01-09 07:13:35
  • springboot 使用QQ邮箱发送邮件的操作方法

    2022-03-03 14:36:22
  • Java为实体类动态添加属性的方法详解

    2023-09-01 05:31:43
  • java实现字符串反转案例

    2021-11-20 04:45:55
  • 浅谈Spring Cloud Ribbon的原理

    2023-07-23 04:11:25
  • asp之家 软件编程 m.aspxhome.com