云音乐-视频合辑

预览

实现方式

视频合辑这个Item上面的遮罩层一直没找到很好的方法,所以有自己撸了一个库来支持,特此也写一章

PASS找实现快捷方法的心酸历程。。。

仔细细扫网易云App,发现它的这层模糊效果是直接模糊了图片叠加在上面的,想到的就是通过google RenderScript来实现

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
/**
* http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0816/6543.html
* https://developer.android.com/guide/topics/renderscript/compute.html
*/

/**
* 图片缩放比例
*/
private const val BITMAP_SCALE = 0.4f

/**
* 最大模糊度(在0.0到25.0之间)
*/
private const val BLUR_RADIUS = 25f

/**
* 模糊图片的具体方法
*
* @param image 需要模糊的图片
* @return 模糊处理后的图片
*/
fun Context.blur(image: Bitmap): Bitmap {
// 计算图片缩小后的长宽
val width = (image.width * BITMAP_SCALE).roundToInt()
val height = (image.height * BITMAP_SCALE).roundToInt()

// 将缩小后的图片做为预渲染的图片。
val inputBitmap = Bitmap.createScaledBitmap(image, width, height, false)
// 创建一张渲染后的输出图片。
val outputBitmap = Bitmap.createBitmap(inputBitmap)

// 创建RenderScript内核对象
val rs = RenderScript.create(this)
// 创建一个模糊效果的RenderScript的工具对象
val blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs))

// 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间。
// 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去。
val tmpIn = Allocation.createFromBitmap(rs, inputBitmap)
val tmpOut = Allocation.createFromBitmap(rs, outputBitmap)

// 设置渲染的模糊程度, 25f是最大模糊度
blurScript.setRadius(BLUR_RADIUS)
// 设置blurScript对象的输入内存
blurScript.setInput(tmpIn)
// 将输出数据保存到输出内存中
blurScript.forEach(tmpOut)

// 将数据填充到Allocation中
tmpOut.copyTo(outputBitmap)

return outputBitmap
}

中间镂空状态是通过Canvas.clipPath()剪切来达到效果

1
2
3
4
5
6
7
8
//画遮罩的颜色
blurBitmap?.let {
canvas.drawBitmap(it, null, bounds, null)
} ?: canvas.drawColor(defaultCoverColor)
//按Path来裁切
canvas.clipPath(clipPath)
//画镂空的范围
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC)

四边上的装饰边线也是通过Path()描绘四边绘制实现

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
val rectF = RectF()
// 设置左上角
rectF.set(
paddingStartEnd,
paddingTopBottom,
paddingStartEnd + roundCorner * 2,
paddingTopBottom + roundCorner * 2
)
strokePath.reset()
strokePath.arcTo(rectF, 180f, 90f, true)
// 右上角
rectF.set(
width - paddingStartEnd - roundCorner * 2,
paddingTopBottom,
width - paddingStartEnd,
paddingTopBottom + roundCorner * 2
)
strokePath.arcTo(rectF, 270f, 90f, true)
// 右下角
rectF.set(
width - paddingStartEnd - roundCorner * 2,
height - paddingTopBottom - roundCorner * 2,
width - paddingStartEnd,
height - paddingTopBottom
)
strokePath.arcTo(rectF, 0f, 90f, true)
// 左下角
rectF.set(
paddingStartEnd,
height - paddingTopBottom - roundCorner * 2,
paddingStartEnd + roundCorner * 2,
height - paddingTopBottom
)
strokePath.arcTo(rectF, 90f, 90f, true)

通过实现这些的前奏,再稍加修饰调整属性,最终得到想要的效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<com.xuie0000.hollowout.drawable.HollowOutShapeableImageView
android:id="@+id/ivImage"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:background="@color/white80"
android:scaleType="centerCrop"
app:hollow_out_padding_horizontal="4dp"
app:hollow_out_padding_vertical="32dp"
app:hollow_out_round_corner="8dp"
app:hollow_out_round_corner_color="@color/white"
app:hollow_out_round_corner_stroke="2dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:shapeAppearanceOverlay="@style/roundedCornerStyle"
tools:src="@mipmap/ic_netease" />

参考

https://github.com/xuie0000/hollowout-drawable
https://zhuanlan.zhihu.com/p/329825945
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0816/6543.html
https://developer.android.com/guide/topics/renderscript/compute.html

文章作者: 二十I邊界
文章链接: https://xuie0000.com/post/2021-06-18-netease-10-hollowout-drawable.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 二十I邊界