Android Window浮窗UI組件使用JetPack

目前接手的一個業務,應用不是用Activity/Fragment作為界面組件,而是用Window浮窗的形式顯示,并且浮窗有很多種類型,每一種類型對應一類業務。那么怎么使用Jatpack的相關特性來設計架構并提高開發效率呢?分下面幾個模塊做分析:

ViewModel

通常在使用ViewModel的時候,是跟Activity/Fragment對應起來的,分別實例化一個對象,這個時候,ViewModel是存在Activity的ViewModelStore里面的,而且會監聽此Activity的生命周期,適時銷毀。
但是在沒有Activity的情況下,并且數據需要全局監聽的時候,可以創建一個全局的ViewModel,設計成單例模式的類,它的生命周期是伴隨應用的整個生命周期,如下:

object MainViewModel : ViewModel() {/*** 語音喚醒的狀態*/var mWakeUp = MutableLiveData<Int>()
}

其實在這里,已經可以不用去實現ViewModel這個類了,定義一個普通的單例模式類就行了:

object MainViewModel {/*** 語音喚醒的狀態*/var mWakeUp = MutableLiveData<Int>()
}

LiveData

MainViewModel中的LiveData主要分為兩大類,一類是全局的數據監聽,一類是針對于特定浮窗中使用到的數據。
第一類全局數據,可以使用全局監聽,它的數據監聽在應用整個生命周期都會有效:

// 全局檢測,不帶LifeCycleOwnerMainViewModel.mWakeUp.observeForever {Log.d(TAG, "wakeUp change $it")refreshWindowShown()}

第二類數據就是對應顯示浮窗的數據,它應該是需要帶生命周期的,即在彈窗顯示的時候才更新數據,而在彈窗消失后,不用再更新數據,避免資源的浪費和內存的泄漏:

class ShowWindow(context: Context) : BaseWindow(context){MainViewModel.mWindowContent.observe(this) {Log.d(TAG, "receive mWindowContent$it")binding.contentTv.text = it}
}

這里就有一個問題,Activity是接入了LifeCycleOwner的,但是Window浮窗是沒有接入的,而LiveData在訂閱非全局的觀察者時,需要有LifeCycleOwner參與,時候那應該怎么辦呢?

LifeCycleOwner

LifeCycleOwner在LifeCycle的設計策略中扮演的是生命周期提供者的角色,一個UI組件想要接入到LifeCycle的一系列規則中,就需要實現LifeCycleOwner,比如我們熟知的ComponentActivity就是實現了LifecycleOwner,并且通過mLifecycleRegistry去做的生命周期的關聯:

public class ComponentActivity extends Activity implementsLifecycleOwner,KeyEventDispatcher.Component {private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);@SuppressLint("RestrictedApi")@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);ReportFragment.injectIfNeededIn(this);}
}

高版本的SDK,是通過ReportFragment監聽生命周期,然后再通過mLifecycleRegistry去設置。
同理,我們的Window浮窗想要去把自身的生命周期導入到LifeCycle中,也是需要實現LifecycleOwner,然后通過mLifecycleRegistry去關聯生命周期,如下:

abstract class BaseWindow(val context : Context) : LifecycleOwner {private val lifecycleRegistry = LifecycleRegistry(this)open fun show() {lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)}open fun hide() {lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)}
}

即在show的時候,去開啟生命周期,而在hide的時候去結束生命周期。這樣,內部關聯的LiveData在感知到Window浮窗的生命周期onStart后,就會開啟數據的更新;在感知到onDestroy的時候去移除觀察者,結束數據更新,如下:

// 處理STARTED事件
@Override
boolean shouldBeActive() {return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);}// 處理 DESTROYED事件
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {removeObserver(mObserver); // 自動解綁return;}// 其他狀態處理...
}

ViewBinding

ViewBinding在Window浮窗的使用沒有任何障礙,通過配置gradle打開ViewBinding功能:

buildFeatures {viewBinding = true}

然后就可以在對應的浮窗中使用ViewBinding:

class ShowWindow(context: Context) : BaseWindow(context){private val binding : ShowwindowBinding by lazy { ShowwindowBinding.inflate(LayoutInflater.from(context)) }
}

這里的context是傳入的ApplicationContext,同樣是沒問題的。
這里想說一下基類和子類怎么處理Viewbinding。比如這一類浮窗中,它們有一些共同的界面邏輯,只是其中一塊顯示區域的內容有差距。這時候,我們可以抽取一個BaseWindow,并在BaseWindow中實現公共的UI和邏輯,同時在界面布局中預留出子類的顯示區域。這樣在BaseWindow和子類Window都可以使用ViewBinding。
也可以理解為,只要有一個layout布局文件,就對應一個ViewBinding類。具體的實現如下:
Base類的布局文件預留一個子類的內容顯示區域,不同的子類會填充不同的內容:

