其他
安卓自定义view中绘画几何图形和文字及圆角ImageView图片等api使用及举例
本文字数:9953字
预计阅读时间:25分钟
本文旨在介绍自定义View的实现及流程着重处介绍安卓中的Canvas的绘制方法,让你从容面对各种view 并附带自定义圆角ImageView,和图片压缩处理等功能。
View 绘制的三个流程
在自定义View的时候一般需要重写父类的onMeasure()、onLayout()、onDraw()三个方法,来完成视图的展示过程。当然,这三个暴露给开发者重写的方法只不过是整个绘制流程的冰山一角,更多复杂的幕后工作,都让系统给代劳了。
一个完整的绘制流程包括measure、layout、draw三个步骤,其中:
measure:测量。系统会先根据xml布局文件和代码中对控件属性的设置,来获取或者计算出每个View和ViewGrop的尺寸,并将这些尺寸保存下来。
layout:布局。根据测量出的结果以及对应的参数,来确定每一个控件应该显示的位置。
draw:绘制。确定好位置后,就将这些控件绘制到屏幕上。
重中之重:draw
对应的API进行详解和举例使用
绘制单点、多点
基本思路继承VIew-重写onDraw方法-进行绘制
//获取画笔,设置画笔属性
//调起画布canvas的drawPoint方法输入相应的参数即可绘画出对应的点。
Paint对于Text的相关设置
1.普通设置
paint.setStrokeWidth(5):设置画笔宽度;
paint.setAntiAlias(true):设置是否使用抗锯齿功能,如果使用,会导致绘图速度变慢;
paint.setStyle(Paint.Style.FILL):设置绘图样式,对于设置文字和几何图形都有效,可取值有三种 :
2、Paint.Style.FILL_AND_STROKE:填充内部和描边
3、Paint.Style.STROKE:仅描边
paint.setTextAlign(Align.CENTER):设置文字对齐方式
paint.setTextSize(12):设置文字大小
paint.setUnderlineText(true):设置下划线
paint.setTextSkewX((float) -0.25):设置字体水平倾斜度,普通斜体字是 -0.25
paint.setStrikeThruText(true):设置带有删除线效果
下面是绘制的相应内容图例展示:
详细代码实现
class ProjectResources(private val resources: Resources) {
val paintLight by lazy {
getBasePaint().apply {
color = Color.LTGRAY
alpha = 128
strokeWidth = resources.dpToPx(2)
textSize = resources.dpToPx(30)
}
}
val paintDark by lazy {
getBasePaint().apply {
color = Color.BLACK
alpha = 128
strokeWidth = resources.dpToPx(4)
textSize = resources.dpToPx(30)
}
}
private fun getBasePaint(): Paint {
return Paint().apply {
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND
strokeJoin = Paint.Join.ROUND
isAntiAlias = true
textAlign = Paint.Align.CENTER
textSize = resources.dpToPx(30)
}
}
}`
点
🔺绘制单点 drawPoint🔺
* 绘制单点 drawPoint
*/
class DrawPointView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawPoint(
0f + paddingStart,//x坐标
0f + paddingTop,//Y坐标
getPaint()//画笔paint
)
}
//获取画笔 设置画笔属性
private fun getPaint(): Paint {
return Paint().apply {
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND
strokeJoin = Paint.Join.ROUND
isAntiAlias = true
textAlign = Paint.Align.CENTER
textSize = resources.dpToPx(30)
color = Color.GREEN//画笔颜色
strokeWidth = resources.dpToPx(16)//画笔宽度
}
}
}
🔺绘制多点 drawPoints🔺
* 绘制多点 drawPoints
*/
class DrawPointsView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawPoints(
listOf(
0f + paddingStart, 0f + paddingTop,//第1个点的 x,y
((width - paddingEnd)).toFloat(), 0f + paddingTop, //第2个点的 x,y
((width - paddingEnd)/2).toFloat(),((height - paddingBottom)/2).toFloat(), //第3个点的 x,y
0f + paddingStart, ((height - paddingBottom)).toFloat(), //第4个点的 x,y
(width - paddingEnd).toFloat(), (height - paddingBottom).toFloat() //第5个点的 x,y
).toFloatArray(),
projectResources.paintPoint
)
}
}
线
🔺绘制直线 drawLine🔺
class DrawLineView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawLine(
0f + paddingStart,//起始位置 x坐标
0f + paddingTop,//起始位置 y坐标
(width - paddingEnd).toFloat(),//终点位置 X坐标
(height - paddingBottom).toFloat(),//终点位置 Y坐标
projectResources.paint// 获取画笔
)
}
}
🔺同时绘制多条线🔺
drawLines
* 同时绘制多条线(1) drawLines
*/
class DrawLinesView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawLines(
listOf(
//第一条线的起始x坐标和Y坐标 末尾位置的X坐标和Y坐标
0f + paddingStart, 0f + paddingTop, (width / 2f), (height - paddingBottom).toFloat(),
//第二条线的起始x坐标和Y坐标 末尾位置的X坐标和Y坐标
width / 2f, (height - paddingBottom).toFloat(), (width - paddingEnd).toFloat(), (0f + paddingStart),
//第三条线的起始x坐标和Y坐标 末尾位置的X坐标和Y坐标
(width - paddingEnd).toFloat(), (0f + paddingStart), width / 2f, (height/2f).toFloat()
).toFloatArray(),
projectResources.paint//画笔
)
}
}
🔺同时绘制多条线🔺
//使用指定的画笔绘制指定的路径(线路)。路径(线路)将根据绘画的风格填充或装裱。
两条线交叉
class DrawPathView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
private val path by lazy {
val path = Path()
path.moveTo(0f + paddingStart, 0f + paddingTop)
path.lineTo((width - paddingEnd).toFloat(), (height - paddingBottom).toFloat())
path.moveTo(0f + paddingStart, (height - paddingBottom).toFloat())
path.lineTo((width - paddingEnd).toFloat(), 0f + paddingTop)
path
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
// * Draw the specified path using the specified paint. The path will be filled or framed based on
// * the Style in the paint.
canvas.drawPath(path, projectResources.paint)
}
}
🔺绘画圆形图 drawCircle🔺
* 绘画圆形图 drawCircle
*/
class DrawCircleView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawCircle(
width/2f,//圆点中心 X坐标
height/2f,//圆点中心 Y坐标
height/2f - paddingTop,//圆半径
projectResources.paint//画笔
)
}
}
🔺绘制矩形框 drawRect🔺
* 绘制矩形框 drawRect
确定确定一个矩形最少需要四个数据,就是对角线的两个点的坐标值,这里一般采用左上角和右下角的两个点的坐标。
*/
class DrawRectView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
private val rect by lazy {
Rect(
0 + paddingStart,//左
0 + paddingTop,//顶
width - paddingEnd,//右
height - paddingBottom//底
)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawRect(rect, projectResources.paint)
}
}
🔺绘制圆角矩形框 drawRoundRect🔺
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
private val rect by lazy {
RectF(
0f + paddingStart,
0f + paddingTop,
(width - paddingEnd).toFloat(),
(height - paddingBottom).toFloat()
)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawRoundRect(rect, resources.dpToPx(10), resources.dpToPx(10), projectResources.paint)
}
}
🔺绘制椭圆框 drawOval🔺
class DrawOvalView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
private val rect by lazy {
RectF(
0f + paddingStart,//包围椭圆的矩形左上角端点x
0f + paddingTop,//包围椭圆的矩形左上角端点Y
width.toFloat() - paddingEnd,//椭圆的高度
height.toFloat() - paddingBottom//椭圆的宽度 当高度和宽度相等时 就是圆
)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawOval(rect, projectResources.paint)
}
}
🔺扇形绘制 (椭圆和圆的)🔺
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
private val rect by lazy {
RectF(
0f + paddingStart,
0f + paddingTop,
height.toFloat() - paddingBottom,
height.toFloat() - paddingBottom
)//// 设置方形区域
}
private val rect2 by lazy {
RectF(
0f + paddingStart,
0f + paddingTop,
width.toFloat() - paddingEnd,
height.toFloat() - paddingBottom
)//// 设置矩形区域
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
/* 设置渐变色 这个正方形的颜色是改变的 */
val mShader = LinearGradient(
0f,
0f,
100f,
100f,
intArrayOf(Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW, Color.LTGRAY),
null,
Shader.TileMode.REPEAT
) // 一个材质,打造出一个线性梯度沿著一条线。
projectResources.paint.shader = mShader
if (width == 0 || height == 0) return
canvas.drawArc(rect,
0f,//从哪个弧度开始
330f,//扇形弧度
true, //是否经过圆心
projectResources.paint)
canvas.drawArc(rect2,
0f,//从哪个弧度开始
30f,//扇形弧度
true, //是否经过圆心
projectResources.paint)
}
}
文字绘制
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
companion object {
private const val TEXT = "三国演义-西游记-"
}
private val textBound by lazy {
val textBound = Rect()
projectResources.paint.getTextBounds(TEXT, 0, TEXT.length, textBound)
textBound
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawText(TEXT,//:要绘制的文字
width/2f,//绘制原点x坐标
height/2f - textBound.exactCenterY(),//绘制原点y坐标 基线的位置。
projectResources.paint)
}
}
🔺可换行的文字🔺
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
companion object {
private const val TEXT = "Too long a line to display in one line, and let it auto wrap go to next line.\nCan handle \"\\n\" as well."
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
val staticLayout =
StaticLayout.Builder.obtain(
TEXT, 0, TEXT.length, projectResources.textPaint, width
).build()
canvas.save()
canvas.translate(width / 2f, (height - staticLayout.height)/2f)
staticLayout.draw(canvas)
canvas.restore()
}
}
🔺自定义每个文字的位置🔺
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
companion object {
private const val TEXT = "Testing 123"
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawPosText(TEXT.toCharArray(),//:要绘制的文字数组
1, //第一个要绘制的文字的索引
3,//要绘制的文字的个数,用来算最后一个文字的位置,从第一个绘制的文字开始算起
listOf(width/1.5f, height/1.5f,
width/2f, height/2f,
width/3f, height/3f).toFloatArray(),//每个字体的位置,同样两两一组,
projectResources.paint)
}
}
🔺绘制文字——沿着路线走🔺
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
companion object {
private const val TEXT = "Test 123"
}
private val path by lazy {
val path = Path()
path.moveTo(0f + paddingStart, 0f + paddingTop)
path.lineTo((width/2f), (height - paddingBottom).toFloat())
path.lineTo((width - paddingEnd).toFloat(), 0f + paddingTop)
path
}
private val textBound by lazy {
val textBound = Rect()
projectResources.paint.getTextBounds(TEXT, 0, TEXT.length, textBound)
textBound
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawPath(path, projectResources.paintLight)
canvas.drawTextOnPath(TEXT, //文字
path,//路径 轨迹
0f,//与路径起始点的水平偏移距离
-textBound.exactCenterY(),//与路径中心的垂直偏移量
projectResources.paint)
}
}
🔺将图片直接绘制到canvas🔺
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
private val bitmap by lazy {
BitmapFactory.decodeResource(resources, R.drawable.image)
}
private val rect by lazy {
Rect(0 + paddingLeft, 0 + paddingTop, width - paddingRight, height - paddingBottom)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawBitmap(bitmap, null, rect, null)
}
}
🔺用颜色充填canvas🔺
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawColor(context.getColor(R.color.colorPrimary))
}
}
🔺用RGB颜色充填🔺
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawRGB(255, 0 , 0)
}
}
🔺图片和颜色重合🔺
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
private val bitmap by lazy {
BitmapFactory.decodeResource(resources, R.drawable.image)
}
private val rect by lazy {
Rect(0 + paddingLeft, 0 + paddingTop, width - paddingRight, height - paddingBottom)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawBitmap(bitmap, null, rect, null)
canvas.drawARGB(128, 255, 0 , 0)
}
}
🔺用pain笔的颜色去填充整个画布🔺
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
companion object {
private const val TEXT = "Testing 123"
}
private val gradientPaint by lazy {
Paint().apply {
shader = RadialGradient(
width/2f,
height/2f,
height/2f,
Color.GREEN,
Color.RED,
Shader.TileMode.MIRROR
)
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawPaint(gradientPaint)
}
}
🔺图像形变🔺
通过对顶点操作可以使图像形变,drawBitmapMesh只对绘制的Bitmap作用
class DrawBitmapMeshView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
private val bitmap by lazy {
BitmapFactory.decodeResource(resources, R.drawable.santou)
}
private val firstX by lazy { 0f + paddingLeft }
private val firstY by lazy { 0f + paddingTop }
private val secondX by lazy { width/5f }
private val secondY by lazy { height/3f }
private val thirdX by lazy { width.toFloat() - paddingRight }
private val thirdY by lazy { height.toFloat() - paddingBottom }
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawBitmapMesh(bitmap, 2, 2,
floatArrayOf(
firstX, firstY, secondX, firstY, thirdX, firstY,
firstX, secondY, secondX, secondY, thirdX, secondY,
firstX, thirdY, secondX, thirdY, thirdX, thirdY),
0, null, 0, null)
canvas.drawLine(firstX, firstY, firstX, thirdY, projectResources.paintLight)
canvas.drawLine(secondX, firstY, secondX, thirdY, projectResources.paintLight)
canvas.drawLine(thirdX, firstY, thirdX, thirdY, projectResources.paintLight)
canvas.drawLine(firstX, firstY, thirdX, firstY, projectResources.paintLight)
canvas.drawLine(firstX, secondY, thirdX, secondY, projectResources.paintLight)
canvas.drawLine(firstX, thirdY, thirdX, thirdY, projectResources.paintLight)
}
}
对图片进行放大切割,及对应点可以给各个顶点颜色纹理渲染
class DrawVerticesView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
companion object {
const val DUMMYCOLOR = -0x100000
}
val paint = Paint().apply {
val bitmap = BitmapFactory.decodeResource(
resources, R.drawable.santou)
val shader = BitmapShader(
bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
this.shader = shader
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
setLayerType(View.LAYER_TYPE_SOFTWARE, null)
val indices = shortArrayOf(0, 1, 2, 0, 2, 3)
val verticesColors = intArrayOf(
Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW,
DUMMYCOLOR, DUMMYCOLOR, DUMMYCOLOR, DUMMYCOLOR, DUMMYCOLOR, DUMMYCOLOR)
val verts = floatArrayOf(width/2f, 0f, 0f, height/2f, width/2f, height.toFloat(), width.toFloat(), height/2f)
canvas.drawVertices(
Canvas.VertexMode.TRIANGLES,//顶点类型 比如他是三角形(连续3个顶点)或者 四边形 (连续4个顶点)等等
verts.size,//顶点数 总共有多少个顶点绘制。
verts, //顶点数组 [0,0,0,1,1,0,...] 前面有xy 3组 如果是类型是三角形 他就构成一个三角形的绘制基元,往后类推。
0,//顶点数据 起始位置 可能全部绘制,也可能只绘制部分顶点。与 vertexCount 配置使用 一般为0
verts,// 纹理数组 就是对图片等进行采样,然后去渲染顶点。
0,//同上offset 就是偏移量
verticesColors, // 颜色数组 直接用颜色渲染顶点
0,//同上offset 就是偏移量
indices,// 顶点索引 可能只绘制部分顶点 这个就是存放那些顶点的index , 即verts[index]
0,// 同上offset 就是偏移量
indices.size,//绘制多少个索引点。
paint
)
}
}
对图片进行缩小和放大,位移等等操作
class DrawPictureView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
private val bitmap by lazy {
BitmapFactory.decodeResource(resources, R.drawable.santou)
}
private val rect by lazy {//将高减为一般半,绘制出的Picture在y轴方向压缩了一半,也就是rect并不是对图形进行裁剪,而是缩小或放大,
Rect(0 + paddingLeft, 0 + paddingTop, width - paddingRight, (height - paddingBottom)/2)
}
private val rect1 by lazy {//看到绘制的形状未变,只不过在y轴上上移了200px,
Rect(0 + paddingLeft, 200, width - paddingRight, 200+height - paddingBottom)
}
private val picture by lazy {
val picture = Picture()
val pCanvas = picture.beginRecording(width, height)//开始录制,在返回的Canvas上进行绘制
pCanvas.drawBitmap(bitmap, null, rect, null)
picture.endRecording()//结束录制
picture
}
private val picture1 by lazy {
val picture = Picture()
val pCanvas = picture.beginRecording(width, height)//开始录制,在返回的Canvas上进行绘制
pCanvas.drawBitmap(bitmap, null, rect1, null)
picture.endRecording()//结束录制
picture
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (width == 0 || height == 0) return
canvas.drawPicture(picture)
canvas.drawPicture(picture1)
}
}
private static Bitmap getImage(String srcPath) {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
newOpts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此时返回bm为空
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
// 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
float hh = 800f;// 这里设置高度为800f
float ww = 480f;// 这里设置宽度为480f
// 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;// be=1表示不缩放
if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放
be = (int) (newOpts.outHeight / hh);
}
if (be <= 0)
be = 1;
newOpts.inSampleSize = be;// 设置缩放比例
// 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩
}`
* 质量压缩方法
*
* @param image
* @return
*/
private static Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 100;
while (baos.toByteArray().length / 1024 > 100) {
baos.reset();
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
options -= 10;
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
return bitmap;
}
* 将压缩的bitmap保存到SDCard卡临时文件夹,用于上传
*
* @param filename
* @param bit
* @return
*/
private static String saveMyBitmap(String filename, Bitmap bit) {
String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/chdd/";
String filePath = baseDir + filename;
File dir = new File(baseDir);
if (!dir.exists()) {
dir.mkdir();
}
File f = new File(filePath);
try {
f.createNewFile();
FileOutputStream fOut = null;
fOut = new FileOutputStream(f);
bit.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return filePath;
}
* 清除缓存文件
*/
public static void deleteCacheFile() {
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/chdd/");
RecursionDeleteFile(file);
}
/**
* 递归删除
*/
private static void RecursionDeleteFile(File file) {
if (file.isFile()) {
file.delete();
return;
}
if (file.isDirectory()) {
File[] childFile = file.listFiles();
if (childFile == null || childFile.length == 0) {
file.delete();
return;
}
for (File f : childFile) {
RecursionDeleteFile(f);
}
file.delete();
}
}
android:id="@+id/ivAvatar"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/space_20"
android:src="@drawable/personal_t" />
private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
private static final int COLORDRAWABLE_DIMENSION = 2;
private static final int DEFAULT_BORDER_WIDTH = 0;
private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
private static final boolean DEFAULT_BORDER_OVERLAY = false;
private final RectF mDrawableRect = new RectF();
private final RectF mBorderRect = new RectF();
private final Matrix mShaderMatrix = new Matrix();
private final Paint mBitmapPaint = new Paint();
private final Paint mBorderPaint = new Paint();
private final Paint mFillPaint = new Paint();
private int mBorderColor = DEFAULT_BORDER_COLOR;
private int mBorderWidth = DEFAULT_BORDER_WIDTH;
private int mFillColor = DEFAULT_FILL_COLOR;
private Bitmap mBitmap;
private BitmapShader mBitmapShader;
private int mBitmapWidth;
private int mBitmapHeight;
private float mDrawableRadius;
private float mBorderRadius;
private ColorFilter mColorFilter;
private boolean mReady;
private boolean mSetupPending;
private boolean mBorderOverlay;
public RoundImageView(Context context) {
super(context);
init();
}
public RoundImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);
mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);
a.recycle();
init();
}
private void init() {
super.setScaleType(SCALE_TYPE);
mReady = true;
if (mSetupPending) {
setup();
mSetupPending = false;
}
}
@Override
public ScaleType getScaleType() {
return SCALE_TYPE;
}
@Override
public void setScaleType(ScaleType scaleType) {
if (scaleType != SCALE_TYPE) {
throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
}
}
@Override
public void setAdjustViewBounds(boolean adjustViewBounds) {
if (adjustViewBounds) {
throw new IllegalArgumentException("adjustViewBounds not supported.");
}
}
@Override
protected void onDraw(Canvas canvas) {
if (mBitmap == null) {
return;
}
if (mFillColor != Color.TRANSPARENT) {
canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mFillPaint);
}
canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mBitmapPaint);
if (mBorderWidth != 0) {
canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mBorderRadius, mBorderPaint);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
setup();
}
public int getBorderColor() {
return mBorderColor;
}
public void setBorderColor(int borderColor) {
if (borderColor == mBorderColor) {
return;
}
mBorderColor = borderColor;
mBorderPaint.setColor(mBorderColor);
invalidate();
}
public void setBorderColorResource(@ColorRes int borderColorRes) {
setBorderColor(ContextCompat.getColor(getContext(), borderColorRes));
}
public int getFillColor() {
return mFillColor;
}
public void setFillColor(int fillColor) {
if (fillColor == mFillColor) {
return;
}
mFillColor = fillColor;
mFillPaint.setColor(fillColor);
invalidate();
}
public void setFillColorResource(@ColorRes int fillColorRes) {
setFillColor(ContextCompat.getColor(getContext(), fillColorRes));
}
public int getBorderWidth() {
return mBorderWidth;
}
public void setBorderWidth(int borderWidth) {
if (borderWidth == mBorderWidth) {
return;
}
mBorderWidth = borderWidth;
setup();
}
public boolean isBorderOverlay() {
return mBorderOverlay;
}
public void setBorderOverlay(boolean borderOverlay) {
if (borderOverlay == mBorderOverlay) {
return;
}
mBorderOverlay = borderOverlay;
setup();
}
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
mBitmap = bm;
setup();
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
mBitmap = getBitmapFromDrawable(drawable);
setup();
}
@Override
public void setImageResource(@DrawableRes int resId) {
super.setImageResource(resId);
mBitmap = getBitmapFromDrawable(getDrawable());
setup();
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
mBitmap = uri != null ? getBitmapFromDrawable(getDrawable()) : null;
setup();
}
@Override
public void setColorFilter(ColorFilter cf) {
if (cf == mColorFilter) {
return;
}
mColorFilter = cf;
mBitmapPaint.setColorFilter(mColorFilter);
invalidate();
}
private Bitmap getBitmapFromDrawable(Drawable drawable) {
if (drawable == null) {
return null;
}
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
try {
Bitmap bitmap;
if (drawable instanceof ColorDrawable) {
bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private void setup() {
if (!mReady) {
mSetupPending = true;
return;
}
if (getWidth() == 0 && getHeight() == 0) {
return;
}
if (mBitmap == null) {
invalidate();
return;
}
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mBitmapPaint.setAntiAlias(true);
mBitmapPaint.setShader(mBitmapShader);
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setAntiAlias(true);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStrokeWidth(mBorderWidth);
mFillPaint.setStyle(Paint.Style.FILL);
mFillPaint.setAntiAlias(true);
mFillPaint.setColor(mFillColor);
mBitmapHeight = mBitmap.getHeight();
mBitmapWidth = mBitmap.getWidth();
mBorderRect.set(0, 0, getWidth(), getHeight());
mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
mDrawableRect.set(mBorderRect);
if (!mBorderOverlay) {
mDrawableRect.inset(mBorderWidth, mBorderWidth);
}
mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
updateShaderMatrix();
invalidate();
}
private void updateShaderMatrix() {
float scale;
float dx = 0;
float dy = 0;
mShaderMatrix.set(null);
if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
scale = mDrawableRect.height() / (float) mBitmapHeight;
dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
} else {
scale = mDrawableRect.width() / (float) mBitmapWidth;
dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
}
mShaderMatrix.setScale(scale, scale);
mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
mBitmapShader.setLocalMatrix(mShaderMatrix);
}
}`
也许你还想看
(▼点击文章标题或封面查看)
加入搜狐技术作者天团
千元稿费等你来!
戳这里!☛