kotlin开发经验-弹窗与viewModel

本文分享了一种将用户菜单实现为PopupWindow的方法,该菜单在视频活动上方显示。通过设置可获焦和背景,使得能够响应返回键。UI布局、适配器初始化和监听器配置也在文中提及。网络请求使用协程并在ViewModel中处理。

一个用户菜单的需求,本来是想单独做成一个活动的,不过由于需要在按菜单键时显示在视频活动的上层,最终决定是做成一个PopupWindow,依托于视频活动的上下文对象。

kotlin和java的写法有些差别,另外根据情况需要入参mContext,
基本架构用到了databinding和viewmodel以及协程,根据需求需要网络请求,请求放在协程io里。
PopupWindow里没有oncreate方法,但是kotlin有init方法,可以做初始化的一些工作。
class UserPop(var mContext: AppCompatActivity?, width: Int = ViewGroup.LayoutParams.MATCH_PARENT, height: Int = ViewGroup.LayoutParams.MATCH_PARENT) : PopupWindow(width, height) {
	private var mRoot: View?=null
	private var binding: PopUserMenuBinding? = PopUserMenuBinding
            .inflate(mContext!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater, null, false)

 	private var viewModel: PopViewModel?
 	private var privacyVM: PrivacyViewModel?

    private var mLifecycleOwner: LifecycleOwner? = mContext!!
    private var userGlobal: ViewTreeObserver.OnGlobalLayoutListener?=null


    private var mLaunchingJob: Job? = null


    init {
        binding!!.lifecycleOwner = mLifecycleOwner
        contentView = binding!!.root
        viewModel = ViewModelProvider(mContext!!,
                BaseViewModelFactory(MyApplication.mApplication,
                        MyApplication.mApplication.getmRepository()))[PopViewModel::class.java]

        privacyVM=ViewModelProvider(mContext!!, 		
        		ViewModelProvider.AndroidViewModelFactory(MyApplication.mApplication))[PrivacyViewModel::class.java]
        initView()
        observeData()
        getLoginInfo()
        getVersion()
        getVipDisCount(System.currentTimeMillis())        //会员h5

        //历史和收藏改用Flow方式获取
//        getRecentData()
//        getCollectionData()
    }

    private fun observeData() {
		 viewModel!!.vipDetail.observe(mLifecycleOwner!!){
            if (it.code==0){
              ...
            }
        }
	}
	
    private fun getVipDisCount(time: Long){
        mLifecycleOwner!!.lifecycleScope.launch {
            viewModel!!.getVipDiscount(mContext!!, time)
        }
    }
    
	自定义弹窗的显示位置showAtLocation
    fun show(root: View?) {
        mRoot=root
        if (root != null) {
            try {
                showAtLocation(root, Gravity.CLIP_VERTICAL, 0, ConvertUtil.dip2px(326f))
                LiveLog.log(LiveLog.LOG_LIVE, tag, "pop44")
            } catch (e: Exception) {
                val handler = Handler(Looper.getMainLooper())
                handler.postDelayed({
                    showAtLocation(root, Gravity.CLIP_VERTICAL, 0, ConvertUtil.dip2px(326f))
                    LiveLog.log(LiveLog.LOG_LIVE, tag, "pop51")
                }, 1000)
            }
        } else {
            LiveLog.log(LiveLog.LOG_LIVE, tag, "pop58")
        }

    }
	**
	弹窗消失时取消订阅的Observer, 释放变量,取消未完成的后台协程
	**
    override fun dismiss() {
        super.dismiss()
        //remove callback
        removeGlobalListener()
        privacyVM!!.h5Result.removeObservers(mLifecycleOwner!!)
        viewModel!!.recentResults.removeObservers(mLifecycleOwner!!)
        viewModel!!.collectionResults.removeObservers(mLifecycleOwner!!)
        if (mLaunchingJob!=null&&mLaunchingJob!!.isActive){
            mLaunchingJob!!.cancel()
            mLaunchingJob=null
        }
        viewModel=null
        mRoot=null
        binding=null
        mLifecycleOwner=null
        mContext=null
        LiveLog.log(LiveLog.LOG_LIVE, tag, "dismiss")
    }

}

