-
Notifications
You must be signed in to change notification settings - Fork 0
/
SobAvatarView.kt
298 lines (272 loc) · 9.68 KB
/
SobAvatarView.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
package com.coder.zt.sobblog.ui.view
import android.content.Context
import android.graphics.*
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.Build
import android.util.AttributeSet
import android.util.Log
import android.view.View
import androidx.annotation.RequiresApi
import com.coder.zt.sobblog.R
import com.luck.picture.lib.tools.ScreenUtils
import kotlin.math.min
import kotlin.math.sqrt
/**
* 自定义View-用户头像
*/
@RequiresApi(Build.VERSION_CODES.M)
class SobAvatarView(context: Context, attrs: AttributeSet): androidx.appcompat.widget.AppCompatImageView(context, attrs) {
companion object{
private const val TAG = "SobAvatarView"
}
/**
* 默认属性
*/
private var isCircle:Boolean = true
private var isVip:Boolean = false
private var defaultBitmap: Bitmap = BitmapFactory.decodeResource(resources, R.mipmap.tab_profile_normal)
private var borderWidth = ScreenUtils.dip2px(context, 3f)
private val defBorderWidth = ScreenUtils.dip2px(context, 4f)
private var viewHeight = 0
private var viewWidth = 0
private val sqrt2 = sqrt(2.0)
private var smallR:Float = 0f
private var avatarBitmap:Bitmap = drawableToBitmap(drawable)
private lateinit var circleBitmap:Bitmap
private lateinit var roundBitmap:Bitmap
private lateinit var vipIconRect:RectF
private val paint:Paint by lazy{
val p = Paint()
p.color = resources.getColor(R.color.sob_vip_color, null)
p
}
private val path:Path by lazy{
val p = Path()
val leftX = vipIconRect.left + vipIconRect.width() * 0.3f
val upY = vipIconRect.top + vipIconRect.height() * 0.3f
val rightX = vipIconRect.right - vipIconRect.height() * 0.3f
val downX = vipIconRect.left + vipIconRect.height() * 0.5f
val downY = vipIconRect.bottom - vipIconRect.height() * 0.3f
p.moveTo(leftX, upY)//左上起始点
p.lineTo(downX, downY)//下中点
p.lineTo(rightX, upY)//右上末尾点
p
}
init {
val obtainStyledAttributes = context.obtainStyledAttributes(attrs, R.styleable.SobAvatarView)
isCircle = obtainStyledAttributes.getBoolean(R.styleable.SobAvatarView_isCircle, isCircle)
isVip = obtainStyledAttributes.getBoolean(R.styleable.SobAvatarView_isVip, isVip)
}
override fun onDraw(canvas: Canvas?) {
canvas?.let {
if(viewHeight == 0 || viewWidth == 0){
//计算相关属性的值
calcValues()
}
if(isCircle){
drawCircleStyle(it)
}else{
drawRoundStyle(it)
}
//画字母V
drawLetterV(it)
}
if (canvas == null) {
super.draw(canvas)
}
}
/**
* darwable转为bitmap
*/
private fun drawableToBitmap(drawable: Drawable?): Bitmap {
if(drawable == null){
return defaultBitmap
}else if(drawable is BitmapDrawable){
return drawable.bitmap
}
val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0,0,canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
/**
* 画圆角矩形图像
*/
private fun drawRoundStyle(it: Canvas) {
//画圆角矩形背景
val roundR = ScreenUtils.dip2px(context, 8f).toFloat()
if (isVip) {
val backgroundRect = RectF(0.0f, 0.0f, viewWidth.toFloat(), viewHeight.toFloat())
it.drawRoundRect(backgroundRect, roundR, roundR, paint)
}
//画用户头像
it.drawBitmap(roundBitmap, paddingLeft.toFloat(), paddingTop.toFloat(), paint)
if (isVip) {
//画VIP图片的矩形背景
paint.color = resources.getColor(R.color.sob_vip_color, null)
val ratio = 0.67f
it.drawRoundRect(
vipIconRect,
roundR * ratio * 0.8f,
roundR * ratio * 0.8f,
paint
)
}
}
/**
* 画圆形头像
*/
private fun drawCircleStyle(it: Canvas) {
//画圆形背景
if (isVip) {
it.drawCircle(
viewWidth / 2.0f,
viewHeight / 2.0f,
min(viewWidth, viewHeight) / 2.0f,
paint
)
}
//画圆形头像图片
it.drawBitmap(circleBitmap, paddingLeft.toFloat(), paddingTop.toFloat(), paint)
//画圆形VIP背景
if (isVip) {
it.drawCircle(viewWidth - smallR, viewHeight - smallR, smallR, paint)
}
}
/**
* 画图案V
*/
private fun drawLetterV(it: Canvas) {
if (isVip) {
paint.color = Color.WHITE
paint.style = Paint.Style.STROKE
paint.strokeCap = Paint.Cap.ROUND
paint.strokeWidth = viewWidth / 30.0f
it.drawPath(path, paint)
paint.style = Paint.Style.FILL
paint.color = resources.getColor(R.color.sob_vip_color, null)
}
}
/**
* 计算相关参数
*/
private fun calcValues() {
//计算控件的宽高
viewHeight = height - paddingBottom - paddingTop
viewWidth = width - paddingStart - paddingEnd
//VIP broad宽度
borderWidth =
(defBorderWidth * 1.0 * (viewWidth * 1.0 / ScreenUtils.dip2px(context, 60f))).toInt()
//VIP图标半径
smallR = viewWidth / 2.0f - ((viewWidth - borderWidth) / 2.0f) * (sqrt2 / 2.0f).toFloat()
//将设置的图片转化为控件大小的Bitmap
avatarBitmap?.let{
val resSizeBitmap = resizeBitmap(it, viewWidth, viewHeight)
if (isCircle) {
circleBitmap = createCircleImage(resSizeBitmap, viewWidth, viewHeight)
} else {
roundBitmap = createRoundImage(resSizeBitmap, viewWidth, viewHeight)
}
}
vipIconRect = RectF(
viewWidth - 2 * smallR, viewWidth - 2 * smallR,
viewWidth.toFloat(), viewHeight.toFloat()
)
}
/**
* 创建圆角矩形图片
*/
private fun createRoundImage(bitmap: Bitmap, width: Int, height: Int): Bitmap {
paint.isAntiAlias = true
val target = Bitmap.createBitmap(bitmap.width, bitmap.width, Bitmap.Config.ARGB_8888)
val canvas = Canvas(target)
val min = Math.min(bitmap.width, bitmap.height)
val dstRect = Rect(0, 0, width, height)
val srcRect = Rect(0, 0, min, min)
val drawBorderWidth = if(isVip){
borderWidth
}else{
0
}
val backgroundRect = RectF(0.0f + drawBorderWidth,0.0f + drawBorderWidth, width.toFloat() - drawBorderWidth, height.toFloat() - drawBorderWidth)
val roundR = ScreenUtils.dip2px(context, 8f).toFloat()
canvas.drawRoundRect(backgroundRect, roundR - borderWidth, roundR - borderWidth, paint)
// 核心代码取两个图片的交集部分
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(bitmap, srcRect, dstRect, paint)
paint.xfermode = null
return target
}
/**
* 将bitmap改变其他大小
*/
private fun resizeBitmap(bitmap: Bitmap, newWidth:Int, newHeight:Int):Bitmap{
val width = bitmap.width
val height = bitmap.height
val x = (newWidth - width) / 2
val y = (newHeight - height) / 2
if (x > 0 && y > 0) {
return Bitmap.createBitmap(bitmap, 0, 0, width, height, null, true)
}
var scale = 1f
scale = if (width > height) {
// 按照宽度进行等比缩放
newWidth.toFloat() / width
} else {
// 按照高度进行等比缩放
// 计算出缩放比
newHeight.toFloat() / height
}
val matrix = Matrix()
matrix.postScale(scale, scale)
return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true)
}
/**
* 创建圆形图片
*/
private fun createCircleImage(bitmap: Bitmap, width:Int, height:Int):Bitmap {
paint.isAntiAlias = true
val target = Bitmap.createBitmap(bitmap.width, bitmap.width, Bitmap.Config.ARGB_8888)
val canvas = Canvas(target)
val min = Math.min(bitmap.width, bitmap.height)
val dstRect = Rect(0, 0, width, height)
val srcRect = Rect(0, 0, min, min)
val drawBorderWidth = if(isVip){
borderWidth
}else{
0
}
canvas.drawCircle((width / 2f),(height / 2f), (Math.min(width, height) / 2f) - drawBorderWidth, paint)
// 核心代码取两个图片的交集部分
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(bitmap, srcRect, dstRect, paint)
paint.xfermode = null
return target
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val width = MeasureSpec.getSize(widthMeasureSpec)
val height = MeasureSpec.getSize(heightMeasureSpec)
setMeasuredDimension(width, height)
}
fun isVip(vip:Boolean){
postDelayed({
isVip = vip
viewHeight = 0
viewWidth = 0
postInvalidate()
},200)
}
/**
* 提供给外部设置头像后用来刷新数据的接口
*/
fun update(bitmap: Bitmap) {
//参数置为0,重新计算参数
viewHeight = 0
viewWidth = 0
avatarBitmap = bitmap
postInvalidate()
}
}