美文网首页
MaterialEditView的简单实现

MaterialEditView的简单实现

作者: 王灵 | 来源:发表于2021-04-09 14:52 被阅读0次

边上课边学习边做笔记


image
  • 预留FloatingLabel的位置

首先需要确定FloatingLabel文字的大小和FloatingLabel与输入文本之间的间隔

    private var TEXT_SIZE = 12.dp
    private var TEXT_MARGIN = 8.dp

通过设置padding的方式来留出FloatingLabel的绘制空间和空隙

setPadding(
                    paddingLeft,
                    (paddingTop + TEXT_SIZE + TEXT_MARGIN).toInt(),//在原有的基础上增加FloatingLabel文字的高度和间隙
                    paddingRight,
                    paddingBottom
                )
  • 绘制FloatingLabel

文字的绘制

/**
     * Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted
     * based on the Align setting in the paint.
     *
     * @param text The text to be drawn
     * @param x The x-coordinate of the origin of the text being drawn
     * @param y The y-coordinate of the baseline of the text being drawn
     * @param paint The paint used for the text (e.g. color, size, style)
     */
    public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
        super.drawText(text, x, y, paint);
    }

text就是提示文字hint
x坐标就是原先的paddingLeft(由于文字自带的有间隙,所以为了实现视觉上的左对齐,我而外添加了1dp的偏移)
y坐标是原先的paddingTop+文字大小

private var HORIZONTAL_OFFSET = paddingLeft.toFloat() + 1.dp
private var VERTICAL_OFFSET = TEXT_SIZE + paddingTop

private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
...
paint.textSize = TEXT_SIZE
...
canvas.drawText(hint.toString(),HORIZONTAL_OFFSET,VERTICAL_OFFSET,paint)
  • 判断FloatingLabel文字的显示与隐藏

文字的显示与隐藏逻辑很简单,在输入框没有输入的情况下不显示FloatingLabel,在输入框不为空的情况下显示FloatingLabel

    // FloatingLabel是否正在显示
    private var floatingLabelShow = false
    ...
    //判断是否要显示,设置paint的透明度,即可实现FloatingLabel文字的显示与隐藏
    override fun onTextChanged(
        text: CharSequence?,
        start: Int,
        lengthBefore: Int,
        lengthAfter: Int
    ) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter)
        if (floatingLabelShow && text.isNullOrEmpty()) {
            floatingLabelShow = false
            paint.alpha = 0
        } else if (!floatingLabelShow && !text.isNullOrEmpty()) {
            floatingLabelShow = true
            paint.alpha = 0xff 
        }
    }
  • 设置显示、隐藏时的动画

使用属性动画来慢慢改变文字的位置和透明度

    //动画进度
    var floatingLabelFraction = 0f
        set(value) {
            field = value
            invalidate()
        }
    ...
    val animator: ObjectAnimator by lazy {
        ObjectAnimator.ofFloat(
            this,
            "floatingLabelFraction",
            1f,
            0f
        )
    }
    ...
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        //floatingLabelFraction的修改会调用invalidate()触发重绘,把透明度与动画进度关联
        paint.alpha = (0xff * floatingLabelFraction).toInt()
        ...
}

然后把onTextChanged里的直接修改alpha变为触发动画

    override fun onTextChanged(
        text: CharSequence?,
        start: Int,
        lengthBefore: Int,
        lengthAfter: Int
    ) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter)
        if (floatingLabelShow && text.isNullOrEmpty()) {
            floatingLabelShow = false
            animator.start()//正向动画
        } else if (!floatingLabelShow && !text.isNullOrEmpty()) {
            floatingLabelShow = true
            animator.reverse()//反向动画
        }
    }

现在实现了显示隐藏的动画,还需要FloatingLabel文字的位置挪移动画
文字显示时,文字的开始位置是EditView的文字位置

//修改之后的顶部padding + edit文字的大小
paddingTop + textSize

结束位置是

VERTICAL_OFFSET

floatingLabelFraction关联

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        paint.alpha = (0xff * floatingLabelFraction).toInt()
        canvas.drawText(
            hint.toString(),
            HORIZONTAL_OFFSET,
            VERTICAL_OFFSET + (paddingTop + textSize - VERTICAL_OFFSET) * (1 - floatingLabelFraction),
            paint
        )
    }
  • 代码设置功能开启与关闭

动态去设置是否开启FloatingLabel功能

    //是否开启功能
    var useFloatingLabel = false

