Kotlin中SharedPreference存取应用信息及图标隐藏持久化问题
解决GridView隐藏应用图标并持久化设置的问题
你已经实现了临时删除图标的功能,但缺少持久化存储隐藏应用的逻辑,导致重启后所有应用又重新显示。下面我会一步步帮你修改代码,实现真正的持久化隐藏:
1. 新增SharedPreferences工具方法
我们需要用SharedPreferences存储要隐藏的应用包名集合,这样重启后能读取并过滤列表。在你的Activity里添加这些方法:
private val PREFS_NAME = "HiddenAppsPrefs" private val HIDDEN_APPS_KEY = "hidden_apps" // 保存隐藏的应用包名集合 private fun saveHiddenApps(hiddenPackages: MutableSet<String>) { getSharedPreferences(PREFS_NAME, MODE_PRIVATE) .edit() .putStringSet(HIDDEN_APPS_KEY, hiddenPackages) .apply() } // 读取已隐藏的应用包名集合 private fun getHiddenApps(): MutableSet<String> { return getSharedPreferences(PREFS_NAME, MODE_PRIVATE) .getStringSet(HIDDEN_APPS_KEY, mutableSetOf()) ?: mutableSetOf() }
2. 修改应用加载逻辑,过滤已隐藏的应用
修改checkForLaunchIntent方法,在添加应用前检查包名是否在隐藏列表里:
private fun checkForLaunchIntent(list: List<ApplicationInfo>): List<ApplicationInfo> { val hiddenPackages = getHiddenApps() val applist = ArrayList<ApplicationInfo>() for (info in list) { try { if (null != packageManager!!.getLaunchIntentForPackage(info.packageName) && !hiddenPackages.contains(info.packageName)) { // 过滤隐藏的应用 applist.add(info) } } catch (e: Exception) { e.printStackTrace() } } Collections.sort(applist, ApplicationInfo.DisplayNameComparator(packageManager)) return applist }
3. 调整长按删除逻辑,持久化保存隐藏的包名
之前你用mSelected存的是position字符串,这很不稳定(比如应用排序变化后position会变),应该直接存包名。同时删除时要把包名加入隐藏集合并保存:
首先修改mSelected的类型:
private var mSelected: MutableSet<String> = mutableSetOf() // 改为存储选中的包名
然后修改长按事件的逻辑:
grid!!.onItemLongClickListener = AdapterView.OnItemLongClickListener { parent, view, position, id -> val app = applist!![position] val packageName = app.packageName if (mSelected.contains(packageName)) { mSelected.remove(packageName) view.setBackgroundColor(Color.TRANSPARENT) } else { mSelected.add(packageName) view.setBackgroundColor(Color.LTGRAY) } button3.setOnClickListener { val builder1 = AlertDialog.Builder(this@appsActivity) builder1.setMessage("Are you sure you want to hide selected apps?") builder1.setCancelable(true) builder1.setPositiveButton("Yes") { dialog, id -> // 获取已有的隐藏集合,加入当前选中的包名 val hiddenApps = getHiddenApps() hiddenApps.addAll(mSelected) saveHiddenApps(hiddenApps) // 重新加载应用列表并刷新适配器 LoadApplications().execute() mSelected.clear() // 清空选中状态 } builder1.setNegativeButton("No") { dialog, id -> dialog.cancel() mSelected.clear() // 重置所有选中项的背景 for (i in 0 until grid!!.count) { grid!!.getChildAt(i)?.setBackgroundColor(Color.TRANSPARENT) } } val alert11 = builder1.create() alert11.show() } true }
4. 移除冗余的deleteSelectedItems方法
现在删除逻辑已经整合到对话框确认流程里,之前的deleteSelectedItems方法可以直接删除,避免逻辑混乱。
修改后的完整Activity代码
private var applist: List<ApplicationInfo>? = null private var listadaptor: ApplicationAdapter? = null private var grid: GridView? = null private var mSelected: MutableSet<String> = mutableSetOf() // 存储选中的包名 var context: Activity = this // SharedPreferences相关常量和方法 private val PREFS_NAME = "HiddenAppsPrefs" private val HIDDEN_APPS_KEY = "hidden_apps" private fun saveHiddenApps(hiddenPackages: MutableSet<String>) { getSharedPreferences(PREFS_NAME, MODE_PRIVATE) .edit() .putStringSet(HIDDEN_APPS_KEY, hiddenPackages) .apply() } private fun getHiddenApps(): MutableSet<String> { return getSharedPreferences(PREFS_NAME, MODE_PRIVATE) .getStringSet(HIDDEN_APPS_KEY, mutableSetOf()) ?: mutableSetOf() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_apps) val packageManager = getPackageManager() LoadApplications().execute() grid = findViewById<View>(R.id.grid) as GridView grid!!.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE) grid!!.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id -> val app = applist!![position] try { val intent = packageManager.getLaunchIntentForPackage(app.packageName) intent?.let { startActivity(it) } } catch (e: Exception) { Toast.makeText(this@appsActivity, e.message, Toast.LENGTH_LONG).show() } } grid!!.onItemLongClickListener = AdapterView.OnItemLongClickListener { parent, view, position, id -> val app = applist!![position] val packageName = app.packageName if (mSelected.contains(packageName)) { mSelected.remove(packageName) view.setBackgroundColor(Color.TRANSPARENT) } else { mSelected.add(packageName) view.setBackgroundColor(Color.LTGRAY) } button3.setOnClickListener { val builder1 = AlertDialog.Builder(this@appsActivity) builder1.setMessage("Are you sure you want to hide selected apps?") builder1.setCancelable(true) builder1.setPositiveButton("Yes") { dialog, id -> val hiddenApps = getHiddenApps() hiddenApps.addAll(mSelected) saveHiddenApps(hiddenApps) LoadApplications().execute() mSelected.clear() } builder1.setNegativeButton("No") { dialog, id -> dialog.cancel() mSelected.clear() for (i in 0 until grid!!.count) { grid!!.getChildAt(i)?.setBackgroundColor(Color.TRANSPARENT) } } val alert11 = builder1.create() alert11.show() } true } } private fun checkForLaunchIntent(list: List<ApplicationInfo>): List<ApplicationInfo> { val hiddenPackages = getHiddenApps() val applist = ArrayList<ApplicationInfo>() for (info in list) { try { if (null != packageManager!!.getLaunchIntentForPackage(info.packageName) && !hiddenPackages.contains(info.packageName)) { applist.add(info) } } catch (e: Exception) { e.printStackTrace() } } Collections.sort(applist, ApplicationInfo.DisplayNameComparator(packageManager)) return applist } @SuppressLint("StaticFieldLeak") private inner class LoadApplications : AsyncTask<Void?, Void?, Void?>() { override fun doInBackground(vararg params: Void?): Void? { applist = checkForLaunchIntent(packageManager!!.getInstalledApplications( PackageManager.GET_META_DATA)) listadaptor = ApplicationAdapter(this@appsActivity, R.layout.grid_item, applist!!) return null } override fun onPostExecute(result: Void?) { grid!!.adapter = listadaptor super.onPostExecute(result) } }
关键说明
- 现在我们用包名作为唯一标识存储隐藏应用,避免了应用排序或更新导致的位置变化问题。
- 每次确认隐藏后,选中的包名会存入SharedPreferences,重启应用时
checkForLaunchIntent方法会自动过滤这些包名,不会显示对应的图标。 - 如果之后需要恢复隐藏的应用,只需从SharedPreferences的集合中移除对应包名即可(你可以拓展一个恢复功能,比如在设置页面展示已隐藏应用列表供用户选择恢复)。
内容的提问来源于stack exchange,提问作者ROOH
相关产品推荐
相关产品推荐

