当前位置: 首页 > news >正文

【Android】MVVM实战:仿Launcher加载与应用过滤

概述

基于MVVM架构,实现一个可配置的应用列表弹窗,支持包名过滤盒白名单机制,通过arrays.xml灵活配置要显示或隐藏的应用

技术栈

  • 架构模式:MVVM+DataBinding
  • 异步处理:RxJava+RxLifecycle
  • 组件:RxDialogFragment+RecyclerView
  • 配置管理:arrays.xml资源文件

效果

在这里插入图片描述

查找后数据为空时显示:
在这里插入图片描述

文件结构:
在这里插入图片描述

核心实现

数据模型 - AppInfo

应用信息实体类,使用BaseObservable支持数据绑定

public class AppInfo extends BaseObservable {private String name;private String packageName;private Drawable icon;private boolean selected;public AppInfo(String name, String packageName, Drawable icon) {this.name = name;this.packageName = packageName;this.icon = icon;this.selected = false;}@Bindablepublic String getName() {return name;}public void setName(String name) {this.name = name;notifyPropertyChanged(BR.name);}@Bindablepublic String getPackageName() {return packageName;}public void setPackageName(String packageName) {this.packageName = packageName;notifyPropertyChanged(BR.packageName);}@Bindablepublic Drawable getIcon() {return icon;}public void setIcon(Drawable icon) {this.icon = icon;notifyPropertyChanged(BR.icon);}@Bindablepublic boolean isSelected() {return selected;}public void setSelected(boolean selected) {this.selected = selected;notifyPropertyChanged(BR.selected);}
}

应用过滤 - AppFilter

核心过滤类,支持白名单和黑名单两种模式

