【问题标题】:How get list of apps icons and names in kotlin to recyclerview如何在 kotlin 中获取应用程序图标和名称列表到 recyclerview
【发布时间】:2021-03-20 05:51:31
【问题描述】:

所以我是 kotlin 的新手,我试图将设备上所有已安装的应用程序获取到回收站查看我的问题是回收站适配器中活动的所有内容都返回 null,即使我通过了上下文

这是我的代码:

class Games_listx  : RecyclerView.Adapter<Games_listx.viewxlist>() {

    var appsx = Gamesff()
    var listofapps  = ArrayList<String>()
    var  all_apps : MutableList<ApplicationInfo>? = null
    var icons  : Drawable? = null

    class viewxlist(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val txt_game = itemView.findViewById<View>(R.id.txtgames) as TextView
        val im_game = itemView.findViewById<View>(R.id.imgames) as ImageView
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Games_listx.viewxlist {




        val layoutInflater = LayoutInflater.from(parent.context)

        val view = layoutInflater.inflate(R.layout.lay_allgames,parent,false)


        all_apps = appsx.activity?.packageManager?.getInstalledApplications(0)
        Log.w("apps" , "app " + all_apps?.size)

        for (app in all_apps?.indices!!) {
            val name = all_apps?.get(app)?.loadLabel(appsx.activity?.packageManager!!)

            if  (!(name?.startsWith("com.")!!)) {
                listofapps.add(name?.toString())
            }

        }
        return viewxlist(view)
    }

    override fun getItemCount(): Int {

       return listofapps.size
    }

    override fun onBindViewHolder(holder: Games_listx.viewxlist, position: Int) {


        holder.txt_game.text = listofapps.get(position).toString()




    }

}

listofapps 的大小为 0,包管理器为空

我使用相同的代码在 onstart 中获取应用程序并且它可以工作

我做错了什么

如何在适配器中不为空的情况下获取活动或上下文

片段中的回收站视图

class Gamesff : Fragment() {





override fun onStart() {
    super.onStart()



}

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {

    val viewx = inflater.inflate(R.layout.fragment_gamesff,container,false)

    val all_games_list = viewx.findViewById<View>(R.id.gamelistfounds) as RecyclerView
    val add_game_folder = viewx.findViewById(R.id.txtaddgamefolder) as TextView









    add_game_folder.setOnClickListener {

        Toast.makeText(activity,"Make your own gaming folder",Toast.LENGTH_SHORT).show()

        val i = Intent(activity,GamingFolder::class.java)
        i.action = "add"

        startActivity(i)

    }


    val gridlay = GridLayoutManager(activity,4,GridLayoutManager.VERTICAL,false)

    all_games_list.layoutManager = gridlay
    all_games_list.fitsSystemWindows = true
    all_games_list.hasFixedSize()
    all_games_list.adapter = Games_listx()


    return viewx
}

【问题讨论】:

  • onCreateViewHolder 被重复调用,为 RecyclerView 中的每个可见列表项视图组调用一次。如果您的列表为空,则一开始就不会调用它。您需要创建数据列表并将其填充到适配器类之外,然后将准备好的数据列表传递给适配器。
  • @Tenfour04 谢谢我知道适配器在java中是如何工作的你可以在适配器中处理你的数据并且可以访问上下文但是在kotlin它总是返回null它必须是方式
  • 它在 Kotlin 和 Java 中的工作方式相同。您在 onCreateViewHolder() 中获取已安装应用程序的列表,这没有任何意义。
  • @Tenfour04the 那么如何将活动中的应用程序信息中的可绘制对象传递给适配器而不是保存在数组中的路径呢?

标签: android android-layout kotlin android-recyclerview


【解决方案1】:

由于关注点分离您的适配器不应负责搜索要显示的数据。

首先,为每个应用程序的信息创建一个数据类,这些信息与您的 RecyclerView 显示或操作相关,例如:

data class AppInfo (
    val appName: String,
    val packageName: String,
    val icon: Drawable
)

您的适配器应该为数据列表公开一个公共属性。这就是任何适配器应该工作的方式。您在onCreateViewHolder 中创建您的视图和ViewHolder,并在onBindViewHolder 中绑定您的值。我喜欢设置 ViewHolder 来在内部处理这个问题,所以我的 onBind 和 onCreate 函数非常简单。我还为项目单击侦听器公开了一个属性,因此单击功能也在外部处理。除了将数据和逻辑连接到视图之外,适配器不做任何事情。

class AppsListAdapter: RecyclerView.Adapter<AppsListAdapter.ViewHolder>() {

    inner class ViewHolder(val rootView: ViewGroup): RecyclerView.ViewHolder(rootView), View.OnClickListener {

        init { rootView.onClickListener = this }

        val titleView = rootView.findViewById<TextView>(android.R.id.title)
        val iconView = rootView.findViewById<ImageView>(android.R.id.icon)

        var data: AppInfo? = null
            set(value) {
                field = value
                value ?: return
                titleView.text = value.appName
                iconView.setImageDrawable(value.icon)
            }

        override fun onClick(v: View) {
            data?.let {
                onListItemClick(it)
            }
        }
    }

