扩展EditText-支持IconFont图标

2024-04-15 17:58

本文主要是介绍扩展EditText-支持IconFont图标,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

扩展EditText-支持IconFont图标


扩展EditText
自带清空按钮 显示/隐藏密码按钮(右图标)
支持IconFont图标

源码
/*** 创建者:  TomCat0916* 创建时间:  2020/10/25* 功能描述:  可配置显示/隐藏按钮(设置密码的显示隐藏) 或 配置清除按钮(清空输入框)  三者同时配置优先清空模式* 支持IconFont** 示例一:图片模式* ```*  <com.*.widget.ClearEditTextView2*      ...*     app:DrawableInvisible="@mipmap/icon_on"*     app:DrawableVisible="@mipmap/icon_off"/>* ```*示例二:IconFont模式* ```*  <com.*.ClearEditTextView2*      ...*      app:IconFontLeft="image"*      app:IconFontVisible="off"*      app:IconFontInvisible="on"/>* ```*  属性设置参考[package.R.styleable.ClearEditTextView2]*/
class ClearEditTextView2 @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = android.R.attr.editTextStyle
) : androidx.appcompat.widget.AppCompatEditText(context, attrs, defStyleAttr),OnFocusChangeListener, TextWatcher, LifecycleObserver {private var mClearDrawable: Drawable? = nullprivate var visibleDrawable: Drawable? = nullprivate var invisibleDrawable: Drawable? = nullprivate var onTouchListener: OnTouchListener? = nullprivate var translateAnimation: Animation? = nullprivate var invisibleText: CharSequence? = nullprivate var clearText: CharSequence? = nullprivate var leftIconText: CharSequence? = nullprivate var visibleText: CharSequence? = nullprivate var iconFontTextSize = 0private var iconFontTextColor = Color.BLACKprivate var iconFontBackgroundColor = Color.TRANSPARENTprivate var iconFontPath: String? = nullprivate var leftIconFontTextLayout: Layout? = nullprivate var rightIconFontTextLayout: Layout? = nullprivate var hasData = falseprivate var oldPaddingLeft = 0private var oldPaddingTop = 0private var oldPaddingRight = 0private var oldPaddingBottom = 0private var isLeftIconDraw = falseprivate var isRightIconDraw = falseprivate var iconLeftPadding = 0fprivate var iconRightPadding = 0fprivate var leftIconW = 0private var rightIconW = 0private var leftRecFBg: RectF? = nullprivate var rightRecFBg: RectF? = nullprivate var iconFontH = 0fprivate var iconFontDrawPadding = 0fprivate var lastVisibleState = trueprivate var lastClearShowState = falseprivate var maxRightTextW = 0private val textMap by lazy {HashMap<String, TextBean>()}private val iconFontPaint: TextPaint by lazy {TextPaint().apply {isAntiAlias = falsecolor = iconFontTextColortextSize = iconFontTextSize.toFloat()style = Paint.Style.FILL}}private val bgPaint: Paint by lazy {Paint().apply {isAntiAlias = falsecolor = iconFontBackgroundColorstyle = Paint.Style.FILL}}private companion object {private const val CLEAR_HIDE_MODEL = 0private const val CLEAR_SHOW_MODEL = 1private const val INVISIBLE_MODEL = 2private const val VISIBLE_MODEL = 3}private var rightIconModel = CLEAR_HIDE_MODEL//0清理按钮隐藏 1清理按钮显示 2 明文显示按钮 3明文关闭按钮@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)fun onDestroy() {onFocusChangeListener = nullremoveTextChangedListener(this)mClearDrawable = nullvisibleDrawable = nullinvisibleDrawable = nullonTouchListener = nullif (translateAnimation != null) {translateAnimation!!.cancel()translateAnimation = null}if (context is LifecycleOwner) {(context as LifecycleOwner).lifecycle.removeObserver(this)}leftIconFontTextLayout = nullrightIconFontTextLayout = nullleftRecFBg = nullrightRecFBg = nulltextMap.clear()}init {if (context is LifecycleOwner) {(context as LifecycleOwner).lifecycle.addObserver(this)}val array =context.obtainStyledAttributes(attrs, R.styleable.ClearEditTextView2)if (array != null) {mClearDrawable = array.getDrawable(R.styleable.ClearEditTextView2_DrawableClear)visibleDrawable = array.getDrawable(R.styleable.ClearEditTextView2_DrawableVisible)invisibleDrawable = array.getDrawable(R.styleable.ClearEditTextView2_DrawableInvisible)leftIconText = array.getText(R.styleable.ClearEditTextView2_IconFontLeft)clearText = array.getText(R.styleable.ClearEditTextView2_IconFontClear)visibleText = array.getText(R.styleable.ClearEditTextView2_IconFontVisible)invisibleText = array.getText(R.styleable.ClearEditTextView2_IconFontInvisible)iconFontPath = array.getString(R.styleable.ClearEditTextView2_IconFontPath)iconFontTextColor =array.getColor(R.styleable.ClearEditTextView2_IconFontTextColor, paint.color)iconFontBackgroundColor =array.getColor(R.styleable.ClearEditTextView2_IconFontBackground,iconFontBackgroundColor)iconFontTextSize = array.getDimensionPixelSize(R.styleable.ClearEditTextView2_IconFontTextSize, paint.textSize.toInt())array.recycle()}setSingleLine()init()}private fun init() {if (isIconFontModel()) {//优先IconFont模式//设置IconFont文本字体样式setIconFontPath(iconFontPath ?: BaseApplication.getInstance().globalIconFontPath)if (clearText?.isNotEmpty() == true) {lastClearShowState = false//默认隐藏清除按钮状态 (优先)rightIconModel = CLEAR_HIDE_MODEL//初始化清除按钮IconFontaddIconFontText(clearText?.toString(), true)} else if (visibleText?.isNotEmpty() == true&& invisibleText?.isNotEmpty() == true) {//初始化显示按钮IconFontaddIconFontText(visibleText?.toString(), true)//初始化隐藏按钮IconFontaddIconFontText(invisibleText?.toString(), true)lastVisibleState = true//默认设置隐藏状态inputType = InputType.TYPE_TEXT_VARIATION_PASSWORDrightIconModel = INVISIBLE_MODELsetVisibleIcon()}//记录原本padding距离oldPaddingLeft = paddingLeftoldPaddingTop = paddingTopoldPaddingRight = paddingRightoldPaddingBottom = paddingBottomif (leftIconText?.isNotEmpty() == true) {//初始化左边IconFontaddIconFontText(leftIconText?.toString(), false)}} else if (mClearDrawable != null) {//设置删除按钮的边界mClearDrawable!!.setBounds(0,0,mClearDrawable!!.intrinsicWidth,mClearDrawable!!.intrinsicHeight)rightIconModel = CLEAR_HIDE_MODEL//默认隐藏删除按钮setClearIcon(false)//监听EditText焦点变化,以根据text长度控制删除按钮的显示、隐藏onFocusChangeListener = this//监听文本内容变化addTextChangedListener(this)} else if (visibleDrawable != null && invisibleDrawable != null) {inputType = InputType.TYPE_TEXT_VARIATION_PASSWORD//设置显示按钮的边界visibleDrawable!!.setBounds(0,0,visibleDrawable!!.intrinsicWidth,visibleDrawable!!.intrinsicHeight)//设置隐藏按钮的边界invisibleDrawable!!.setBounds(0,0,invisibleDrawable!!.intrinsicWidth,invisibleDrawable!!.intrinsicHeight)rightIconModel = INVISIBLE_MODELlastVisibleState = truesetVisibleIcon()}}/*** 根据状态获取右边IconFont内容文字*/private fun getRightIcon(): CharSequence? {return when (rightIconModel) {VISIBLE_MODEL -> visibleTextINVISIBLE_MODEL -> invisibleTextCLEAR_SHOW_MODEL -> clearTextelse -> ""}}private fun isIconFontModel(): Boolean {return (leftIconText?.isNotEmpty() == true|| clearText?.isNotEmpty() == true|| visibleText?.isNotEmpty() == true|| invisibleText?.isNotEmpty() == true|| iconFontPath?.isNotEmpty() == true)}/*** 设置IconFont字体样式文件(优先assets下字体文件)** @param path 文件路径*/@Suppress("UNCHECKED")fun setIconFontPath(path: String?) {Intrinsics.checkNotNull(path)if (!TextUtils.isEmpty(path)) {val assets = context.assetsif (assets != null) {try {assets.open(path!!).use {iconFontPaint.typeface = Typeface.createFromAsset(assets, path)}} catch (e: IOException) {setIconFontPathFromFile(path)}} else {setIconFontPathFromFile(path)}}}/*** 设置IconFont字体样式文件** @param path 文件路径*/@Suppress("UNCHECKED")fun setIconFontPathFromFile(path: String?) {Intrinsics.checkNotNull(path)if (!TextUtils.isEmpty(path)) {setIconFontPathFromFile(File(path))}}/*** 设置IconFont字体样式文件** @param fontFile 字体样式文件*/@Suppress("UNCHECKED")fun setIconFontPathFromFile(fontFile: File) {Intrinsics.checkNotNull(fontFile)if (fontFile.exists()) {iconFontPaint.typeface = Typeface.createFromFile(fontFile)}}/*** 控制EditText右边制眼睛按钮的显示、隐藏*/private fun setVisibleIcon() {val isVisible = rightIconModel == VISIBLE_MODELif (lastVisibleState != isVisible) {lastVisibleState = isVisibleif (isIconFontModel()) {this.transformationMethod =if (isVisible) HideReturnsTransformationMethod.getInstance() else PasswordTransformationMethod.getInstance()setRightText()invalidate()requestLayout()} else {val visibleIcon = if (isVisible) visibleDrawable else invisibleDrawablesetCompoundDrawables(compoundDrawables[0], compoundDrawables[1],visibleIcon, compoundDrawables[3])setCompoundDrawablesWithIntrinsicBounds(compoundDrawables[0], compoundDrawables[1],visibleIcon, compoundDrawables[3])this.transformationMethod =if (isVisible) HideReturnsTransformationMethod.getInstance() else PasswordTransformationMethod.getInstance()setSelection(length())}setSelection(text?.length ?: 0)}}/*** 控制EditText右边制删除按钮的显示、隐藏*/private fun setClearIcon(isShow: Boolean) {if (isShow != lastClearShowState) {lastClearShowState = isShowrightIconModel = if (isShow) CLEAR_SHOW_MODEL else CLEAR_HIDE_MODELif (isIconFontModel()) {setRightText()invalidate()requestLayout()} else if (mClearDrawable != null) {val rightDrawable = if (isShow) mClearDrawable else nullsetCompoundDrawables(compoundDrawables[0], compoundDrawables[1],rightDrawable, compoundDrawables[3])}}}private fun isClearModel() =rightIconModel == CLEAR_SHOW_MODEL || rightIconModel == CLEAR_HIDE_MODEL/*** 有焦点,并文本长度大于0则显示删除按钮*/override fun onFocusChange(v: View, hasFocus: Boolean) {if (isClearModel()) {if (hasFocus) {setClearIcon(!text.isNullOrEmpty())} else {setClearIcon(false)}}}/*** 文本内容变化时回调* 当文本长度大于0时显示删除按钮, 否则隐藏*/override fun onTextChanged(text: CharSequence,start: Int,lengthBefore: Int,lengthAfter: Int) {super.onTextChanged(text, start, lengthBefore, lengthAfter)if (hasData && text.isEmpty() && lengthAfter == 0) {startShake(3)}hasData = text.isNotEmpty()if (isClearModel()) {setClearIcon(hasData)}}override fun beforeTextChanged(s: CharSequence,start: Int,count: Int,after: Int) {}override fun afterTextChanged(s: Editable) {}/*** 通过手指的触摸位置模式删除按钮的点击事件*/@SuppressLint("ClickableViewAccessibility")override fun onTouchEvent(event: MotionEvent): Boolean {if (compoundDrawables[2] != null || isRightIconDraw) {if (event.action == MotionEvent.ACTION_UP) {if (isClearModel()) {if ((isClickDrawable(event, mClearDrawable)|| isClickRecF(event, rightRecFBg))) {setText("")return true}} else if (isClickDrawable(event, visibleDrawable)|| isClickDrawable(event, invisibleDrawable)|| isClickRecF(event, rightRecFBg)) {rightIconModel =if (rightIconModel == VISIBLE_MODEL) INVISIBLE_MODEL else VISIBLE_MODELsetVisibleIcon()return true}}}return if (onTouchListener != null && onTouchListener!!.onTouch(this, event)) {true} else super.onTouchEvent(event)}override fun setOnTouchListener(l: OnTouchListener) {onTouchListener = l}/*** 是否在右边图标范围内点击*/private fun isClickDrawable(event: MotionEvent, drawable: Drawable?): Boolean {if (drawable == null) return falseval xTouchable =(event.x > width - paddingRight - drawable.intrinsicWidth&& event.x < width - paddingRight)val yTouchable =(event.y > (height - drawable.intrinsicHeight) shr 1&& event.y < (height + drawable.intrinsicHeight) shr 1)return xTouchable && yTouchable}/*** 是否在右边iconFont范围内点击*/private fun isClickRecF(event: MotionEvent, rectF: RectF?): Boolean {if (rectF == null) return falseval xTouchable =(event.x > right - paddingRight&& event.x < right)val yTouchable =(event.y > rectF.top&& event.y < rectF.bottom)return xTouchable && yTouchable}/*** EditText抖动*/@Suppress("UNCHECKED")fun startShake(counts: Int) {if (translateAnimation == null) {synchronized(this) {if (translateAnimation == null) {translateAnimation = TranslateAnimation(0f, 10f, 0f, 0f)translateAnimation?.interpolator = CycleInterpolator(counts.toFloat())translateAnimation?.duration = 500}}}startAnimation(translateAnimation)}override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {super.onSizeChanged(w, h, oldw, oldh)if (isIconFontModel()) {iconFontDrawPadding = compoundDrawablePadding.toFloat()val fontMetrics = iconFontPaint.fontMetricsleftRecFBg = RectF()rightRecFBg = RectF()iconFontH = fontMetrics.top.absoluteValue + fontMetrics.bottom.absoluteValue//字体高度val textBean = textMap[leftIconText]leftIconFontTextLayout = textBean?.layoutleftIconW = textBean?.with ?: 0isLeftIconDraw = leftIconW > 0iconLeftPadding = 2 * iconFontDrawPadding + leftIconW//内容文本左偏移距离setRightText()iconRightPadding = 2 * iconFontDrawPadding + maxRightTextW//内容文本右偏移距离setPadding(oldPaddingLeft, oldPaddingTop, oldPaddingRight, oldPaddingBottom)}}private fun setRightText() {val textBean = textMap[getRightIcon()?.toString()]rightIconFontTextLayout = textBean?.layoutrightIconW = textBean?.with ?: 0isRightIconDraw = rightIconW > 0}override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) {oldPaddingLeft = leftoldPaddingTop = topoldPaddingRight = rightoldPaddingBottom = bottomsuper.setPadding(left + (iconLeftPadding).toInt(),top,right + (iconRightPadding).toInt(),bottom)}override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)if (isLeftIconDraw || isRightIconDraw) {val compoundPaddingTop = compoundPaddingTopval compoundPaddingBottom = compoundPaddingBottomval mScrollX: Int = scrollX//横向滚动距离  防止IconFont跟随内容滚动val mScrollY: Int = scrollY//纵向滚动距离  防止IconFont跟随内容滚动//iconFont文字顶部位置val mTop =mScrollY + compoundPaddingTop + ((bottom - top - compoundPaddingBottom - compoundPaddingTop - iconFontH) / 2f)//iconFont文字底部位置val mBottom = mTop + iconFontHif (isLeftIconDraw) {setLeftRecF(mScrollX, mTop, mBottom)//绘制左边IconFont背景drawIconFontBg(canvas, leftRecFBg)//绘制左边IconFontdrawIconFont(canvas, leftRecFBg, leftIconFontTextLayout)}if (isRightIconDraw) {setRightRecF(mScrollX, mTop, mBottom)//绘制右边IconFont背景drawIconFontBg(canvas, rightRecFBg)//绘制右边IconFontdrawIconFont(canvas, rightRecFBg, rightIconFontTextLayout)}}}private fun setLeftRecF(mScrollX: Int, mTop: Float, mBottom: Float) {val mLeft = (mScrollX + iconFontDrawPadding)leftRecFBg?.left = mLeftleftRecFBg?.right = mLeft + leftIconWleftRecFBg?.top = mTopleftRecFBg?.bottom = mBottom}private fun setRightRecF(mScrollX: Int, mTop: Float, mBottom: Float) {val left = mScrollX + right - left - rightIconW - iconFontDrawPaddingrightRecFBg?.left = leftrightRecFBg?.right = left + rightIconWrightRecFBg?.top = mToprightRecFBg?.bottom = mBottom}private fun drawIconFont(canvas: Canvas?, rectF: RectF?, layout: Layout?) {canvas?.save()canvas?.translate(rectF?.left ?: 0f, rectF?.top ?: 0f)layout?.draw(canvas)canvas?.restore()}@Suppress("DEPRECATION")private fun addIconFontText(source: String?, isRight: Boolean) {if (!source.isNullOrEmpty()) {val rect = Rect()iconFontPaint.getTextBounds(source, 0, source.length, rect)val width = (rect.width() + iconFontPaint.textSize / 2).toInt()
//        1.需要分行的字符串
//        4.画笔对象
//        5.layout的宽度,字符串超出宽度时自动换行。
//        6.layout的对其方式,有ALIGN_CENTER, ALIGN_NORMAL, ALIGN_OPPOSITE 三种。
//        7.相对行间距,相对字体大小,1.5f表示行间距为1.5倍的字体高度。
//        8.在基础行距上添加多少 实际行间距等于这两者的和。
//        9.设置是否在字体的上升和下降之外包括额外的空间(避免在某些语言(例如阿拉伯语和卡纳达语)中进行剪切是必需的*)。 *默认值为{@code true}val textLayout = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {StaticLayout.Builder.obtain(source, 0, source.length, iconFontPaint, width).setAlignment(Layout.Alignment.ALIGN_CENTER).setLineSpacing(0f, 1f).build()} else {StaticLayout(source,iconFontPaint,width,Layout.Alignment.ALIGN_CENTER,1f,0f,true)}if (textLayout != null) {textMap[source] = TextBean(textLayout, width)}if (isRight) {maxRightTextW = max(maxRightTextW, width)}}}private fun drawIconFontBg(canvas: Canvas?, bgRectF: RectF?) {if (bgRectF != null) {canvas?.save()canvas?.drawRoundRect(bgRectF,0f,0f,bgPaint)canvas?.restore()}}private data class TextBean(val layout: Layout? = null,val with: Int? = null)
}
attr资源
 <declare-styleable name="ClearEditTextView2"><!--清空按钮图标 图片模式下高优先级--><attr name="DrawableClear" format="reference" /><!--显示按钮图标--><attr name="DrawableVisible" format="reference" /><!--隐藏按钮图标--><attr name="DrawableInvisible" format="reference" /><!--清空按钮IconFont 最高优先级--><attr name="IconFontClear" format="string" /><!--显示按钮IconFont--><attr name="IconFontVisible" format="string" /><!--隐藏按钮IconFont--><attr name="IconFontInvisible" format="string" /><!--左边IconFont 资源为空不显示--><attr name="IconFontLeft" format="string" /><!--IconFont字体文件路径--><attr name="IconFontPath" format="string" /><!--IconFont背景颜色 默认透明色--><attr name="IconFontBackground" format="color" /><!--IconFont字体颜色 默认内容文本颜色--><attr name="IconFontTextColor" format="color" /><!--IconFont字体尺寸 默认内容文本尺寸--><attr name="IconFontTextSize" format="dimension" /></declare-styleable>

