文章目录
不带有搜索历史的仿ios搜索框
需求分析
- 输入框右侧添加放大镜和清空除按钮,未输入内容显示放大镜,输入内容则显示清空按钮,注:放大镜和清空按钮在同一个位置
- 点击回车搜索时如果输入框内有内容则隐藏右侧清空按钮和放大镜隐藏
- 收起软键盘时操作同回车搜索操作一样
实现逻辑
创建自定义View
@SuppressLint("AppCompatCustomView")
class ClearEditText(context: Context, attr: AttributeSet) : EditText(context, attr), TextView.OnEditorActionListener, View.OnFocusChangeListener, ViewTreeObserver.OnGlobalLayoutListener {
}
定义成员变量
private var rightClearDrawable: Drawable? = null
private var drawable: Drawable? = null
private var searchDrawable: Drawable? = null
初始化初始属性
init {
/*获取删除按钮图片的Drawable对象*/
drawable = ContextCompat.getDrawable(context, R.drawable.login_phone_close)
searchDrawable = ContextCompat.getDrawable(context, R.drawable.ic_serach)
/*设置图片的范围*/
drawable!!.setBounds(0, 0, convertDpToPixel(13f).toInt(), convertDpToPixel(13f).toInt())
searchDrawable!!.setBounds(0, 0, convertDpToPixel(15f).toInt(), convertDpToPixel(15f).toInt())
/*设置EditText和删除按钮图片的间距*/
compoundDrawablePadding = context.resources.getDimensionPixelSize(R.dimen.dp5)
/*输入框内容监听*/
afterTextChanged {
/*判断输入框有没有内容,设置是否显示删除按钮*/
if ("" != text.toString().trim { it <= ' ' } && text.toString().trim { it <= ' ' }.isNotEmpty()) {
setHideClearDrawable(true)
} else {
setHideClearDrawable(false)
}
}
if (logo_location == 1) {
setCompoundDrawables(searchDrawable, compoundDrawables[1], rightClearDrawable, compoundDrawables[3])
}
/*设置是否显示删除按钮*/
setHideClearDrawable(false)
setOnEditorActionListener(this)
onFocusChangeListener = this
rootView.viewTreeObserver.addOnGlobalLayoutListener(this)
}
fun convertDpToPixel(dp: Float): Float {
val metrics = context.resources.displayMetrics
return dp * (metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT)
}
创建EditTextExtensionFunction,定义扩展函数
fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
this.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(editable: Editable?) {
afterTextChanged.invoke(editable.toString())
}
})
}
@SuppressLint("ClickableViewAccessibility")
fun ClearEditText.searchClearTouch(event: MotionEvent) {
/*判断手指按下的x坐标*/
val x = event.x
/*获取自定义EditText宽度*/
val width = this.width.toFloat()
/*获取EditText右Padding值*/
val totalPaddingRight = this.totalPaddingRight.toFloat()
/*判断手指按下的区域是否在删除按钮宽高范围内*/
if (event.action == MotionEvent.ACTION_UP) {
if (x > width - totalPaddingRight && x < width && event.y < this.height) {
val drawable = compoundDrawables[2]
if ("" != text.toString().trim { it <= ' ' } && text.toString().trim { it <= ' ' }.isNotEmpty() && drawable != null) {
this.setText("")
}
}
}
}
判断显示图标是放大镜还是清空按钮
private fun setHideClearDrawable(isVisible: Boolean) {
/*是否显示删除按钮*/
rightClearDrawable = if (isVisible) {
drawable
} else {
if (logo_location == 2) searchDrawable else null
}
/*给EditText左,上,右,下设置图片*/
setCompoundDrawables(compoundDrawables[0], compoundDrawables[1], rightClearDrawable, compoundDrawables[3])
}
监听点击事件
这里判断点击位置是否是清空按钮
override fun onTouchEvent(event: MotionEvent): Boolean {
searchClearTouch(event)
return super.onTouchEvent(event)
}
监听回车事件
判断输入框输入内容,如果为空则不隐藏按钮,反之则隐藏
override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
clearFocus()
if (!TextUtils.isEmpty(text)) {
setCompoundDrawables(compoundDrawables[0], compoundDrawables[1], null, compoundDrawables[3])
hideSoftInput()
}
}
return false
}
监听是否获取焦点
监听获取焦点和失去焦点,失去焦点是输入框内又内容的情况下需要隐藏按钮反之显示
override fun onFocusChange(v: View?, hasFocus: Boolean) {
if (hasFocus) {
// 此处为得到焦点时的处理内容
if (!TextUtils.isEmpty(text)) {
setHideClearDrawable(true)
} else {
setHideClearDrawable(false)
}
} else {
// 此处为失去焦点时的处理内容
hideSoftInput()
if (!TextUtils.isEmpty(text)) {
setCompoundDrawables(compoundDrawables[0], compoundDrawables[1], null, compoundDrawables[3])
}
}
}
监听软键盘
通过View树监听判断软键盘是否弹出
var rootViewVisibleHeight = 0
override fun onGlobalLayout() {
val rect = Rect()
rootView.getWindowVisibleDisplayFrame(rect)
var visibleHeight = rect.height()
if (rootViewVisibleHeight == 0) {
//拷贝一份,用于比较值的改变
rootViewVisibleHeight = visibleHeight
return
}
//软键盘显示/隐藏状态没有改变
if (rootViewVisibleHeight == visibleHeight) {
return
}
if (rootViewVisibleHeight - visibleHeight > 200) {
rootViewVisibleHeight = visibleHeight
return
}
if (visibleHeight - rootViewVisibleHeight > 200) {
clearFocus()
rootViewVisibleHeight = visibleHeight
return
}
}
扩展
扩展自定义属性,使用更灵活,可根据需要自行扩展
<declare-styleable name="ClearEditText">
<attr name="clears_icon" format="reference"/>
<attr name="logo" format="reference"/>
<attr name="logo_location" format="integer">
<enum name="left" value="1"/>
<enum name="right" value="2"/>
</attr>
</declare-styleable>
val obtainStyledAttributes = context.obtainStyledAttributes(attr, R.styleable.ClearEditText)
clear_icon = obtainStyledAttributes.getResourceId(R.styleable.ClearEditText_clears_icon, 0)
logo = obtainStyledAttributes.getResourceId(R.styleable.ClearEditText_logo, 0)
logo_location = obtainStyledAttributes.getInteger(R.styleable.ClearEditText_logo_location, 2)
obtainStyledAttributes.recycle()
为了方便扩展其他独立操作给出自定义回调接口
private var mOnEditorActionListener: MyOnEditorActionListener? = null
private var mOnFocusChangeListener: MyOnFocusChangeListener? = null
private var onSoftKeyShowListener: OnSoftKeyShowListener? = null
fun setOnSoftKeyShowListener(onSoftKeyShowListener: OnSoftKeyShowListener){
this.onSoftKeyShowListener = onSoftKeyShowListener
}
interface OnSoftKeyShowListener {
fun onSoftKeyShow(isShow: Boolean)
}
fun setMyOnEditorActionListener(l: MyOnEditorActionListener?) {
mOnEditorActionListener = l
}
interface MyOnEditorActionListener {
fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?)
}
fun setMyOnFocusChangeListener(f: MyOnFocusChangeListener) {
mOnFocusChangeListener = f;
}
interface MyOnFocusChangeListener {
fun onFocusChange(v: View?, hasFocus: Boolean)
}
源码
ClearEditText
package com.zhengyou.jxdkj.kotlin.view
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.text.TextUtils
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.ViewTreeObserver
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.TextView
import androidx.core.content.ContextCompat
import com.zhengyou.jxdkj.R
import com.zhengyou.jxdkj.kotlin.extension.*
import kotlinx.android.synthetic.main.view_sys_msg.view.*
@SuppressLint("AppCompatCustomView")
class ClearEditText(context: Context, attr: AttributeSet) : EditText(context, attr),
TextView.OnEditorActionListener, View.OnFocusChangeListener,
ViewTreeObserver.OnGlobalLayoutListener {
var rootViewVisibleHeight = 0
var clear_icon = 0
var logo = 0
var logo_location = 0
private var rightClearDrawable: Drawable? = null
private var drawable: Drawable? = null
private var searchDrawable: Drawable? = null
private var mOnEditorActionListener: MyOnEditorActionListener? = null
private var mOnFocusChangeListener: MyOnFocusChangeListener? = null
private var onSoftKeyShowListener: OnSoftKeyShowListener? = null
init {
val obtainStyledAttributes = context.obtainStyledAttributes(attr, R.styleable.ClearEditText)
clear_icon = obtainStyledAttributes.getResourceId(R.styleable.ClearEditText_clears_icon, 0)
logo = obtainStyledAttributes.getResourceId(R.styleable.ClearEditText_logo, 0)
logo_location = obtainStyledAttributes.getInteger(R.styleable.ClearEditText_logo_location, 2)
obtainStyledAttributes.recycle()
/*获取删除按钮图片的Drawable对象*/
drawable = ContextCompat.getDrawable(context, if (clear_icon == 0) R.drawable.login_phone_close else clear_icon)
searchDrawable = ContextCompat.getDrawable(context, if (logo == 0) R.drawable.ic_serach else logo)
/*设置图片的范围*/
drawable!!.setBounds(0, 0, convertDpToPixel(13f).toInt(), convertDpToPixel(13f).toInt())
searchDrawable!!.setBounds(0, 0, convertDpToPixel(15f).toInt(), convertDpToPixel(15f).toInt()
)
/*设置EditText和删除按钮图片的间距*/
compoundDrawablePadding = context.resources.getDimensionPixelSize(R.dimen.dp5)
/*输入框内容监听*/
afterTextChanged {
/*判断输入框有没有内容,设置是否显示删除按钮*/
if ("" != text.toString().trim { it <= ' ' } && text.toString().trim { it <= ' ' }.isNotEmpty()) {
setHideClearDrawable(true)
} else {
setHideClearDrawable(false)
}
}
/*设置是否显示删除按钮*/
setHideClearDrawable(false)
if (logo_location == 1) {
setCompoundDrawables(searchDrawable, compoundDrawables[1], rightClearDrawable, compoundDrawables[3])
}
setOnEditorActionListener(this)
onFocusChangeListener = this
rootView.viewTreeObserver.addOnGlobalLayoutListener(this)
}
private fun setHideClearDrawable(isVisible: Boolean) {
/*是否显示删除按钮*/
rightClearDrawable = if (isVisible) {
drawable
} else {
if (logo_location == 2) searchDrawable else null
}
/*给EditText左,上,右,下设置图片*/
setCompoundDrawables(compoundDrawables[0], compoundDrawables[1], rightClearDrawable, compoundDrawables[3])
}
/**
* 监听点击事件是否点击到清空按钮
*/
override fun onTouchEvent(event: MotionEvent): Boolean {
searchClearTouch(event)
return super.onTouchEvent(event)
}
/**
* 监听回车时间
*/
override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
clearFocus()
if (!TextUtils.isEmpty(text)) {
setCompoundDrawables(compoundDrawables[0], compoundDrawables[1], null, compoundDrawables[3])
hideSoftInput()
}
}
mOnEditorActionListener?.let {
it.onEditorAction(v, actionId, event)
}
return false
}
/**
* 监听是否获取焦点
*/
override fun onFocusChange(v: View?, hasFocus: Boolean) {
if (hasFocus) {
// 此处为得到焦点时的处理内容
if (!TextUtils.isEmpty(text)) {
setHideClearDrawable(true)
} else {
setHideClearDrawable(false)
}
} else {
// 此处为失去焦点时的处理内容
hideSoftInput()
if (!TextUtils.isEmpty(text)) {
setCompoundDrawables(compoundDrawables[0], compoundDrawables[1],
null, compoundDrawables[3])
}
}
mOnFocusChangeListener?.onFocusChange(v, hasFocus)
}
override fun onGlobalLayout() {
val rect = Rect()
rootView.getWindowVisibleDisplayFrame(rect)
var visibleHeight = rect.height()
if (rootViewVisibleHeight == 0) {
//拷贝一份,用于比较值的改变
rootViewVisibleHeight = visibleHeight
return
}
//软键盘显示/隐藏状态没有改变
if (rootViewVisibleHeight == visibleHeight) {
return
}
if (rootViewVisibleHeight - visibleHeight > 200) {
onSoftKeyShowListener?.onSoftKeyShow(true)
rootViewVisibleHeight = visibleHeight
return
}
if (visibleHeight - rootViewVisibleHeight > 200) {
clearFocus()
onSoftKeyShowListener?.onSoftKeyShow(false)
rootViewVisibleHeight = visibleHeight
return
}
}
fun convertDpToPixel(dp: Float): Float {
val metrics = context.resources.displayMetrics
return dp * (metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT)
}
fun setOnSoftKeyShowListener(onSoftKeyShowListener: OnSoftKeyShowListener) {
this.onSoftKeyShowListener = onSoftKeyShowListener
}
interface OnSoftKeyShowListener {
fun onSoftKeyShow(isShow: Boolean)
}
fun setMyOnEditorActionListener(l: MyOnEditorActionListener?) {
mOnEditorActionListener = l
}
interface MyOnEditorActionListener {
fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?)
}
fun setMyOnFocusChangeListener(f: MyOnFocusChangeListener) {
mOnFocusChangeListener = f;
}
interface MyOnFocusChangeListener {
fun onFocusChange(v: View?, hasFocus: Boolean)
}
}
EditTextExtensionFunction
package com.zhengyou.jxdkj.kotlin.extension
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Rect
import android.text.Editable
import android.text.TextWatcher
import android.view.MotionEvent
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import com.scwang.smart.refresh.layout.SmartRefreshLayout
import com.scwang.smart.refresh.layout.api.RefreshLayout
import com.zhengyou.jxdkj.kotlin.view.ClearEditText
import com.zhengyou.jxdkj.widget.view.AnimHeader
/**
* 扩展函数
*/
fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
this.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(editable: Editable?) {
afterTextChanged.invoke(editable.toString())
}
})
}
fun EditText.onTextChanged(onTextChanged: (String, Int, Int, Int) -> Unit) {
this.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
onTextChanged.invoke(s.toString(), start, before, count)
}
override fun afterTextChanged(editable: Editable?) {
}
})
}
fun EditText.beforeTextChanged(addTextChangedListener: (String, Int, Int, Int) -> Unit) {
this.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
addTextChangedListener.invoke(s.toString(), start, count, after)
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(editable: Editable?) {
}
})
}
@SuppressLint("ClickableViewAccessibility")
fun ClearEditText.searchClearTouch(event: MotionEvent) {
/*判断手指按下的x坐标*/
val x = event.x
/*获取自定义EditText宽度*/
val width = this.width.toFloat()
/*获取EditText右Padding值*/
val totalPaddingRight = this.totalPaddingRight.toFloat()
/*判断手指按下的区域是否在删除按钮宽高范围内*/
if (event.action == MotionEvent.ACTION_UP) {
if (x > width - totalPaddingRight && x < width && event.y < this.height) {
val drawable = compoundDrawables[2]
if ("" != text.toString().trim { it <= ' ' } && text.toString().trim { it <= ' ' }.isNotEmpty() && drawable != null) {
this.setText("")
}
}
}
}
var rootViewVisibleHeight = 0
fun EditText.softKeyIsShow(): Boolean? {
val rect = Rect()
rootView.getWindowVisibleDisplayFrame(rect)
var visibleHeight = rect.height()
if (rootViewVisibleHeight == 0) {
//拷贝一份,用于比较值的改变
rootViewVisibleHeight = visibleHeight
return null
}
//软键盘显示/隐藏状态没有改变
if (rootViewVisibleHeight == visibleHeight) {
return null
}
if (rootViewVisibleHeight - visibleHeight > 200) {
rootViewVisibleHeight = visibleHeight
return true
}
if (visibleHeight - rootViewVisibleHeight > 200) {
rootViewVisibleHeight = visibleHeight
return false
}
return false
}
fun EditText.hideSoftInput(){
val im = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
if (im != null && windowToken != null) {
im.hideSoftInputFromWindow(windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
}
}
<declare-styleable name="ClearEditText">
<attr name="clears_icon" format="reference"/>
<attr name="logo" format="reference"/>
<attr name="logo_location" format="integer">
<enum name="left" value="1"/>
<enum name="right" value="2"/>
</attr>
</declare-styleable>