    fun interface OnItemClickListener {
        fun onItemClick(appInfo: AppInfo)
    }

    var dataList: List<AppInfo> = emptyList()
        set(value) {
            field = value
            notifyDataSetChanged()
        }

    private var onItemClickListener: OnItemClickListener? = null
    
    fun setOnItemClickListener(listener: OnItemClickListener) 
        this.onItemClickListener = listener
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
        ViewHolder(
            LayoutInflater.from(parent.context)
                .inflate(R.layout.app_list_item, parent, false)
        )

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.data = dataList[position]
    }
}

然后在您的 Activity 或 Fragment 或 ViewModel 中,您可以使用协程获取所有数据,因此不会在主线程上加载可绘制对象。就我而言,我在 ViewModel 中执行此操作并填充 UI 订阅的 SharedFlow,但为简单起见,由于您提到了 Activity,因此您可以在 Activity 中执行此操作:

val adapter = AppsListAdapter()
adapter.setOnItemClickListener { // Set what to do when item is clicked
    Log.d("item click", "Clicked package ${it.packageName}")
    
}
myRecyclerView.adapter = adapter

lifecycleScope.launch {
    val packageManager = context.packageManager
    val appsData = withContext(Dispatchers.IO) { // do all loading in background thread
        packageManager.getInstalledPackages(0)
            .filterNot { it.packageName.startsWith("com.") } // any filtering you want before loading
            .map {
                AppInfo(
                    it.applicationInfo.loadLabel(packageManager).toString(),
                    it.packageName,
                    it.applicationInfo.loadIcon(packageManager)
                )
            }
    }
    adapter.dataList = appsData
}

实际上,由于用户可能安装了许多应用程序,我也支持在获取初始列表和每五个图标加载后提前取消协程,如下所示:

lifecycleScope.launch {
    val packageManager = context.packageManager
    val appsData = withContext(Dispatchers.IO) { 
        packageManager.getInstalledPackages(0)
            .filterNot { it.packageName.startsWith("com.") }
            .mapIndexed { idx, it ->
                if (idx % 5 == 0) yield()
                AppsInfo(
                    it.applicationInfo.loadLabel(packageManager).toString(),
                    it.packageName,
                    it.applicationInfo.loadIcon(packageManager)
                )
            }
    }
    adapter.dataList = appsData
}

【讨论】:

  • 感谢您的回答,它的完整答案现在我知道 kotlin 是如何工作的我发现我的问题,还有一件事如果不在 java 中的 kotlin 中,如果 (!s.startwith(...) ) {} 我如何在 kotlin 中做到这一点?
  • 在 Kotlin 中完全一样
  • 我在 kotlin 中使用 if (!string.startwith("") {} 来表示 not startwith 但是出现错误未解决的引用
【解决方案2】:

如果我理解正确,您需要一种在适配器中初始化数据的方法。 你做错的事情是试图初始化onCreateViewHolder() 中的列表。在调用onCreateViewHolder() 之前,将调用getItemCount()。它将返回 0,因为它返回 0 onCreateViewHolder() 将永远不会被调用。你应该做的是在constructor() 中初始化你的listofapps

class AppsAdapter() : RecyclerView.Adapter<AppsAdapter.AppsViewHolder>() {

private var list: MutableList<App> = ArrayList()

constructor(packageManager: PackageManager): this() {
    val packageInfos = packageManager.getInstalledPackages(0)
    for (packageInfo in packageInfos) {
        val appName = packageInfo.applicationInfo.loadLabel(packageManager) as String
        if (appName.startsWith("com.", true)) {
            continue
        }
        val appDrawable = packageInfo.applicationInfo.loadIcon(packageManager)
        list.add(App(appDrawable, appName))
    }
}



override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppsViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    val view: View = inflater.inflate(R.layout.item_app, parent, false)
    return AppsViewHolder(view)
}

override fun onBindViewHolder(holder: AppsViewHolder, position: Int) {
    holder.onBind(list[position])
}

override fun getItemCount(): Int {
    return list.size
}

class AppsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    fun onBind(app: App) {
        val appIcon = itemView.findViewById<ImageView>(R.id.appIconImageView)
        val textView = itemView.findViewById<TextView>(R.id.appNameTextView)
        appIcon.setImageDrawable(app.appIcon)
        textView.text = app.appName
    }
}}

App.kt 在哪里:

class App (val appIcon: Drawable, val appName: String)

在 MainActivity 你应该:

class MainActivity : AppCompatActivity() {

private lateinit var adapter: AppsAdapter

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    adapter = AppsAdapter(packageManager)
    val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
    recyclerView.layoutManager = GridLayoutManager(
        this,
        4,
        GridLayoutManager.VERTICAL,
        false
    )
    recyclerView.adapter = adapter
}}

【讨论】:

    猜你喜欢
    • 2012-05-13
    • 2019-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-13
    • 2015-07-21
    • 1970-01-01
    相关资源
    最近更新 更多