这篇关于扩展EditText-支持IconFont图标的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/906522

相关文章

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

[环境配置]ubuntu20.04安装后wifi有图标但是搜不到热点解决方法

最近刚入手一台主机,暗影精灵8plus电竞主机,安装ubuntu后wifi怎么都搜不到热点,前后重装系统6次才算解决问题。这个心酸历程只有搞技术人才明白。下面介绍我解决过程。 首先主机到手后是个windows10系统,我用无线网连接了一下,可以正常上网,说明主机有无限网卡且正常。然后我就直接开始安装Ubuntu20.04了,安装成功后发现wifi有图标但是搜不到热点,我想是不是无线网卡驱动有没有

PHP7扩展开发之数组处理

前言 这次,我们将演示如何在PHP扩展中如何对数组进行处理。要实现的PHP代码如下: <?phpfunction array_concat ($arr, $prefix) {foreach($arr as $key => $val) {if (isset($prefix[$key]) && is_string($val) && is_string($prefix[$key])) {$arr[

PHP7扩展开发之字符串处理

前言 这次,我们来看看字符串在PHP扩展里面如何处理。 示例代码如下: <?phpfunction str_concat($prefix, $string) {$len = strlen($prefix);$substr = substr($string, 0, $len);if ($substr != $prefix) {return $prefix." ".$string;} else

PHP7扩展开发之类型处理

前言 这次,我们将演示如何在PHP扩展中如何对类型进行一些操作。如,判断变量类型。要实现的PHP代码如下: <?phpfunction get_size ($value) {if (is_string($value)) {return "string size is ". strlen($value);} else if (is_array($value)) {return "array si

PHP7扩展开发之依赖其他扩展

前言 有的时候,我们的扩展要依赖其他扩展。比如,我们PHP的mysqli扩展就依赖mysqlnd扩展。这中情况下,我们怎么使用其他扩展呢?这个就是本文讲述的内容。 我们新建立一个扩展,名字叫 demo_dep , 依赖之前的say扩展。 在demo_dep扩展中,我们实现demo_say方法。这个方法调用say扩展的say方法。 代码 基础代码 确保say扩展的头文件正确安装到了php

PHP7扩展开发之函数方式使用lib库

前言 首先说下什么是lib库。lib库就是一个提供特定功能的一个文件。可以把它看成是PHP的一个文件,这个文件提供一些函数方法。只是这个lib库是用c或者c++写的。 使用lib库的场景。一些软件已经提供了lib库,我们就没必要再重复实现一次。如,原先的mysql扩展,就是使用mysql官方的lib库进行的封装。 在本文,我们将建立一个简单的lib库,并在扩展中进行封装调用。 代码 基础

PHP7扩展开发之对象方式使用lib库

前言 上一篇文章,我们使用的是函数方式调用lib库。这篇文章我们将使用对象的方式调用lib库。调用代码如下: <?php $hello = new hello(); $result = $hello->get(); var_dump($result); ?> 我们将在扩展中实现hello类。hello类中将依赖lib库。 代码 基础代码 这个扩展,我们将在say扩展上增加相关代码。sa