依赖库:
// androidx
api 'androidx.appcompat:appcompat:1.1.0'
api 'androidx.core:core-ktx:1.3.0'
api 'androidx.constraintlayout:constraintlayout:1.1.3'
api "androidx.fragment:fragment-ktx:1.2.4"
api "androidx.lifecycle:lifecycle-extensions:2.2.0"
api "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
api "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
api "androidx.navigation:navigation-fragment-ktx:2.2.2"
api "androidx.navigation:navigation-ui-ktx:2.2.2"
api "androidx.viewpager2:viewpager2:1.1.0-alpha01"
api "androidx.legacy:legacy-support-v4:1.0.0"
//rx
api 'io.reactivex.rxjava2:rxjava:2.2.16'
api 'io.reactivex.rxjava2:rxandroid:2.1.1'
// webView
api 'com.github.delight-im:Android-AdvancedWebView:v3.0.0'
BaseMvvmActivity:
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Observer
import com.blankj.utilcode.util.ToastUtils
import com.etekcity.vesyncbase.widget.dialog.GlobalLoadingDialog
abstract class BaseMvvmActivity<VB : ViewDataBinding, VM : BaseViewModel> : AppCompatActivity() {
protected var binding: VB? = null
protected lateinit var viewModel: VM
private var viewModelId = 0
private val loadingDialog: GlobalLoadingDialog by lazy {
GlobalLoadingDialog.init(supportFragmentManager)
.setCancelableOutside(false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//页面接受的参数方法
initParam()
//私有的初始化Databinding和ViewModel方法
initViewDataBinding()
//初始化页面配置的方法
initView(savedInstanceState)
//私有的ViewModel与View的契约事件回调逻辑
registerUIChangeLiveDataCallBack()
//页面数据初始化方法
initData()
//页面事件监听的方法,一般用于ViewModel层转到View层的事件注册
initViewObservable()
}
override fun onDestroy() {
super.onDestroy()
binding?.unbind()
}
/**
* 注入绑定
*/
private fun initViewDataBinding() { //DataBindingUtil类需要在project的build中配置 dataBinding {enabled true }, 同步后会自动关联android.databinding包
binding = DataBindingUtil.setContentView(this, layoutId())
binding?.lifecycleOwner = this
viewModelId = initVariableId()
// viewModel =
viewModel = createViewModel(this)
//关联ViewModel
binding!!.setVariable(viewModelId, viewModel)
}
/**
* 初始化界面传递参数
*/
open fun initParam() {}
/**
* 初始化数据
*/
open fun initData() {}
/**
* 初始化界面观察者的监听
*/
open fun initViewObservable() {}
/**
* 初始化界面
*/
open fun initView(savedInstanceState: Bundle?) {}
/**
* =====================================================================
*/
/**
* 初始化根布局
*
* @return 布局layout的id
*/
abstract fun layoutId(): Int
/**
* 初始化ViewModel的id
*
* @return BR的id
*/
abstract fun initVariableId(): Int
/**
* 创建ViewModel 需子类必须实现
*
* @param activity
* @return VM
*/
abstract fun createViewModel(activity: FragmentActivity): VM
/**
* 处理自定义事件,子类重写根据Message code分类处理
*/
open fun handleEvent(msg: Message) {
}
//可供子类实现处理接口异常 子类实现返回true
open fun handleCloudError(event: CloudErrorEvent): Boolean {
return false
}
/**
* 注册 UI 事件
*/
private fun registerUIChangeLiveDataCallBack() {
viewModel.uiEvent.showLoadingDialog.observe(this, Observer {
showLoading()
})
viewModel.uiEvent.dismissLoadingDialog.observe(this, Observer {
dismissLoading()
})
viewModel.uiEvent.toastEvent.observe(this, Observer {
ToastUtils.showShort(it)
})
viewModel.uiEvent.msgEvent.observe(this, Observer {
handleEvent(it)
})
viewModel.uiEvent.finishEvent.observe(this, Observer {
finish()
})
viewModel.uiEvent.cloudErrorEvent.observe(this, Observer {
if (!handleCloudError(it)) {
if (!it.isProceed) {
ToastUtils.showShort(it.errorMessage)
it.isProceed = true
}
}
})
}
/**
* 打开等待框
*/
private fun showLoading() {
loadingDialog.show()
}
/**
* 关闭等待框
*/
private fun dismissLoading() {
if(loadingDialog.dialog != null && loadingDialog.dialog!!.isShowing)
loadingDialog.dismiss()
}
}
BaseViewModel:
import androidx.lifecycle.*
import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
abstract class BaseViewModel : ViewModel(), LifecycleObserver {
private var compositeDisposable: CompositeDisposable? = null
val uiEvent: UIEvent by lazy { UIEvent() }
/**
* UI事件
*/
inner class UIEvent {
val showLoadingDialog by lazy { SingleLiveEvent<String>() }
val dismissLoadingDialog by lazy { SingleLiveEvent<Void>() }
val toastEvent by lazy { SingleLiveEvent<String>() }
val finishEvent by lazy { SingleLiveEvent<Void>() }
// 接口请求异常事件
val cloudErrorEvent by lazy { MutableLiveData<CloudErrorEvent>() }
// 自定义传递事件
val msgEvent by lazy { SingleLiveEvent<Message>() }
}
@JvmOverloads
fun postMessageEvent(
code: Int = 0,
msg: String = "",
arg1: Int = 0,
arg2: Int = 0,
obj: Any? = null
) {
uiEvent.msgEvent.value = Message(code, msg, arg1, arg2, obj)
}
fun showLoading(msg: String? = null) {
if (msg.isNullOrEmpty()) {
uiEvent.showLoadingDialog.call()
} else {
uiEvent.showLoadingDialog.value = msg
}
}
fun dismissLoading() {
uiEvent.dismissLoadingDialog.call()
}
fun showToast(msg: String) {
uiEvent.toastEvent.value = msg
}
fun finish(){
uiEvent.finishEvent.call()
}
fun handleError(throwable: Throwable) {
uiEvent.cloudErrorEvent.postValue(CloudErrorEvent.handleError(throwable))
}
override fun onCleared() {
super.onCleared()
compositeDisposable?.clear()
}
private fun addSubscribe(disposable: Disposable) {
if (compositeDisposable == null) {
compositeDisposable = CompositeDisposable()
}
compositeDisposable!!.add(disposable)
}
fun <T> Observable<T>.addDisposable(): Observable<T> {
return this.doOnSubscribe {
addSubscribe(it)
}
}
fun <T> Observable<T>.subscribeExt(onNext: (it: T) -> Unit): Disposable {
return addDisposable().subscribe({
onNext(it)
}, {
handleError(it)
})
}
fun setMessageEvent(message: Message) {
uiEvent.msgEvent.value = message
}
}
SingleLiveEvent:
import android.util.Log
import androidx.annotation.MainThread
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.util.concurrent.atomic.AtomicBoolean
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
*
*
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
*
*
* Note that only one observer is going to be notified of changes.
*/
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Log.w(
"SingleLiveEvent",
"Multiple observers registered but only one will be notified of changes."
)
}
// Observe the internal MutableLiveData
super.observe(owner, Observer { t ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
@MainThread
override fun setValue(t: T?) {
pending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
value = null
}
}
Message:
class Message @JvmOverloads constructor(
val code: Int = 0,
val msg: String = "",
val arg1: Int = 0,
val arg2: Int = 0,
val obj: Any? = null
)
GlobalLoadingDialog:
import android.view.Gravity
import android.view.View
import android.view.animation.AnimationUtils
import android.widget.ImageView
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LifecycleOwner
import com.etekcity.vesyncbase.R
import com.etekcity.vesyncwidget.dialog.base.BaseDialog
import com.etekcity.vesyncwidget.dialog.base.ViewHandlerListener
import com.etekcity.vesyncwidget.dialog.base.ViewHolder
class GlobalLoadingDialog : BaseDialog<GlobalLoadingDialog>() {
@androidx.annotation.AnimRes
private var animationResource = R.anim.rotation
/**
* View Handler
* The management of the relevant state of the view is written here
*/
override fun viewHandler(): ViewHandlerListener? {
return object : ViewHandlerListener() {
override fun convertView(holder: ViewHolder, dialog: BaseDialog<*>) {
holder.getView<ImageView>(R.id.iv_loading).apply {
startAnimation(AnimationUtils.loadAnimation(context, animationResource))
}
}
}
}
override fun layoutRes(): Int = R.layout.layout_loading_dialog
override fun layoutView(): View? = null
fun setAnimation(@androidx.annotation.AnimRes animation: Int): GlobalLoadingDialog {
animationResource = animation
return this
}
companion object {
fun init(fragmentManager: FragmentManager): GlobalLoadingDialog {
val dialog = GlobalLoadingDialog()
dialog.setFragmentManager(fragmentManager)
dialog.setDimAmount(0.0f)
dialog.setBackgroundDrawableRes(0)
dialog.setWidthScale(0f)
dialog.setHeightScale(0f)
dialog.setKeepWidthScale(false)
dialog.setGravity(Gravity.CENTER)
return dialog
}
}
}
BaseDialog:
import android.content.Context
import android.content.DialogInterface
import android.content.res.Configuration
import android.graphics.Point
import android.os.Bundle
import android.os.Parcelable
import android.view.*
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.annotation.DrawableRes
import androidx.annotation.FloatRange
import androidx.annotation.LayoutRes
import androidx.annotation.StyleRes
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LifecycleOwner
import com.etekcity.vesyncwidget.R
import kotlinx.android.parcel.Parcelize
@Suppress("UNCHECKED_CAST")
abstract class BaseDialog<T : BaseDialog<T>> : DialogFragment() {
protected var baseParams: BaseDialogParams
protected var viewHandlerListener: ViewHandlerListener?
private var onDialogDismissListener: OnDialogDismissListener? = null
protected lateinit var mContext: Context
protected var dialogConfig: DialogConfig? = null
protected var level = DialogLevel.NORMAL
protected var cleanAllDialog: Boolean = false
init {
baseParams = BaseDialogParams().apply {
layoutRes = layoutRes()
view = layoutView()
}
viewHandlerListener = this.viewHandler()
}
@LayoutRes
protected abstract fun layoutRes(): Int
protected abstract fun layoutView(): View?
protected abstract fun viewHandler(): ViewHandlerListener?
open fun initView(view: View) {}
override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//Restore UI status
savedInstanceState?.let {
baseParams = it.getParcelable(KEY_PARAMS) ?: BaseDialogParams()
viewHandlerListener = savedInstanceState.getParcelable(KEY_VIEW_HANDLER)
onDialogDismissListener = savedInstanceState.getParcelable(KEY_DISMISS_LISTENER)
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
super.onCreateView(inflater, container, savedInstanceState)
//Clear the title of Android4.4
dialog?.requestWindowFeature(Window.FEATURE_NO_TITLE)
return when {
baseParams.layoutRes > 0 -> inflater.inflate(baseParams.layoutRes, container)
baseParams.view != null -> baseParams.view!!
else ->
throw IllegalArgumentException("请先设置LayoutRes或View!")
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewHandlerListener?.convertView(ViewHolder.create(view), this)
initView(view)
//Set open Keyboard
if (this.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT && baseParams.needKeyboardViewId != 0) {
val editText = view.findViewById<EditText>(baseParams.needKeyboardViewId)
editText.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
?: return
editText.isFocusable = true
editText.isFocusableInTouchMode = true
editText.requestFocus()
if (imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT)) {
editText.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}
})
}
}
//save UI state
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.apply {
putParcelable(KEY_PARAMS, baseParams)
putParcelable(KEY_VIEW_HANDLER, viewHandlerListener)
putParcelable(KEY_DISMISS_LISTENER, onDialogDismissListener)
}
}
override fun onStart() {
super.onStart()
//Get screen size
val point = Point()
val windowManager = activity?.getSystemService(Context.WINDOW_SERVICE) as? WindowManager
windowManager?.defaultDisplay?.getSize(point)
//Set window
dialog?.window?.let {
val params = it.attributes
params.dimAmount = baseParams.dimAmount
params.gravity = baseParams.gravity
it.attributes
//Set dialog width
when {
baseParams.widthScale > 0f -> {
if ((this.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && baseParams.keepWidthScale)
|| this.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
//横屏并且保持比例 或者 竖屏
params.width = (point.x * baseParams.widthScale).toInt()
}
}
baseParams.widthDp > 0f -> params.width = dp2px(mContext, baseParams.widthDp)
}
//Set dialog height
when {
baseParams.heightScale > 0f -> {
if ((this.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && baseParams.keepHeightScale)
|| this.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
//横屏并且保持比例 或者 竖屏
params.height = (point.y * baseParams.heightScale).toInt()
}
}
baseParams.heightDp > 0f -> params.height = dp2px(mContext, baseParams.heightDp)
}
//Set Window verticalMargin
params.verticalMargin = baseParams.verticalMargin
it.attributes = params
if (baseParams.backgroundDrawableRes == 0) {
it.setBackgroundDrawable(null)
} else {
it.setBackgroundDrawableResource(baseParams.backgroundDrawableRes)
}
it.setWindowAnimations(baseParams.animStyle)
}
//Set touch cancelable
if (!baseParams.cancelable) {
isCancelable = baseParams.cancelable
} else {
dialog?.setCanceledOnTouchOutside(baseParams.cancelableOutside)
}
}
fun setDialogLevel(level: DialogLevel) {
this.level = level
}
fun setCleanAll(cleanAll: Boolean) {
this.cleanAllDialog = cleanAll
}
override fun onCancel(dialog: DialogInterface) {
if(dialogConfig != null)
DialogManager.instance.remove(dialogConfig!!)
else
super.onCancel(dialog)
}
override fun onDismiss(dialog: DialogInterface) {
if (baseParams.needKeyboardViewId != 0) {
val editText = view?.findViewById<EditText>(baseParams.needKeyboardViewId)
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
?: return
imm.hideSoftInputFromWindow(editText?.windowToken, 0)
}
if(dialogConfig != null)
DialogManager.instance.remove(dialogConfig!!)
else
super.onDismiss(dialog)
onDialogDismissListener?.onDismiss(dialog)
}
protected fun setFragmentManager(fragmentManager: FragmentManager) {
baseParams.fragmentManager = fragmentManager
}
protected fun setLifecycleOwner(lifecycleOwner: LifecycleOwner?) {
baseParams.lifecycleOwner = lifecycleOwner
}
fun initDialogConfig () {
if(baseParams.lifecycleOwner == null)
return
if(dialogConfig == null) {
dialogConfig = DialogConfig.builder(this, baseParams.fragmentManager!! , baseParams.lifecycleOwner!!, baseParams.tag)
dialogConfig!!.dialogLevel = level
dialogConfig!!.cleanAllDialog = cleanAllDialog
}
}
/*** Set Params (start) [External call]***/
fun setTag(tag: String): T {
baseParams.tag = tag
return this as T
}
fun setDismissListener(onDialogDismissListener: OnDialogDismissListener): T {
this.onDialogDismissListener = onDialogDismissListener
return this as T
}
fun setGravity(gravity: Int): T {
baseParams.gravity = gravity
return this as T
}
/**
* Dialog occupies the proportion of the screen
* {setWidthScale()} priority is higher than {setWidthDp()}
* @param scale Float
* @return T
*/
fun setWidthScale(@FloatRange(from = 0.0, to = 1.0) scale: Float): T {
baseParams.widthScale = scale
return this as T
}
fun setWidthDp(dp: Float): T {
baseParams.widthDp = dp
return this as T
}
fun setHeightScale(@FloatRange(from = 0.0, to = 1.0) scale: Float): T {
baseParams.heightScale = scale
return this as T
}
fun setHeightDp(dp: Float): T {
baseParams.heightDp = dp
return this as T
}
/**
* Whether to maintain the {setWidthScale()} when the screen is rotated
* If not set {setWidthScale()}, This item does not take effect
* @param isKeep Boolean [Default false]
* @return T
*/
fun setKeepWidthScale(isKeep: Boolean): T {
baseParams.keepWidthScale = isKeep
return this as T
}
/**
* Whether to maintain the {setHeightScale()} when the screen is rotated
* If not set {setHeightScale()}, This item does not take effect
* @param isKeep Boolean [Default false]
* @return T
*/
fun setKeepHeightScale(isKeep: Boolean): T {
baseParams.keepHeightScale = isKeep
return this as T
}
fun setVerticalMargin(@FloatRange(from = 0.0, to = 0.1) verticalMargin: Float): T {
baseParams.verticalMargin = verticalMargin
return this as T
}
fun setCancelableAll(cancelable: Boolean): T {
baseParams.cancelable = cancelable
return this as T
}
fun setCancelableOutside(cancelableOutside: Boolean): T {
baseParams.cancelableOutside = cancelableOutside
return this as T
}
fun setBackgroundDrawableRes(@DrawableRes resId: Int): T {
baseParams.backgroundDrawableRes = resId
return this as T
}
fun setAnimStyle(@StyleRes animStyleRes: Int): T {
baseParams.animStyle = animStyleRes
return this as T
}
/**
* 设置dialog外部背景的透明度
*/
fun setDimAmount(@FloatRange(from = 0.0, to = 1.0) dimAmount: Float): T {
baseParams.dimAmount = dimAmount
return this as T
}
/**
* auto open keyboard, (only EditText)
* @param id Int EditTextView ID
* @return T
*/
fun setNeedKeyboardEditTextId(id: Int): T {
baseParams.needKeyboardViewId = id
return this as T
}
open fun show(): T {
initDialogConfig()
if(dialogConfig != null)
DialogManager.instance.show(dialogConfig!!)
else {
try {
//在每个add事务前增加一个remove事务,防止连续的add
baseParams.fragmentManager?.beginTransaction()?.remove(this)?.commit();
super.show(baseParams.fragmentManager!!, baseParams.tag)
} catch (e:Exception ) {
//同一实例使用不同的tag会异常,这里捕获一下
e.printStackTrace();
}
}
return this as T
}
/*** Set Params (end)***/
companion object {
private const val KEY_PARAMS = "key_params"
private const val KEY_VIEW_HANDLER = "view_handler"
private const val KEY_DISMISS_LISTENER = "dismiss_listener"
private fun dp2px(context: Context, dipValue: Float): Int {
val scale = context.resources.displayMetrics.density
return (dipValue * scale + 0.5f).toInt()
}
}
abstract class UnParcelableParams(var fragmentManager: FragmentManager? =null,
var lifecycleOwner: LifecycleOwner? = null,
var view: View? = null)
@Parcelize
class BaseDialogParams(
@LayoutRes var layoutRes: Int = 0,
var widthScale: Float = 0f,
var widthDp: Float = 0f,
var heightScale: Float = 0f,
var heightDp: Float = 0f,
var keepWidthScale: Boolean = false,
var keepHeightScale: Boolean = false,
var verticalMargin: Float = 0f,
var gravity: Int = Gravity.CENTER,
var tag: String = "LDialog",
var cancelable: Boolean = true,
var cancelableOutside: Boolean = true,
var backgroundDrawableRes: Int = R.drawable.def_dialog_bg,
var animStyle: Int = 0,
var needKeyboardViewId: Int = 0,
var dimAmount:Float = 0.3f
) : UnParcelableParams(), Parcelable
}
LoginActivity:
class LoginActivity : BaseMvvmActivity<LoginActivityLoginBinding, LoginViewModel>() {
override fun layoutId() = R.layout.login_activity_login
override fun initVariableId() = BR.viewModel
override fun createViewModel(activity: FragmentActivity): LoginViewModel {
return ViewModelProvider(activity).get(LoginViewModel::class.java)
}
override fun initView(savedInstanceState: Bundle?) {
super.initView(savedInstanceState)
// 设置状态栏颜色为白色
SystemBarHelper.immersiveStatusBar(window, 0.0.toFloat())
SystemBarHelper.setStatusBarDarkMode(this, true)
ClickUtils.applyGlobalDebouncing(btn_login) {
// 逻辑判断
viewModel.showLoading()
AliAuthHelper.getLoginToken(object : AliAuthLoginConfig() {
@SuppressLint("CheckResult")
override fun onAuthSuccess(token: String) {
LogUtils.d("onTokenSuccess token = $token")
viewModel.showLoading()
AccountManager.quickLogin(token)
.doFinally {
viewModel.dismissLoading()
}
.subscribe({
// 登录成功
}, {
LogUtils.d("error it = $it")
viewModel.handleError(it)
})
}
override fun onUiEventClick(viewType: Int) {
when (viewType) {
TYPE_CLICK_OTHER -> {
LogUtils.d("TYPE_CLICK_OTHER")
ActivityUtils.startActivity(LoginByPhoneActivity::class.java)
}
TYPE_CLICK_WECHAT -> {
LogUtils.d("TYPE_CLICK_WECHAT")
weChatLogin()
}
TYPE_CLICK_BY_PWD -> {
LogUtils.d("TYPE_CLICK_BY_PWD")
ActivityUtils.startActivity(LoginByPwdActivity::class.java)
}
}
}
override fun onStartAuthUISuccess(ret: String?) {
viewModel.dismissLoading()
}
override fun onAuthFailed(ret: String?) {
LogUtils.d("onTokenFailed ret = $ret")
viewModel.dismissLoading()
ActivityUtils.startActivity(LoginByPhoneActivity::class.java)
}
})
}
val loginTerm = getString(R.string.login_term)
val loginPolicy = getString(R.string.login_policy)
tv_term_and_policy.makeLinks(
Pair(loginTerm, View.OnClickListener {
gotoTermOfUser(loginTerm)
}),
Pair(loginPolicy, View.OnClickListener {
gotoPrivacyPolicy(loginPolicy)
})
)
otherWayLoginInit()
}
override fun initData() {
super.initData()
var bundle = intent.extras
var isShowTokenExpiredDialog =
bundle?.getBoolean(LOGIN_KEY_IS_TOKEN_EXPIRED, false) ?: false
if (isShowTokenExpiredDialog) {
showTokenExpiredDialog()
}
}
}
LoginViewModel:
class LoginViewModel:BaseViewModel() {
}
WebViewActivity:
import android.content.Intent
import android.graphics.Bitmap
import android.os.Bundle
import android.webkit.WebSettings
import com.etekcity.vesyncbase.R
import com.etekcity.vesyncbase.utils.SystemBarHelper
import com.etekcity.vesyncbase.widget.dialog.GlobalLoadingDialog
import im.delight.android.webview.AdvancedWebView
import kotlinx.android.synthetic.main.activity_webview.*
class WebViewActivity : BaseActivity(), AdvancedWebView.Listener {
private val loadingDialog: GlobalLoadingDialog by lazy {
GlobalLoadingDialog.init(supportFragmentManager)
.setCancelableOutside(false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView( R.layout.activity_webview)
// 设置状态栏颜色为白色
SystemBarHelper.immersiveStatusBar(window, 0.0.toFloat())
SystemBarHelper.setStatusBarDarkMode(this, true)
SystemBarHelper.setHeightAndPadding(this, toolbar)
toolbar.setNavigationOnClickListener {
finish()
}
tv_title.text = intent.getStringExtra(KEY_TITLE)
web_view.settings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
web_view.setListener(this, this)
web_view.loadUrl(intent.getStringExtra(KEY_URL))
loadingDialog.show()
}
override fun onPageFinished(url: String?) {
loadingDialog.dismiss()
}
override fun onPageError(errorCode: Int, description: String?, failingUrl: String?) {
loadingDialog.dismiss()
}
override fun onDownloadRequested(
url: String?,
suggestedFilename: String?,
mimeType: String?,
contentLength: Long,
contentDisposition: String?,
userAgent: String?
) {
}
override fun onExternalPageRequest(url: String?) {
}
override fun onPageStarted(url: String?, favicon: Bitmap?) {
}
override fun onResume() {
super.onResume()
web_view.onResume()
}
override fun onPause() {
web_view.onPause()
super.onPause()
}
override fun onDestroy() {
web_view.onDestroy()
super.onDestroy()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
web_view.onActivityResult(requestCode, resultCode, data)
}
override fun onBackPressed() {
if (!web_view.onBackPressed()) {
return
}
super.onBackPressed()
}
companion object {
const val KEY_TITLE = "web_view_title"
const val KEY_URL = "web_view_url"
}
}
webViewActivity布局:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fafafa">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/white"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="@drawable/icon_return_black"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
<TextView
android:id="@+id/tv_title"
style="@style/TextAppearance.Medium.ToolbarTitle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:layout_marginEnd="?attr/actionBarSize" />
</androidx.appcompat.widget.Toolbar>
<im.delight.android.webview.AdvancedWebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar" />
</androidx.constraintlayout.widget.ConstraintLayout>