Android 实现微信读书划线的效果

Android 实现微信读书划线的效果

技术博客 admin 124 浏览

最近遇到过一个实现类似微信读书的划线效果的需求。如下图所示,可以看到,微信读书划线支持涂抹、直线以及波浪线三种效果。

对于涂抹效果可以使用 BackgroundColorSpan实现,代码示例如下:

kotlin
代码解读
复制代码
val content = SpannableStringBuilder(textView.text) content.setSpan(BackgroundColorSpan(Color.RED), 0, content.length / 2, Spannable.SPAN_INCLUSIVE_EXCLUSIVE) textView.text = content

效果如下图所示:

对于直线划线的效果则可以通过 UnderlineSpan 来实现,代码如下所示:

kotlin
代码解读
复制代码
val content = SpannableStringBuilder(textView.text) content.setSpan(UnderlineSpan(), 0, content.length / 2, Spannable.SPAN_INCLUSIVE_EXCLUSIVE) textView.text = content

效果如下图所示:

如果你需要设置下划线的颜色和粗细,则需要自定义 UnderlineSpan,代码示例如下:

kotlin
代码解读
复制代码
class CustomUnderLine(val color: Int, val underlineThickness: Float): UnderlineSpan() { @RequiresApi(Build.VERSION_CODES.Q) override fun updateDrawState(ds: TextPaint) { ds.underlineColor = color // 下划线的颜色 ds.underlineThickness = underlineThickness // 下划线的粗细 super.updateDrawState(ds) } }

效果如下所示:

但是对于绘制波浪线,Android 没有没有提供直接的接口来实现。这时我们可以通过 LineBackgroundSpan 来间接实现波浪线的效果。

kotlin
代码解读
复制代码
class Standard implements LineBackgroundSpan, ParcelableSpan { // 存储背景颜色的变量 private final int mColor; // 构造方法,接受一个颜色整数值作为参数,用于定义背景颜色 public Standard(@ColorInt int color) { mColor = color; } // 从包裹中创建 LineBackgroundSpan.Standard 对象的构造方法 public Standard(@NonNull Parcel src) { mColor = src.readInt(); } @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } /** @hide */ @Override public int getSpanTypeIdInternal() { return TextUtils.LINE_BACKGROUND_SPAN; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ @Override public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeInt(mColor); } /** * 获取该 span 的颜色 * @return 颜色整数值 */ @ColorInt public final int getColor() { return mColor; } // 绘制背景的方法,在画布上绘制指定颜色的矩形作为行背景 // left:该行相对于输入画布的左边界位置,以像素为单位。 // right:该行相对于输入画布的右边界位置,以像素为单位。 // top:该行相对于输入画布的上边界位置,以像素为单位。 // baseline:该行文本的基线相对于输入画布的位置,以像素为单位。 // bottom:该行相对于输入画布的下边界位置,以像素为单位。 // text:当前的文本内容。 // start:该行文本在整个文本中的起始字符索引。 // end:该行文本在整个文本中的结束字符索引。 // lineNumber:在当前文本布局中的行号。 @Override public void drawBackground(@NonNull Canvas canvas, @NonNull Paint paint, @Px int left, @Px int right, @Px int top, @Px int baseline, @Px int bottom, @NonNull CharSequence text, int start, int end, int lineNumber) { final int originColor = paint.getColor(); paint.setColor(mColor); canvas.drawRect(left, top, right, bottom, paint); paint.setColor(originColor); } }

如上的源码所示,LineBackgroundSpan 主要用于改变文本中的行的背景。LineBackgroundSpan 有一个实现LineBackgroundSpan.Standard,作用和 BackgroundColorSpan 都是改变文本的背景颜色,区别是LineBackgroundSpan 主要是用于改变文本中某一行或者某几行的背景。它在绘制背景时,考虑的是行的位置信息,如行的左右边界(leftright)、顶部和底部位置(topbottom)。简单说就是 LineBackgroundSpan 提供了更多行的信息,方便我们做更细致的处理。

代码示例如下:

kotlin
代码解读
复制代码
class WaveLineBackgroundSpan(val waveColor: Int) : LineBackgroundSpan { // 创建画笔用于绘制波浪线,初始化时设置颜色、样式和线宽 val wavePaint = Paint().apply { color = waveColor style = Paint.Style.STROKE strokeWidth = 6f } override fun drawBackground( canvas: Canvas, paint: Paint, @Px left: Int, @Px right: Int, @Px top: Int, @Px baseline: Int, @Px bottom: Int, text: CharSequence, start: Int, end: Int, lineNumber: Int ) { // 定义波浪线的振幅和波长,振幅决定波浪的高度,波长决定波浪的周期 val amplitude = 5 val wavelength = 15 // 获取要绘制波浪线的文本宽度 val width = paint.measureText(text.subSequence(start, end).toString()).toInt() // 遍历文本宽度范围内的每个点,计算并绘制波浪线上的点 for (x in left until (left + width)) { // 根据正弦函数计算每个点的 y 坐标,实现波浪效果 val y = (amplitude * Math.sin((x.toFloat() / wavelength).toDouble())).toInt() // 在画布上绘制波浪线上的点,确保 x 坐标不超过右边界 canvas.drawPoint(x.toFloat().coerceAtMost(right.toFloat()), (bottom + y).toFloat(), wavePaint) } } }

效果如下图所示:

参考

源文:Android 实现微信读书划线的效果

如有侵权请联系站点删除!

Technical cooperation service hotline, welcome to inquire!