自定义View⑦之自定义EditText

269 阅读2分钟

效果如下,要实现一个自定义hint,这个hint文字可以根据输入情况改变显示位置的功能,hint文字具有透明度变化和上下移动的动画效果
36

实现

  1. 首先要让这个EditText比普通EditText占用空间要大一些,包含两行,输入文字和hint单独各占一行,设置一个paddingTop边距
  2. 设置paint画笔的文字大小
  3. 把自定义hint的文字画出来
  4. hint显示规则处理,当输入文字不为空时,才显示自定义hint字体,这一步放到第五步处理
  5. 悬浮自定义hint
    • 当输入为空时,自定义hint不显示
    • 当输入不为空时,自定义hint动画弹出显示
  6. 自定义hint动画效果,加一个透明度变化
  7. 自定义hint动画效果,加一个上下移动的动画
  8. 增加一个自定义hint的开关属性useFloatingLabel,这里提供的是java和kotlin代码动态调用的方式
        materialET.useFloatingLabel = true
    
  9. 增加一个xml中配置的useFloatingLabel属性,需要在attrs中添加如下代码
    <resources>
        <!--  MaterialEditText  -->
        <declare-styleable name="MaterialEditText">
            <attr name="useFloatingLabel" format="boolean"/>
        </declare-styleable>
    </resources>
    
  10. 我们继续深入了解下attrs是干什么的
    • 通过下面的截图,我们可以看到,xml中各个属性都能在attrs中找到对应的属性值

完整代码

MaterialEditText

public class MaterialEditText(context: Context, attrs: AttributeSet
) : android.support.v7.widget.AppCompatEditText(context, attrs) {

  private  val TAG = "MaterialEditText"
  //画笔
  private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
  //悬浮hint是否显示
  private var floatingLabelShown = false
  var floatingLabelFraction = 0f
    set(value) {
      field = value
      invalidate()
    }

  //显示动画 延迟初始化
  private val animator by lazy {
    ObjectAnimator.ofFloat(this,"floatingLabelFraction",0f,1f)
  }
  //消失动画 延迟初始化
  private val animatorReverse by lazy {
    ObjectAnimator.ofFloat(this,"floatingLabelFraction",0f)
  }
  //8 java,kotlin方式开启FloatingLabel
  var useFloatingLabel = false
    set(value) {
      if (field!=value) {
        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)
        }
      }
    }

  init {
    //2.hint 字体文字
    paint.textSize = TEXT_SIZE;
    //1. 设置padding,给hint文字留有空间,比普通EditText要大
    if (useFloatingLabel) {
      setPadding(paddingLeft, (paddingTop+ TEXT_SIZE+ TEXT_MARGIN).toInt(),paddingRight,paddingBottom)
    }
    // 9 xml中useFloatingLabel属性处理
    // 过滤并取值
//    val typeArray = context.obtainStyledAttributes(attrs, R.styleable.MaterialEditText)
//    useFloatingLabel = typeArray.getBoolean(R.styleable.MaterialEditText_useFloatingLabel,true)
    //kotlin优化写法,自定义取值法
    val typeArray = context.obtainStyledAttributes(attrs, intArrayOf(R.attr.useFloatingLabel))
    useFloatingLabel = typeArray.getBoolean(0,true)
    typeArray.recycle()

    //10 打印探究一下attrs中包含哪些信息,指向xml中添加的属性
    for (index in 0 until attrs.attributeCount){
      Log.d(TAG, "key:${attrs.getAttributeName(index)},value:${attrs.getAttributeValue(index)}")
    }

  }

  override fun onTextChanged(text: CharSequence?, start: Int, lengthBefore: Int, lengthAfter: Int) {
    super.onTextChanged(text, start, lengthBefore, lengthAfter)
    //5 浮动hint处理逻辑
    if (floatingLabelShown&&text.isNullOrEmpty()){
      //5.1 消失 1->0
      floatingLabelShown = false;
//      animatorReverse.start()
      //使用反向动画替代animatorReverse
      animator.reverse()
    }else if (!floatingLabelShown && !text.isNullOrEmpty()){
      //5.2 显示 0->1
      floatingLabelShown = true;
      animator.start()
    }
  }

  override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
//    4.当输入文字不为空时,显示上方hint文字,这一步做演示,忽略
//    if (!text.isNullOrEmpty()){
      //6. 绘制时画笔透明度变化
      paint.alpha = (floatingLabelFraction*0xff).toInt()
      //7.上下移动动画
      val currentVerticalValue = VERTICAL_OFFSET+EXTRA_VERTICAL_OFFSET*(1-floatingLabelFraction)
      //3.把hint字体画出来
      canvas.drawText(hint.toString(),HORIZONTAL_OFFSET,currentVerticalValue,paint)
//    }

  }

  
}

xml中

 <com.dsh.txlessons.viewmaterialedittext.MaterialEditText
        android:id="@+id/met"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:ignore="MissingConstraints"
        android:hint="Username"
        app:useFloatingLabel="false"
        />

Activity使用

class MaterialEditTextActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_material_edit_text)
    met.postDelayed(Runnable {
      met.useFloatingLabel = true
    },3000)

  }
}