Toast
Toast
pop
import android.animation.Animator
import android.animation.AnimatorSet
import android.app.Activity
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.ColorFilter
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PixelFormat
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.view.MotionEvent
import android.view.View
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.view.Window
import android.widget.ImageView
import android.widget.PopupWindow
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import com.gotokeep.keep.common.extensions.dp
import com.gotokeep.keep.common.extensions.setVisible
import com.gotokeep.keep.common.extensions.updateLayoutParamsSafely
import com.gotokeep.keep.common.utils.ActivityUtils
import com.gotokeep.keep.common.utils.RR
import com.gotokeep.keep.common.utils.ViewUtils
import com.gotokeep.keep.commonui.R
import com.gotokeep.keep.commonui.utils.AnimatorUtils
import com.gotokeep.keep.commonui.view.TipsWindowCallbackProxy
import com.gotokeep.keep.logger.KLog
import kotlinx.android.synthetic.main.view_tips_pop.view.buttonNegative
import kotlinx.android.synthetic.main.view_tips_pop.view.buttonPositive
import kotlinx.android.synthetic.main.view_tips_pop.view.layoutButton
import java.lang.ref.WeakReference
/**
* 上下位置时箭头的宽度,左右时是高度
*/
private val arrowSizeLong = 10.dp
/**
* 上下位置时箭头的高度,左右时是宽度
*/
private val arrowSizeShort = 8.dp
/**
* 偏左 or 偏右 时,箭头离气泡边缘的距离,可由 arrowOffsetX 自定义
*/
private var arrowDistanceFromEdge = 16.dp
private val marginToAnchor = 12.dp
private val animatorDuration = 200
private val animatorAlphaMin = 0.0f
private val animatorAlphaMax = 1.0f
private val animatorScaleXMin = 0.0f
private val animatorScaleXMax = 1.0f
private val animatorScaleYMin = 0.0f
private val animatorScaleYMax = 1.0f
init {
keepToolTipsRef = WeakReference(this)
rootView = ViewUtils.newInstance(builder.context, R.layout.view_tips_pop)
as ViewGroup
rootView.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
if (builder.context is Activity) {
activity = builder.context
}
contentMaxLines = builder.contentMaxLines
contentLineMaxLength = builder.contentLineMaxLength
val layoutMessage =
rootView.findViewById<RelativeLayout>(R.id.layoutMessage)
val imageIcon = rootView.findViewById<ImageView>(R.id.imageTipIcon)
if (builder.iconResource != 0) {
imageIcon.setVisible()
imageIcon.setImageResource(builder.iconResource)
layoutMessage.updateLayoutParamsSafely<RelativeLayout.LayoutParams> {
marginStart = 15.dp
topMargin = 11.dp
rootView.layoutParams = this
}
}
contentView = when (builder.contentView) {
null -> createTextView(builder.context, builder.message)
else -> builder.contentView
}
if (contentView?.id == View.NO_ID) {
contentView.id = R.id.tips_content
}
contentView?.measure(0, 0)
layoutMessage.addView(contentView)
when (builder.style) {
DARK ->
layoutMessage.setBackgroundResource(R.drawable.bg_shape_tips_dark)
PURPLE ->
layoutMessage.setBackgroundResource(R.drawable.bg_shape_tips_purple)
LIGHT ->
layoutMessage.setBackgroundResource(R.drawable.bg_shape_tips_green)
RED ->
layoutMessage.setBackgroundResource(R.drawable.bg_shape_tips_red)
}
direction = builder.direction
resident = builder.resident
focusable = builder.focusable
shouldShowCloseIcon = builder.shouldShowCloseIcon
if (builder.arrowOffsetX > 0) {
arrowDistanceFromEdge = builder.arrowOffsetX
}
delayTime = builder.delayTime
showArrow(builder.style)
rootView.measure(View.MeasureSpec.UNSPECIFIED,
View.MeasureSpec.UNSPECIFIED)
when {
resident -> {
if (shouldShowCloseIcon && builder.onPositiveCallback == null) {
builder.onPositive(RR.getString(R.string.close), object:
TipsButtonCallback {
override fun onClick(action: Action) = Unit
})
}
showButton(rootView, builder)
rootView.measure(View.MeasureSpec.UNSPECIFIED,
View.MeasureSpec.UNSPECIFIED)
}
}
windowHeight = rootView.measuredHeight
windowWidth = rootView.measuredWidth
popupWindow = PopupWindow(rootView)
quickAction = builder.quickAction
if (builder.closeWhenTouchInside) {
rootView.setOnClickListener {
quickAction?.onQuickAction()
dismiss()
}
}
popupWindow?.width = windowWidth
popupWindow?.height = windowHeight
popupWindow?.setBackgroundDrawable(ColorDrawable(RR.getColor(R.color.transparent)))
popupWindow?.isFocusable = focusable
popupWindow?.isOutsideTouchable = true
popupWindow?.setOnDismissListener(builder.onDismissListener)
val closeWhenTouchOutside = builder.closeWhenTouchOutside
popupWindow?.setTouchInterceptor { _, event ->
when (event.action) {
MotionEvent.ACTION_OUTSIDE -> {
return@setTouchInterceptor true
}
MotionEvent.ACTION_DOWN -> {
val x = event.x.toInt()
val y = event.y.toInt()
/**
* 显示 button(close 按钮替换为 button)
* 只有一个按钮,居右显示;
* 两个时候在文字下方
*/
private fun showButton(rootView: ViewGroup, builder: Builder) {
if (builder.onPositiveCallback == null) return
builder.onPositiveCallback?.let {
rootView.buttonPositive.visibility = VISIBLE
rootView.buttonPositive.text = builder.positiveText
rootView.buttonPositive.setOnClickListener {
builder.onPositiveCallback?.onClick(Action.POSITIVE)
dismiss()
}
/**
* 外部动态更新 contentView(TextView)中内容
* */
fun updateContent(updateFunc: (View) -> Unit) {
contentView?.let {
updateFunc(it)
}
}
fun dismiss() {
if (isShowing()) {
animatorExit()
}
if (mTipCallback != null) {
removeWindowTouch()
}
}
init {
this.mPaint.color = foregroundColor
}
@Synchronized
private fun updatePath(bounds: Rect) {
mPath = Path()
when {
mDirection == RIGHT -> {
mPath?.apply {
moveTo(bounds.width().toFloat(), bounds.height().toFloat())
lineTo(0f, (bounds.height() / 2).toFloat())
lineTo(bounds.width().toFloat(), 0f)
lineTo(bounds.width().toFloat(), bounds.height().toFloat())
}
}
mDirection and BOTTOM != 0 -> {
mPath?.apply {
moveTo(0f, bounds.height().toFloat())
lineTo((bounds.width() / 2).toFloat(), 0f)
lineTo(bounds.width().toFloat(), bounds.height().toFloat())
lineTo(0f, bounds.height().toFloat())
}
}
mDirection == LEFT -> {
mPath?.apply {
moveTo(0f, 0f)
lineTo(bounds.width().toFloat(), (bounds.height() /
2).toFloat())
lineTo(0f, bounds.height().toFloat())
lineTo(0f, 0f)
}
}
mDirection and TOP != 0 -> {
mPath?.apply {
moveTo(0f, 0f)
lineTo((bounds.width() / 2).toFloat(),
bounds.height().toFloat())
lineTo(bounds.width().toFloat(), 0f)
lineTo(0f, 0f)
}
}
}
mPath?.close()
}
interface QuickAction {
fun onQuickAction()
}
interface TipsButtonCallback {
fun onClick(action: Action)
}
companion object {
const val KEEP_LEFT = 1
const val KEEP_RIGHT = 1 shl 1
const val TOP = 1 shl 2
const val BOTTOM = 1 shl 3
const val LEFT = 1 shl 4
const val RIGHT = 1 shl 5
/**
* 是否有正在展示的 Tips
*/
fun isKeepToolTipsShowing(): Boolean {
return keepToolTipsRef?.get()?.isShowing() == true
}
}
}