FloatingLabel功能的开启与否会影响padding

    //是否开启功能
    var useFloatingLabel = false
        set(value) {
            if (field == value) return 
            field = value
            if (field) {
                setPadding(
                    paddingLeft,
                    (paddingTop + TEXT_SIZE + TEXT_MARGIN).toInt(),
                    paddingRight,
                    paddingBottom
                )

            } else {
                setPadding(
                    paddingLeft,
                    (paddingTop - TEXT_SIZE - TEXT_MARGIN).toInt(),//从开启状态变为关闭状态。开启时的paddingTop已经是修改过的了,所以要减去才算恢复
                    paddingRight,
                    paddingBottom
                )
            }
            invalidate()
        }

如果功能关闭,那么就不需要判断是否需要动画了

    override fun onTextChanged(
        text: CharSequence?,
        start: Int,
        lengthBefore: Int,
        lengthAfter: Int
    ) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter)
        if (useFloatingLabel) {
            if (floatingLabelShow && text.isNullOrEmpty()) {
                floatingLabelShow = false
                animator.start()//正向动画
            } else if (!floatingLabelShow && !text.isNullOrEmpty()) {
                floatingLabelShow = true
                animator.reverse()//反向动画
            }
        }
    }

如果功能关闭,绘制也不需要了


    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (useFloatingLabel) {
            paint.alpha = (0xff * floatingLabelFraction).toInt()
            canvas.drawText(
                hint.toString(),
                HORIZONTAL_OFFSET,
                VERTICAL_OFFSET + (paddingTop + textSize - VERTICAL_OFFSET) * (1 - floatingLabelFraction),
                paint
            )
        }
    }
  • 支持xml设置

attrs.xml里增添属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MaterialEditView">
        <attr name="use_floating_label" format="boolean"></attr>
    </declare-styleable>
</resources>

在view里去读取设置,并对useFloatingLabel赋值

        val typeArray = context.obtainStyledAttributes(attrs, R.styleable.MaterialEditView)
        useFloatingLabel =
            typeArray.getBoolean(R.styleable.MaterialEditView_use_floating_label, true)
        typeArray.recycle()//用完,需要手动关闭
  • 完整的代码

class MaterialEditView(context: Context, attrs: AttributeSet) : AppCompatEditText(context, attrs) {
    private var TEXT_SIZE = 12.dp
    private var TEXT_MARGIN = 8.dp
    private var HORIZONTAL_OFFSET = paddingLeft.toFloat() + 1.dp
    private var VERTICAL_OFFSET = TEXT_SIZE + paddingTop

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    private var floatingLabelShow = false

    //动画进度
    var floatingLabelFraction = 0f
        set(value) {
            field = value
            invalidate()
        }

    //是否开启功能
    var useFloatingLabel = false
        set(value) {
            if (field == value) return
            field = value
            if (field) {
                setPadding(
                    paddingLeft,
                    (paddingTop + TEXT_SIZE + TEXT_MARGIN).toInt(),
                    paddingRight,
                    paddingBottom
                )

            } else {
                setPadding(
                    paddingLeft,
                    (paddingTop - TEXT_SIZE - TEXT_MARGIN).toInt(),
                    paddingRight,
                    paddingBottom
                )
            }
            invalidate()
        }
    val animator: ObjectAnimator by lazy {
        ObjectAnimator.ofFloat(
            this,
            "floatingLabelFraction",
            1f,
            0f
        )
    }

    init {

        paint.textSize = TEXT_SIZE

//        for (index in 0 until attrs.attributeCount){
//            attrs.getAttributeName(index)
//            attrs.getAttributeValue(index)
//        }

        val typeArray = context.obtainStyledAttributes(attrs, R.styleable.MaterialEditView)
        useFloatingLabel =
            typeArray.getBoolean(R.styleable.MaterialEditView_use_floating_label, true)
        typeArray.recycle()
    }

    override fun onTextChanged(
        text: CharSequence?,
        start: Int,
        lengthBefore: Int,
        lengthAfter: Int
    ) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter)
        if (floatingLabelShow && text.isNullOrEmpty()) {
            floatingLabelShow = false
            animator.start()//正向动画
        } else if (!floatingLabelShow && !text.isNullOrEmpty()) {
            floatingLabelShow = true
            animator.reverse()//反向动画
        }
    }


    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (useFloatingLabel) {
            paint.alpha = (0xff * floatingLabelFraction).toInt()
            canvas.drawText(
                hint.toString(),
                HORIZONTAL_OFFSET,
                VERTICAL_OFFSET + (paddingTop + textSize - VERTICAL_OFFSET) * (1 - floatingLabelFraction),
                paint
            )
        }
    }
}

相关文章

网友评论

      本文标题:MaterialEditView的简单实现

      本文链接:https://www.haomeiwen.com/subject/ypyikltx.html