佰聯(lián)軸承網(wǎng)做的網(wǎng)站網(wǎng)站seo優(yōu)化培訓(xùn)
一、背景及樣式效果? ? ??
因項目需要,需要文本編輯時,支持項目符號(無序列表)嘗試了BulletSpan,但不是很理想,并且考慮到影響老版本回顯等因素,最終決定自定義一個BulletEditText。
? ? ? ? 先看效果:
????????
視頻效果
二、自定義View BulletEditText? ? ? ??
????????自定義控件BulletEditText源碼:
package com.ml512.widgetimport android.content.Context
import android.util.AttributeSet
import androidx.core.widget.doOnTextChanged/*** @Description: 簡單支持項目號的文本編輯器* @Author: Marlon* @CreateDate: 2024/2/1 17:44* @UpdateRemark: 更新說明:* @Version: 1.0*/
class BulletEditText : androidx.appcompat.widget.AppCompatEditText {/*** 是否開啟項目符號*/private var isNeedBullet: Boolean = false/*** 項目符號*/private var bulletPoint: String = "? "/*** 項目符號占用字符數(shù),方便設(shè)置光標(biāo)位置*/private var bulletOffsetIndex = bulletPoint.length/*** 相關(guān)監(jiān)聽回調(diào)*/private var editListener: EditListener? = nullconstructor(context: Context) : super(context)constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context,attrs,defStyleAttr)init {this.doOnTextChanged { text, start, before, count ->//如果是關(guān)閉狀態(tài)不做格式處理if (!isNeedBullet) {return@doOnTextChanged}if (count > before) {//處理項目號邏輯var offset = 0var tmp = text.toString()//連續(xù)回車去掉項目符號if (start >= bulletOffsetIndex && tmp.substring(start, start + count) == "\n") {val preSub = tmp.substring(start - bulletOffsetIndex, start)if (preSub == bulletPoint) {changeBulletState(false)tmp = tmp.replaceRange(start-bulletOffsetIndex, start + count, "")offset -= bulletOffsetIndex + 1setTextAndSelection(tmp, start + count + offset)return@doOnTextChanged}}//加入項目符號if (tmp.substring(start, start + count) == "\n") {changeBulletState(true)tmp = tmp.replaceRange(start, start + count, "\n$bulletPoint")offset += bulletOffsetIndexsetTextAndSelection(tmp, start + count + offset)}}}}override fun onSelectionChanged(selStart: Int, selEnd: Int) {super.onSelectionChanged(selStart, selEnd)//復(fù)制選擇時直接返回,關(guān)閉項目符號if (selStart != selEnd) {changeBulletState(false)return}//判斷當(dāng)前段落是否有項目號,有開啟,沒有關(guān)閉val tmp = text.toString()val prefix = tmp.substring(0, selectionStart)if (prefix.isEmpty()) {changeBulletState(false)return}if (prefix.startsWith(bulletPoint) && !prefix.contains("\n")) {changeBulletState(true)return}val lastEnterIndex = prefix.lastIndexOf("\n")if (lastEnterIndex != -1 && lastEnterIndex + bulletOffsetIndex + 1 <= prefix.length) {val mathStr = prefix.substring(lastEnterIndex, lastEnterIndex + bulletOffsetIndex + 1)if (mathStr == "\n$bulletPoint") {changeBulletState(true)return}}changeBulletState(false)}/*** 更新bullet狀態(tài)*/private fun changeBulletState(isOpen: Boolean) {isNeedBullet = isOpeneditListener?.onBulletStateChange(isOpen)}/*** 設(shè)置是否開啟項目號*/fun setBullet(isOpen: Boolean) {isNeedBullet = isOpenval tmp = text.toString()var index = selectionStartvar prefix = tmp.substring(0, index)val suffix = tmp.substring(index)//加項目號if (isOpen) {//首個段落if (!prefix.contains("\n") && prefix.startsWith(bulletPoint)) {return}index += bulletOffsetIndexif (prefix.isEmpty() || (!prefix.contains("\n") && !prefix.startsWith(bulletPoint))) {setTextAndSelection("$bulletPoint$prefix$suffix", index)return}prefix = prefix.replaceLast("\n", "\n$bulletPoint")setTextAndSelection("$prefix$suffix", index)return}//去掉項目號if (prefix.startsWith(bulletPoint) && !prefix.contains("\n$bulletPoint")) {//首行邏輯index -= bulletOffsetIndexprefix = prefix.replaceLast(bulletPoint, "")setTextAndSelection("$prefix$suffix", index)return}if (prefix.contains("\n$bulletPoint")) {index -= bulletOffsetIndexprefix = prefix.replaceLast("\n$bulletPoint", "\n")setTextAndSelection("$prefix$suffix", index)}}/*** 設(shè)置文本及光標(biāo)位置*/private fun setTextAndSelection(text: String, index: Int) {setText(text)setSelection(index)}/*** 替換最后一個字符*/private fun String.replaceLast(oldValue: String, newValue: String): String {val lastIndex = lastIndexOf(oldValue)if (lastIndex == -1) {return this}val prefix = substring(0, lastIndex)val suffix = substring(lastIndex + oldValue.length)return "$prefix$newValue$suffix"}/*** 設(shè)置監(jiān)聽*/fun setEditListener(listener: EditListener) {editListener = listener}/*** 監(jiān)聽回調(diào)*/interface EditListener {/*** 項目符號開關(guān)狀態(tài)變化*/fun onBulletStateChange(isOpen: Boolean)}
}
三、調(diào)用
????????使用時一個項目符號的按鈕開關(guān)設(shè)置調(diào)用setBullet(isOpen: Boolean)?設(shè)置是否開啟項目符號,同時實現(xiàn)一個setEditListener(listener: EditListener)根據(jù)光標(biāo)位置判斷當(dāng)前段落是否含有項目符號,并回顯按鈕狀態(tài)。
<com.ml512.widget.BulletEditTextandroid:id="@+id/etInput"android:layout_width="match_parent"android:layout_height="200dp"android:layout_below="@+id/tvTitle"android:layout_marginStart="15dp"android:layout_marginTop="15dp"android:layout_marginEnd="15dp"android:layout_marginBottom="15dp"android:autofillHints="no"android:background="@drawable/shape_edit_bg"android:gravity="top"android:hint="@string/text_please_input_some_worlds"android:inputType="textMultiLine"android:padding="15dp"android:textColor="@color/black"android:textColorHint="@color/color_FF_999999"android:textSize="16sp" />
//點擊按鈕設(shè)置添加/取消項目符號tvBullet.setOnClickListener {tvBullet.isSelected = !tvBullet.isSelectedetInput.setBullet(tvBullet.isSelected)}//項目符號狀態(tài)監(jiān)聽,回顯到按鈕etInput.setEditListener(object :BulletEditText.EditListener{override fun onBulletStateChange(isOpen: Boolean) {tvBullet.isSelected = isOpen}})
大功告成!