需求分析
如何创建一个大局告诉的弹窗?如下图所示。

从手机顶部划入,时间短停留后,再从顶部划出。
首要需求清晰的是:
1、这个弹窗的弹出逻辑纷歧定是当时界面编写的,比如用户上传文件,用户或许持续浏览其他页面的内容,但是监听文件是否上传完结还是在原来的Activity,但是Dialog的弹出是需求APP当时页面的上下文Context的。
2、Dialog弹窗有必要支撑手势,用户在Dialog上向上滑时,Dialog需求退出,点击时或许需求处理点击事线程的几种状态情。
一、Dialog的编写
/**
* 告诉的自定义Dialog
*/
class NotificationDialog(context: Context, var title: String, var content: String) :
Dialog(context, R.style.dialog_notifacation_top) {
private var mListener: OnNotificationClick? = null
private var mStartY: Float = 0F
private var mView: View? = null
private var mHeight: Int? = 0
init {
mView = LayoutInflater.from(context).inflate(R.layout.common_layout_notifacation, null)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mView!!)
window?.setGravity(Gravity.TOP)
val layoutParams = window?.attributes
layoutParams?.width = ViewGroup.LayoutParams.MATCH_PARENT
layoutParams?.height = ViewGroup.LayoutParams.WRAP_CONTENT
layoutParams?.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
window?.attributes = layoutParams
window?.setWindowAnimations(R.style.dialog_animation)
//按空白处不能取消
setCanceledOnTouchOutside(false)
//初始化界面数据
initData()
}
private fun initData() {
val tvTitle = findViewById<TextView>(R.id.tv_title)
val tvContent = findViewById<TextView>(R.id.tv_content)
if (title.isNotEmpty()) {
tvTitle.text = title
}
if (content.isNotEmpty()) {
tvContent.text = content
}
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
if (isOutOfBounds(event)) {
mStartY = event.y
}
}
MotionEvent.ACTION_UP -> {
if (mStartY > 0 && isOutOfBounds(event)) {
val moveY = event.y
if (abs(mStartY - moveY) >= 20) { //滑动超越20认定为滑动事情
//Dialog消失
} else { //认定为点击事情
//Dialog的点击事情
mListener?.onClick()
}
dismiss()
}
}
}
return false
}
/**
* 点击是否在规模外
*/
private fun isOutOfBounds(event: MotionEvent): Boolean {
val yValue = event.y
if (yValue > 0 && yValue <= (mHeight ?: (0 + 40))) {
return true
}
return false
}
private fun setDialogSize() {
mView?.addOnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
mHeight = v?.height
}
}
/**
* 显现Dialog但是不会主动退出
*/
fun showDialog() {
if (!isShowing) {
show()
setDialogSize()
}
}
/**
* 显现Dialog,3000毫秒后主动退出
*/
fun showDialogAutoDismiss() {
if (!isShowing) {
show()
setDialogSize()
//推迟3000毫秒后主动消失
Handler(Looper.getMainLooper()).postDelayed({
if (isShowing) {
dismiss()
}
}, 3000L)
}
}
//处理告诉的点击事情
fun setOnNotificationClickListener(listener: OnNotificationClick) {
mListener = listener
}
interface OnNotificationClick {
fun onClick()
}
}
Dialog的主题
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="dialog_notifacation_top">
<item name="android:windowIsTranslucent">true</item>
<!--设置布景透明-->
<item name="android:windowBackground">@android:color/transparent</item>
<!--设置dialog浮与activity上面-->
<item name="android:windowIsFloating">true</item>
<!--去掉布景模糊效果-->
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowNoTitle">true</item>
<!--去掉边框-->
<item name="android:windowFrame">@null</item>
</style>
<style name="dialog_animation" parent="@android:style/Animation.Dialog">
<!-- 进入时的动画 -->
<item name="android:windowEnterAnimation">@anim/dialog_enter</item>
<!-- 退出时的动画 -->
<item name="android:windowExitAnimation">@anim/dialog_exit</item>
</style>
</resources>
Dialog的动画
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="600"
android:fromYDelta="-100%p"
android:toYDelta="0%p" />
</set>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromYDelta="0%p"
android:toYDelta="-100%p" />
</set>
Dialog的布局,通CardView包裹一下就有立体暗影的效果java怎么读
<androidx.cardview.widget.CardView
android:id="@+id/cd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/size_15dp"
app:cardCornerRadius="@dimen/size_15dp"
app:cardElevation="@dimen/size_15dp"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/size_15dp"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="@dimen/font_14sp" android:textStyle="bold"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/size_15dp"
android:textColor="#333"
android:textSize="@dimen/font_12sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
二、获取当时显现的Activity的弱引证android手机
/**
* 前台Activity管理类
*/
class ForegroundActivityManager {
private var currentActivityWeakRef: WeakReference<Activity>? = null
companion object {
val TAG = "ForegroundActivityManager"
private val instance = ForegroundActivityManager()
@JvmStatic
fun getInstance(): ForegroundActivityManager {
return instance
}
}
fun getCurrentActivity(): Activity? {
var currentActivity: Activity? = null
if (currentActivityWeakRef != null) {
currentActivity = currentActivityWeakRef?.get()
}
return currentActivity
}
fun setCurrentActivity(activity: Activity) {
currentActivityWeakRef = WeakReference(activity)
}
}
监听一切Activity的生androidstudio安装教程命周期
class AppLifecycleCallback:Application.ActivityLifecycleCallbacks { companion object{ val TAG = "AppLifecycleCallback" } override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { //获取Activity弱引证 ForegroundActivityManager.getInstance().setCurrentActivity(activity) } override fun onActivityStarted(activity: Activity) { } override fun onActivityResumed(activity: Activity) { //获取Activity弱引证 ForegroundActivityManager.getInstance().setCurrentActivity(activity) } override fun onActivityPaused(activity: Activity) { } override fun onActivityStopped(activity: Activity) { } override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { } override fun onActivityDestroyed(activity: Activity) { } }
在Aappreciatepplication中注册
//注册Activity生命周期 registerActivityLifecycleCallbacks(AppLifecycleCallback())
三、封装和运用
/** * 告诉的管理类 * example: * //发体系告诉 * NotificationControlManager.getInstance()?.notify("文件上传完结", "文件上传完结,请点击检查概况") * //发运用内告诉 * NotificationControlManager.getInstance()?.showNotificationDialog("文件上传完结","文件上传完结,请点击检查概况", * object : NotificationControlManager.OnNotificationCallback { * override fun onCallback() { * Toast.makeText(this@MainActivity, "被点击了", Toast.LENGTH_SHORT).show() * } * }) */ class NotificationControlManager { private var autoIncreament = AtomicInteger(1001) private var dialog: NotificationDialog? = null companion object { const val channelId = "aaaaa" const val description = "描述信息" @Volatile private var sInstance: NotificationControlManager? = null @JvmStatic fun getInstance(): NotificationControlManager? { if (sInstance == null) { synchronized(NotificationControlManager::class.java) { if (sInstance == null) { sInstance = NotificationControlManager() } } } return sInstance } } /** * 是否翻开告诉 */ fun isOpenNotification(): Boolean { val notificationManager: NotificationManagerCompat = NotificationManagerCompat.from( ForegroundActivityManager.getInstance().getCurrentActivity()!! ) return notificationManager.areNotificationsEnabled() } /** * 跳转到体系设置页面去翻开告诉,留意在这之前应该有个Dialog提醒用户 */ fun openNotificationInSys() { val context = ForegroundActivityManager.getInstance().getCurrentActivity()!! val intent: Intent = Intent() try { intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS //8.0及今后版别运用这两个extra. >=API 26 intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName) intent.putExtra(Settings.EXTRA_CHANNEL_ID, context.applicationInfo.uid) //5.0-7.1 运用这两个extra. <= API 25, >=API 21 intent.putExtra("app_package", context.packageName) intent.putExtra("app_uid", context.applicationInfo.uid) context.startActivity(intent) } catch (e: Exception) { e.printStackTrace() //其他低版别或许异常情况,走该节点。进入APP设置界面 intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS intent.putExtra("package", context.packageName) //val uri = Uri.fromParts("package", packageName, null) //intent.data = uri context.startActivity(intent) } } /** * 发告诉 * @param title 标题 * @param content 内容 * @param cls 告诉点击后跳转的Activity,默以为null跳转到MainActivity */ fun notify(title: String, content: String, cls: Class<*>) { val context = ForegroundActivityManager.getInstance().getCurrentActivity()!! val notificationManager = context.getSystemService(AppCompatActivity.NOTIFICATION_SERVICE) as NotificationManager val builder: Notification.Builder val intent = Intent(context, cls) val pendingIntent: PendingIntent? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) } else { PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val notificationChannel = NotificationChannel(channelId, description, NotificationManager.IMPORTANCE_HIGH) notificationChannel.enableLights(true); notificationChannel.lightColor = Color.RED; notificationChannel.enableVibration(true); notificationChannel.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400) notificationManager.createNotificationChannel(notificationChannel) builder = Notification.Builder(context, channelId) .setSmallIcon(R.drawable.jpush_notification_icon) .setContentIntent(pendingIntent) .setContentTitle(title) .setContentText(content) } else { builder = Notification.Builder(context) .setSmallIcon(R.drawable.jpush_notification_icon) .setLargeIcon( BitmapFactory.decodeResource( context.resources, R.drawable.jpush_notification_icon ) ) .setContentIntent(pendingIntent) .setContentTitle(title) .setContentText(content) } notificationManager.notify(autoIncreament.incrementAndGet(), builder.build()) } /** * 显现运用内告诉的Dialog,需求自己处理点击事情。listener默以为null,不处理也能够。dialog会在3000毫秒后主动消失 * @param title 标题 * @param content 内容 * @param listener 点击的回调 */ fun showNotificationDialog( title: String, content: String, listener: OnNotificationCallback? = null ) { val activity = ForegroundActivityManager.getInstance().getCurrentActivity()!! dialog = NotificationDialog(activity, title, content) if (Thread.currentThread() != Looper.getMainLooper().thread) { //子线程 activity.runOnUiThread { showDialog(dialog, listener) } } else { showDialog(dialog, listener) } } /** * show dialog */ private fun showDialog( dialog: NotificationDialog?, listener: OnNotificationCallback? ) { dialog?.showDialogAutoDismiss() if (listener != null) { dialog?.setOnNotificationClickListener(object : NotificationDialog.OnNotificationClick { override fun onClick() = listener.onCallback() }) } } /** * dismiss Dialog */ fun dismissDialog() { if (dialog != null && dialog!!.isShowing) { dialog!!.dismiss() } } interface OnNotificationCallback { fun onCallback() } }
别的需求留意的点javaee是,由于dialog是推迟封闭的,或许用户java是什么意思立刻退出Activity线程安全,导致推迟时间届时dialog退出时报错,解决办法能够在BaseActivity的onDestroy办法中测验封闭Dialo线程安全g:
override fun onDestroy() { super.onDestroy() NotificationControlManager.getInstance()?.dismissDialog() }
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
评论(0)