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