You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:28:11