边上课边学习边做笔记
-
预留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
)
}
}
}
网友评论