Android 自定义Toast
原生的
Toast
其实相当好用,而且足够简单轻量,但是架不住需求千奇百怪,而且老板一般都会觉得这个提示不明显!原本Toast
是可以自定义样式的,但现在setView
方法已经过期,本文通过自定义View的形式来实现类Toast
效果,先上效果图
获取屏幕宽高
因为需要将Toast显示到一个大致固定的位置、尽量显示一行且不能超过屏幕宽度,所以需要获取屏幕宽高,这里简单写了个工具类。
object DisplayUtil {
/**
* 可用显示大小的绝对宽度(以像素为单位)
*/
fun getWidth(): Int = Application.getInstance().resources.displayMetrics.widthPixels
/**
* 可用显示大小的绝对高度(以像素为单位)
*/
fun getHeight(): Int = Application.getInstance().resources.displayMetrics.heightPixels
}
这里使用的是单例Application来获取resources
,还不知道的可以网上搜一下,也可以看看这篇文章<
自定义Toast
先创建toast_dialog_bg_style.xml
用作Toast的背景样式,这里就是黑色有一点点透明的圆角背景。
然后是Toast的布局,创建alert_dialog_toast.xml
,我这里只放了文本,有兴趣的可以在布局里加上图片之类的。
最后就是核心代码了,因为使用悬浮窗是需要权限,高版本还需要用户授权才可以,所以我这里直接继承PopupWindow来编写。
class MToast private constructor() : PopupWindow(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
) {
private var mActivity: WeakReference? = null
private var mView: WeakReference? = null
private var mText: WeakReference? = null
private var textQueue: Queue> = LinkedBlockingQueue()
// 最大等待显示数目
private val maxWaitShowNumber = 10
// 左右内边距之和
private var paddingWidth = 0
companion object {
fun makeText(
context: Context,
text: String,
showLength: Int = Toast.LENGTH_SHORT
): MToast {
val instance = Inner.instance
if (instance.maxWaitShowNumber > instance.textQueue.size) {
instance.textQueue.offer(arrayListOf(text, showLength * 2 + 1))
}
val activity = context as Activity
if (instance.mActivity?.get()?.localClassName != activity.localClassName) {
instance.mActivity = WeakReference(activity)
instance.initView()
}
return instance
}
}
private object Inner {
var instance: MToast = MToast()
}
@SuppressLint("InflateParams")
private fun initView() {
mActivity?.get()?.let {
val view = LayoutInflater.from(it)
.inflate(R.layout.alert_dialog_toast, null, false)
mView = WeakReference(view)
mText = WeakReference(view.findViewById(R.id.toast_text))
paddingWidth = view.paddingStart + view.paddingEnd
contentView = view
// 不设置焦点
isFocusable = false
// 点击后退键pop消失
isTouchable = false
setBackgroundDrawable(ColorDrawable(0x00000000))
// 设置自带的淡出淡入效果
animationStyle = R.style.Animation_AppCompat_Dialog
}
}
fun show() {
if (!isShowing) {
textQueue.poll()?.let { item ->
mActivity?.get()?.let {
val nText = item[0] as String
val showLength = item[1] as Int
mText?.get()?.let { textView ->
textView.text = nText
// 界面宽高
val heightPixels = DisplayUtil.getHeight()
val widthPixels = DisplayUtil.getWidth()
// 内容宽度+布局宽度
val dw = StaticLayout.getDesiredWidth(nText, textView.paint)
val cmpWidth = dw.toInt() + paddingWidth
width = if (cmpWidth > widthPixels)
widthPixels - paddingWidth
else
cmpWidth
showAtLocation(it.window.decorView, Gravity.CENTER, 0, heightPixels / 2 - heightPixels / 10)
CoroutineUtil.execIO {
delay(showLength * 1000L)
withContext(Dispatchers.Main) {
hide()
if (textQueue.size > 0) {
show()
}
}
}
}
}
}
}
}
private fun hide() {
if (isShowing) {
dismiss()
}
}
}
这里调用和Toast的调用一样,但实际上还是有一些区别,这里做成了静态单例类,然后有一个队列来装要显示的数据,通过makeText进行添加,show则是按顺序显示在队列里的数据,直到没有为止。
MToast.makeText(context, "msg", Toast.LENGTH_SHORT).show()
Acitivity切换的时候应该还是会存在一些问题,不过常规使用应该可以了
共有 0 条评论