<FrameLayoutandroid:id="@+id/content_layout"android:layout_width="870dp"android:layout_height="match_parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintRight_toRightOf="parent"/>

Base類的代碼實現:

abstract class BaseWindow(context: Context){
private val binding : BaseWindowBinding by lazy { BaseWindowBinding.inflate(LayoutInflater.from(context)) }override fun getView(): View {if (binding.contentLayout.childCount == 0) {binding.contentLayout.addView(getContentLayout())}return binding.root}//預留給子類實現abstract fun getContentLayout() : View

在子類的實現中,使用ViewBindiing加載自己的布局模塊就好,并把自己的根布局通過getContentLayout方法返回。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/898623.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/898623.shtml
英文地址,請注明出處:http://en.pswp.cn/news/898623.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于WebRtc,GB28181,Rtsp/Rtmp,SIP,JT1078,H265/WEB融合視頻會議接入方案

智能融合視頻會議系統方案—多協議、多場景、全兼容的一站式視頻協作平臺 OvMeet,LiveMeet針對用戶?核心痛點實現功能與用戶價值 &#xff0c;Web平臺實現MCU多協議&#xff0c;H265/H264等不同編碼監控&#xff0c;直播&#xff0c;會議&#xff0c;調度資源統一融合在一套界…

深入淺出理解LLM PPO:基于verl框架的實現解析之一

1. 寫在前面 強化學習(Reinforcement Learning,RL)在大型語言模型(Large Language Model,LLM)的訓練中扮演著越來越重要的角色。特別是近端策略優化(Proximal Policy Optimization,PPO)算法,已成為對齊LLM與人類偏好的主流方法之一。本文將基于verl框架(很多復刻De…

卷積神經網絡 - 匯聚層

卷積神經網絡一般由卷積層、匯聚層和全連接層構成&#xff0c;本文我們來學習匯聚層。 匯聚層(Pooling Layer)也叫子采樣層(Subsampling Layer)&#xff0c;其作用是進 行特征選擇&#xff0c;降低特征數量&#xff0c;從而減少參數數量。 卷積層雖然可以顯著減少網絡中連接的…

vue使用element-ui自定義樣式思路分享【實操】

前言 在使用第三方組件時&#xff0c;有時候組件提供的默認樣式不滿足我們的實際需求&#xff0c;需要對默認樣式進行調整&#xff0c;這就需要用到樣式穿透。本篇文章以vue3使用element-ui的Tabs組件&#xff0c;對Tabs組件的添加按鈕樣式進行客制化為例。 確定需要修改的組…

【工具分享】vscode+deepseek的接入與使用

目錄 第一章 前言 第二章 獲取Deepseek APIKEY 2.1 登錄與充值 2.2 創建API key 第三章 vscode接入deepseek并使用 3.1 vscode接入deepseek 3.2 vscode使用deepseek 第一章 前言 deepseek剛出來時有一段時間余額無法充值&#xff0c;導致小編沒法給大家發完整的流程&…

【藍橋杯速成】| 9.回溯升級

題目一&#xff1a;組合綜合 問題描述 39. 組合總和 - 力扣&#xff08;LeetCode&#xff09; 給你一個 無重復元素 的整數數組 candidates 和一個目標整數 target &#xff0c;找出 candidates 中可以使數字和為目標數 target 的 所有 不同組合 &#xff0c;并以列表形式返…

【C++進階】深入探索類型轉換

目錄 一、C語言中的類型轉換 1.1 隱式類型轉換 1.2. 顯式類型轉換 1.3.C語言類型轉換的局限性 二、C 類型轉換四劍客 2.1 static_cast&#xff1a;靜態類型轉換&#xff08;編譯期檢查&#xff09; 2.2 dynamic_cast&#xff1a;動態類型轉換&#xff08;運行時檢查&…

代碼隨想錄_動態規劃

代碼隨想錄 動態規劃 509.斐波那契數 509. 斐波那契數 斐波那契數 &#xff08;通常用 F(n) 表示&#xff09;形成的序列稱為 斐波那契數列 。該數列由 0 和 1 開始&#xff0c;后面的每一項數字都是前面兩項數字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n…

計算機基礎:編碼03,根據十進制數,求其原碼

專欄導航 本節文章分別屬于《Win32 學習筆記》和《MFC 學習筆記》兩個專欄&#xff0c;故劃分為兩個專欄導航。讀者可以自行選擇前往哪個專欄。 &#xff08;一&#xff09;WIn32 專欄導航 上一篇&#xff1a;計算機基礎&#xff1a;編碼02&#xff0c;有符號數編碼&#xf…

設計模式(創建型)-單例模式

摘要 在軟件開發的世界里&#xff0c;設計模式是開發者們智慧的結晶&#xff0c;它們為解決常見問題提供了經過驗證的通用方案。單例模式作為一種基礎且常用的設計模式&#xff0c;在許多場景中發揮著關鍵作用。本文將深入探討單例模式的定義、實現方式、應用場景以及可…

基于FPGA頻率、幅度、相位可調的任意函數發生器(DDS)實現

基于FPGA實現頻率、幅度、相位可調的DDS 1 摘要 直接數字合成器( DDS ) 是一種通過生成數字形式的時變信號并進行數模轉換來產生模擬波形(通常為正弦波)的方法,它通過數字方式直接合成信號,而不是通過模擬信號生成技術。DDS主要被應用于信號生成、通信系統中的本振、函…

本地JAR批量傳私服

在有網絡隔離的環境下&#xff0c;Maven項目如果沒有搭建私服就得把用到的通用組件通過U盤在每個組員間拷貝來拷貝去。非常的麻煩跟低效。搭建私服&#xff0c;如果通用組件很多的時候手工一個一個上傳更是非常的麻煩跟低效&#xff1b; 我就遇上這問題&#xff0c;跟A公司合作…

【ROS實戰】02-ROS架構介紹

1. 簡介 你是否曾有過這樣的疑問&#xff1a;我按照文檔安裝了ROS&#xff0c;依照要求寫了一些示例節點&#xff08;node&#xff09;、消息&#xff08;msg&#xff09;和話題&#xff08;topic&#xff09;&#xff0c;但覺得過程既麻煩又繁瑣。也許你開始懷疑&#xff1a;…

LeetCode算法題(Go語言實現)_07

題目 給你一個整數數組 nums&#xff0c;返回 數組 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘積 。 題目數據 保證 數組 nums之中任意元素的全部前綴元素和后綴的乘積都在 32 位 整數范圍內。 請 不要使用除法&#xff0c;且在 O(n) 時間復…

網絡華為HCIA+HCIP 網絡編程自動化

telnetlib介紹 telnetlib是Python標準庫中的模塊。它提供了實現Telnet功能的類telnetlib.Telnet。這里通過調用telnetlib.Telnet類里的不同方法實現不同功能。 配置云

查看GPU型號、大小;CPU型號、個數、核數、內存

GPU型號、大小 nvidia-smiCPU型號 cat /proc/cpuinfo | grep model name | uniqCPU個數 cat /proc/cpuinfo | grep "physical id" | uniq | wc -lCPU核數 cat /proc/cpuinfo | grep "cpu cores" | uniqCPU內存 cat /proc/meminfo | grep MemTotal參考…

Docker與K8S是什么該怎么選?

用了很久的容器化&#xff0c;最近突然看到一個問題問&#xff1a; docker和K8S究竟有什么區別&#xff0c;到底該怎么選&#xff1f;我認真思考了一會&#xff0c;發現一時間還真說不明白&#xff0c;于是就研究了一段時間發布今天的博文&#xff01; Docker vs Kubernetes&a…

Android Handler 通過線程安全的 MessageQueue 和底層喚醒機制實現跨線程通信

目錄 一、MessageQueue 的線程安全實現 1. 消息隊列的同步鎖&#xff08;synchronized&#xff09; 2. 消息順序與延時處理 二、底層喚醒機制&#xff1a;從 Java 到 Linux 內核 1. 消息插入后的喚醒邏輯 2. Native 層實現&#xff08;基于 Linux 的 eventfd 和 epoll&am…

關于 2>/dev/null 的作用以及機理

每個進程都有三個標準文件描述符&#xff1a;stdin&#xff08;標準輸入&#xff09;、stdout&#xff08;標準輸出&#xff09;和stderr&#xff08;標準錯誤&#xff09;。默認情況下&#xff0c;stderr會輸出到終端。使用2>可以將stderr重定向到其他地方&#xff0c;比如…

MySQL中的鎖機制:從全局鎖到行級鎖

目錄 1. 鎖的基本概念 2. 全局鎖 2.1 全局鎖的定義 2.2 全局鎖的類型 2.3 全局鎖的使用場景 2.4 全局鎖的實現方式 2.5 全局鎖的優缺點 2.6 全局鎖的優化 3. 表級鎖 3.1 表級鎖的類型 3.2 表級鎖的使用場景 3.3 表級鎖的優缺點 4. 意向鎖&#xff08;Intention Lo…