public class AppFilter {/*** 过滤应用列表* @param apps 原始应用列表* @param context 上下文,用于获取过滤数组* @return 过滤后的应用列表*/public static List<ApplicationInfo> filterApps(List<ApplicationInfo> apps, Context context) {// 获取要过滤的包名数组String[] filterPackages = context.getResources().getStringArray(R.array.filter_packages);Set<String> filterSet = new HashSet<>(Arrays.asList(filterPackages));List<ApplicationInfo> filteredList = new ArrayList<>();for (ApplicationInfo app : apps) {if (shouldInclude(app, filterSet)) {filteredList.add(app);}}return filteredList;}private static boolean shouldInclude(ApplicationInfo app, Set<String> filterSet) {// 模式1:黑名单过滤//过滤系统应用/*boolean isSystemApp = (app.flags & ApplicationInfo.FLAG_SYSTEM) != 0;if (isSystemApp) {return false;}*/// 过滤指定的包名//return !filterSet.contains(app.packageName) && !app.packageName.contains("com.zt.");// 模式2:白名单过滤(只显示指定包名)return filterSet.contains(app.packageName);}
}

配置示例

<?xml version="1.0" encoding="utf-8"?>
<resources><string-array name="filter_packages"><item>com.android.vending</item><item>com.ubercab</item></string-array>
</resources>

ViewModel层 - AppListDialogViewModel

核心业务逻辑处理,管理应用列表数据和UI状态

public class AppListDialogViewModel extends BaseViewModel {public final ObservableField<String> title = new ObservableField<>("选择应用");public final ObservableBoolean showEmpty = new ObservableBoolean(false);public final ObservableBoolean showList = new ObservableBoolean(false);private final MutableLiveData<List<AppInfo>> appListLiveData = new MutableLiveData<>();private final MutableLiveData<AppInfo> appItemClickEvent = new MutableLiveData<>();private final MutableLiveData<Void> cancelClickEvent = new MutableLiveData<>();private AppInfo selectedApp;public AppListDialogViewModel(@NonNull Application application) {super(application);}/*** 加载应用列表(使用AppFilter进行过滤)*/public void loadApps() {showEmpty.set(false);showList.set(false);addSubscribe(io.reactivex.rxjava3.core.Observable.fromCallable(() -> {// 在IO线程执行耗时的应用列表加载和过滤PackageManager pm = getApplication().getPackageManager();List<ApplicationInfo> allApps = pm.getInstalledApplications(PackageManager.GET_META_DATA);// 使用AppFilter过滤应用List<ApplicationInfo> filteredApplicationInfos = AppFilter.filterApps(allApps, getApplication());// 转换为AppInfo列表List<AppInfo> apps = new ArrayList<>();for (ApplicationInfo appInfo : filteredApplicationInfos) {String appName = pm.getApplicationLabel(appInfo).toString();String packageName = appInfo.packageName;Drawable icon = pm.getApplicationIcon(appInfo);apps.add(new AppInfo(appName, packageName, icon));}return apps;}).subscribe(apps -> {appListLiveData.setValue(apps);// 更新UI状态boolean isEmpty = apps.isEmpty();showEmpty.set(isEmpty);showList.set(!isEmpty);}, throwable -> {showEmpty.set(true);getUC().getShowDialogEvent().setValue("加载应用列表失败: " + throwable.getMessage());}));}/*** 应用项点击事件*/public void onAppItemClick(AppInfo appInfo) {// 取消之前的选择if (selectedApp != null) {selectedApp.setSelected(false);}// 设置新的选择appInfo.setSelected(true);selectedApp = appInfo;appItemClickEvent.setValue(appInfo);}/*** 取消按钮点击*/public void onCancelClick() {cancelClickEvent.setValue(null);}/*** 获取选中的应用*/public AppInfo getSelectedApp() {return selectedApp;}/*** 设置对话框标题*/public void setTitle(String title) {this.title.set(title);}public MutableLiveData<List<AppInfo>> getAppListLiveData() {return appListLiveData;}public MutableLiveData<AppInfo> getAppItemClickEvent() {return appItemClickEvent;}public MutableLiveData<Void> getCancelClickEvent() {return cancelClickEvent;}
}

UI层 - AppListDialogFragment

对话框管理类,处理UI交互和生命周期

public class AppListDialogFragment extends BaseDialogFragment<DialogAppListBinding, AppListDialogViewModel> {private static final String ARG_TITLE = "title";private AppListAdapter adapter;private OnAppSelectedListener appSelectedListener;public static AppListDialogFragment newInstance(String title) {AppListDialogFragment fragment = new AppListDialogFragment();Bundle args = new Bundle();args.putString(ARG_TITLE, title);fragment.setArguments(args);return fragment;}@Overridepublic int initContentView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {return R.layout.dialog_app_list;}@Overridepublic int initVariableId() {return BR.viewModel;}@Overridepublic AppListDialogViewModel initViewModel() {return new ViewModelProvider(this).get(AppListDialogViewModel.class);}@Overrideprotected int getDialogWidth() {return (int) (getResources().getDisplayMetrics().widthPixels * 0.7);}@Overrideprotected int getDialogHeight() {return (int) (getResources().getDisplayMetrics().heightPixels * 0.6);}@Overridepublic void initData() {super.initData();adapter = new AppListAdapter(new ArrayList<>(), viewModel);GridLayoutManager layoutManager = new GridLayoutManager(getContext(), 4);binding.recyclerView.setLayoutManager(layoutManager);binding.recyclerView.setAdapter(adapter);Bundle args = getArguments();if (args != null) {String title = args.getString(ARG_TITLE, "选择应用");viewModel.setTitle(title);}viewModel.loadApps();}@Overridepublic void initViewObservable() {super.initViewObservable();viewModel.getAppListLiveData().observe(getViewLifecycleOwner(), adapter::setAppList);viewModel.getAppItemClickEvent().observe(getViewLifecycleOwner(), appInfo -> {appSelectedListener.onAppSelected(appInfo);dismiss();});// 监听取消按钮点击viewModel.getCancelClickEvent().observe(getViewLifecycleOwner(), v -> {if (appSelectedListener != null) {appSelectedListener.onCancel();}dismiss();});}@Overridepublic void onStart() {super.onStart();if (getDialog() != null && getDialog().getWindow() != null) {getDialog().getWindow().setLayout(getDialogWidth(), getDialogHeight());}}public void setOnAppSelectedListener(OnAppSelectedListener listener) {this.appSelectedListener = listener;}public interface OnAppSelectedListener {void onAppSelected(AppInfo appInfo);// 取消按钮void onCancel();}
}

布局

应用项布局(item_app.xml)

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><data><import type="android.view.View"/><variablename="appInfo"type="com.ys.myapplication.bean.AppInfo" /><variablename="viewModel"type="com.ys.myapplication.view.dialog.AppListDialogViewModel" /></data><LinearLayoutandroid:layout_width="100dp"android:layout_height="120dp"android:orientation="vertical"android:padding="12dp"android:onClick="@{() -> viewModel.onAppItemClick(appInfo)}"><!-- 应用图标 --><ImageViewandroid:id="@+id/ivIcon"android:layout_width="48dp"android:layout_height="48dp"android:src="@{appInfo.icon}"android:layout_gravity="center"android:contentDescription="@{appInfo.name}"/><!-- 应用名称 --><TextViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:textSize="16sp"android:textColor="@color/white"android:text="@{appInfo.name}"/></LinearLayout></layout>

对话框布局(dialog_app_list.xml)

<?xml version="1.0" encoding="utf-8"?>
<layout 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"><data><import type="android.view.View" /><variablename="viewModel"type="com.ys.myapplication.view.dialog.AppListDialogViewModel" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:background="@drawable/content_bg"android:padding="@dimen/dimen_20"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><!-- 标题 --><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="3"android:text="@{viewModel.title}"android:textSize="18sp"android:textStyle="bold"android:textColor="@color/white"android:gravity="center"android:layout_marginBottom="16dp" /><ImageViewandroid:layout_width="40dp"android:layout_height="40dp"android:src="@mipmap/btn_delete"android:layout_gravity="end"android:onClick="@{() -> viewModel.onCancelClick()}"/></LinearLayout><!-- 空状态 --><TextViewandroid:id="@+id/tvEmpty"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="暂无可用应用"android:textSize="16sp"android:textColor="@color/gray_666"android:visibility="@{viewModel.showEmpty ? View.VISIBLE : View.GONE}"android:layout_marginBottom="16dp" /><!-- 应用列表 --><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:visibility="@{viewModel.showList ? View.VISIBLE : View.GONE}"tools:listitem="@layout/item_app" /></LinearLayout></layout>

适配器 - AppListAdapter

public class AppListAdapter extends RecyclerView.Adapter<AppListAdapter.AppViewHolder> {private List<AppInfo> appList;private AppListDialogViewModel viewModel;public AppListAdapter(List<AppInfo> appList, AppListDialogViewModel viewModel) {this.appList = appList;this.viewModel = viewModel;}public void setAppList(List<AppInfo> appList) {this.appList = appList;notifyDataSetChanged();}@NonNull@Overridepublic AppViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {LayoutInflater inflater = LayoutInflater.from(parent.getContext());ItemAppBinding binding = ItemAppBinding.inflate(inflater, parent, false);return new AppViewHolder(binding);}@Overridepublic void onBindViewHolder(@NonNull AppViewHolder holder, int position) {AppInfo appInfo = appList.get(position);holder.bind(appInfo, viewModel);}@Overridepublic int getItemCount() {return appList != null ? appList.size() : 0;}static class AppViewHolder extends RecyclerView.ViewHolder {private final ItemAppBinding binding;public AppViewHolder(@NonNull ItemAppBinding binding) {super(binding.getRoot());this.binding = binding;}public void bind(AppInfo appInfo, AppListDialogViewModel viewModel) {binding.setAppInfo(appInfo);binding.setViewModel(viewModel);binding.executePendingBindings();}}
}

使用示例

private void showAppListDialog() {AppListDialogFragment dialog = AppListDialogFragment.newInstance("Application");dialog.setOnAppSelectedListener(new AppListDialogFragment.OnAppSelectedListener() {@Overridepublic void onAppSelected(AppInfo appInfo) {// 处理选中的应用String selectedPackageName = appInfo.getPackageName();try {Intent launchIntent = getPackageManager().getLaunchIntentForPackage(selectedPackageName);if (launchIntent != null) {startActivity(launchIntent);}} catch (Exception e) {SystemUtil.getToast(getApplicationContext(), "start app failed: " + e.getMessage());}}@Overridepublic void onCancel() {}});dialog.show(getSupportFragmentManager(),"tag");}

待优化

  • 性能优化:应用图标缓存、列表分页加载

附录

依赖
在这里插入图片描述
在这里插入图片描述

http://www.dtcms.com/a/593115.html

相关文章:

  • seowhy什么意思丹阳seo公司
  • 质量智能革命:SPC软件助力中国制造驶入高质量发展快车道
  • 步骤记录器广州搜索排名优化
  • 3分钟搞定,CI/CD工具Arbess安装和配置
  • 5G智慧矿山监控终端:引领矿山智能化新潮流 在科技浪潮汹涌澎湃的当下,矿山行业正处于智能化转型的关键转折点
  • oracle 11查询数据库锁
  • 网站后台模板免费海外网站服务器网址
  • Oracle dblink 连接TDSQL-PG,及乱码处理
  • [出现错误 2147942402 (0x80070002) (启动“ubuntu2004.exe”时)]
  • jdbc基础(工具类)
  • 生物电子实验箱 生物医学教学平台 嵌入式生物医学电子实验箱 医疗电子试验箱
  • 长沙做网站微联讯点很好wordpress百度云直链
  • YOLOv5(四):models/experimental.py
  • TCP/IP协议:互联网的基石与通信灵魂
  • 进程间消息通信RegisterWindowMessage(C++源码)
  • Redis进阶 - 数据结构底层机制
  • 基于YOLO11深度学习的电动车头盔检测系统【Python源码+Pyqt5界面+数据集+安装使用教程+训练代码】【附下载链接】
  • 在 React 生态中,有多个成熟的 虚拟列表 npm 库
  • 攻防世界-Misc-Test-flag-please-ignore
  • 研发管理知识库(2)华为管理模式和《华为基本法》
  • 网站开发项目任务html编辑器安卓版下载
  • python程序封装成exe文件
  • JavaEE 进阶第六期:Spring Boot 如何重塑后端开发
  • 郑州商品交易所计算机专业笔试题:矩阵列优先存储
  • TypeScript 中的单例模式
  • 工会网站群建设方案网站建设及售后服务的说明书
  • GAMS全攻略——覆盖LP/NLP/MIP/MINLP模型、人工智能算法、数学优化方法与程序调试等
  • 杭州金融网站建设网站网页设计在哪找
  • 六爻观测基础(一)——定位
  • 微网站开发要多少钱攀枝花网站建设兼职