管理viewModel生成的工厂类

class BaseViewModelFactory(private val application: Application, private val repository:DataRepository) : ViewModelProvider.AndroidViewModelFactory(application) {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T = with(modelClass){
        when{
            isAssignableFrom(PopViewModel::class.java)->PopViewModel(repository)
            else->{
                throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
            }
        }
    }as T
}

initView()中设置可获焦,设置背景以响应返回键。
设置UI,初始化adapter和设置监听

    private fun initView() {
        isFocusable = true

        binding?.loginIcon= ContextCompat.getDrawable(mContext!!, R.drawable.login_default)
        binding?.loginTitle=mContext!!.getString(R.string.user_card_mine_title)
        binding?.loginDetail=mContext!!.getString(R.string.user_card_mine_detail)
        binding?.versionCode="* * *"
        val info=OnLineUtils.getMemberDetailDTO()
        if(info!=null&& !info.copyWriting.isNullOrEmpty()){
            binding?.vipTitle=info.name
            binding?.vipDetail=info.copyWriting
        }else{
            binding?.vipTitle="——"
            binding?.vipDetail=mContext!!.getString(R.string.user_card_vip_detail)
        }
        val dw = ColorDrawable(0x00000000)
        setBackgroundDrawable(dw) //设置背景可使popwindow响应返回键

        /*    binding!!.bg.isFocusable=true
            binding!!.bg.setOnKeyListener { v, keyCode, event ->
                if (event.action == KeyEvent.ACTION_DOWN && keyCode==KeyEvent.KEYCODE_BACK){
                    dismiss()
                    return@setOnKeyListener true
                }else false
            }*/
        //recent
        recentAdapter= HistoryAdapter(this, R.layout.list_card, HistoryAdapter.DiffCallBack())

        //collection
        collectionAdapter = FavoriteAdapter(this, R.layout.list_card, FavoriteAdapter.DiffCallBack())

        binding?.recent?.recent_part_recyclerview?.apply {
            adapter = recentAdapter
            layoutManager = LinearLayoutManager(mContext!!, LinearLayoutManager.HORIZONTAL, false)
            addItemDecoration(CardItemDecoration(0, ConvertUtil.dip2px(cardItemSpacing)))

            setSelectedItemOffset(offsetStart, offsetEnd)

        }

         binding?.collection?.collection_recyclerview?.apply {
            adapter = collectionAdapter
            layoutManager = LinearLayoutManager(mContext!!, LinearLayoutManager.HORIZONTAL, false)
            addItemDecoration(CardItemDecoration(0, ConvertUtil.dip2px(cardItemSpacing)))

            setSelectedItemOffset(offsetStart, offsetEnd)
        }

        //加载中占位
        recentAdapter.replaceData(listOf(History("-1000", mContext!!.getString(R.string.loading), mContext!!.getString(R.string.loading))))
        collectionAdapter.replaceData(listOf(Favorite("-1000", mContext!!.getString(R.string.loading), mContext!!.getString(R.string.loading))))

        initKeyListener()
        initClickListener()
        initFocusListener()
     }

弹窗中的网络请求放在协程中,通过viewmodel中的方法调用。

    suspend fun getVipDiscount(context: Context, timeStamp:Long){
        withContext(Dispatchers.IO){
            val resultString=async { NetRequest().postVipDetailRequest(context = context, timestamp = timeStamp) }
            val result=resultString.await()

            try {
                LiveLog.log(LiveLog.LOG_LIVE," getVipDiscount result: $result")
                val memberDetail=JSONObject.parseObject(result, MemberDetailResult::class.java)
                if (memberDetail?.code==0&&memberDetail.data!=null) _vipDetail.postValue(memberDetail)

            }catch (e:Throwable){
                e.printStackTrace()
            }

        }
    }
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值