Android - 持久存储 ArrayList

Android - Store ArrayList persistent

我的应用程序在 RecyclerView 中显示各种类别(香草、配菜等)的列表。根据您单击的类别,将打开一个带有新 RecylcerView 的新 Activity,其中包含所有成分。

现在我有一个 ArrayList,它根据选择的类别通过“.add”填充成分。

我现在面临的问题是,我想为用户实现一个选项来添加自己的成分。我尝试使用 Gson 将包含成分的 ArrayList 存储在 SharedPreferences 中,但我无法添加元素,因为它总是覆盖当前列表。

储存原料的最佳方式是什么?一个房间,sqlite,..? 没有进一步解释,配料表最多只包含大约 70 项。

提前致谢。

编辑:

CatList.kt

class CatList : AppCompatActivity() {

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


        //Create List for categories
        val cats = ArrayList<IngCat>()

        //Fill categories
        cats.add(IngCat(R.drawable.herbs, "Herbs"))
        cats.add(IngCat(R.drawable.fluessiges, "Liquids"))
        cats.add(IngCat(R.drawable.festes, "Solids"))
        cats.add(IngCat(R.drawable.beilagen, "Sides"))



        //Recyclerview
        id_rv_CatList.layoutManager = LinearLayoutManager(this)
        id_rv_CatList.adapter =
            CatListAdapter(cats) {listItem, position -> //go to Ingredient List Activity
                    goToIngList(position, listItem.name)
                }

        //id_rv_CatList.addItemDecoration(DividerItemDecoration(this,DividerItemDecoration.HORIZONTAL))

        //actionbar
        val actionbar = supportActionBar
        //set actionbar title
        actionbar!!.title = "Ingredient - Categories"


    }
    private fun goToIngList(cat: Int, name: String){
        val intent = Intent(this, IngList::class.java)
        intent.putExtra("Category", cat)
        intent.putExtra("Name", name)
        startActivity(intent)
    }
}

data class IngCat(var mImageResource:Int, var name:String)

IngList.kt

class IngList : AppCompatActivity() {

    companion object {
        var categoryChoosen : Int = 0
        var catName : String = "Err"
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_ing_list)


        //Initilize Ingredient List
        val ings :ArrayList<IngIng> = ArrayList()


        //Get category and category name
        categoryChoosen = intent.getIntExtra("Kategorie",0)
        catName = intent.getStringExtra("Name")!!

       when (categoryChoosen) {
            0 -> {
                ings.add(IngIng("https://doeel.com/images/thumbnails/1100/900/detailed    /92/Turmeric_Powder___Holud_Gura__.png", "Turmeric Powder"))
            }
            1 -> ings.add(IngIng("https://www.miraherba.de/4923-large_default/bio-ghee-300-g.jpg", "Ghee"))
            2 -> ings.add(IngIng("https://www.organicfacts.net/wp-content/uploads/coriander-1.jpg", "Coriander leaves"))
            3 -> ings.add(IngIng("https://gbc-cdn-public-media.azureedge.net/img75602.1426x713.jpg", "Potatoes"))
        }

        

        //Actionbar Settings
        setSupportActionBar(toolbar)
        val actionbar = supportActionBar
        actionbar!!.title = "Ingredients- $catName"
        actionbar.setDisplayHomeAsUpEnabled(true)


        //Recyclerview
        id_rv_IngList.layoutManager = GridLayoutManager(this,2)
        id_rv_IngList.adapter =
            IngListAdapter(ings) {//ClickListener RecyclerView
                Toast.makeText(this, "Item clicked: ${it.name}", Toast.LENGTH_SHORT).show()
            }





//Actionbar
    }
    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.actionbar_ing_list, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.id_menu_action_add -> {
                val intent = Intent(this, AddIngredient::class.java)
                startActivity(intent)
                true
            }
            else -> super.onOptionsItemSelected(item)

    }
}

override fun onSupportNavigateUp(): Boolean {
        onBackPressed()
        return true
    }



}

IngListAdapter.kt

class IngListAdapter (private val ings: ArrayList<IngIng>, val clickListener: (IngIng)->Unit): RecyclerView.Adapter<RecyclerView.ViewHolder>(){


    override fun getItemCount(): Int = ings.size




    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val v: View = LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_ing_list_item, parent, false)
        return IngViewHolder(v)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        var currentItem = ings.get(position)
        when (holder) {
            is IngViewHolder -> {
                holder.tvIngList.text = currentItem.name
               // holder.ivIngImage.setImageResource(currentItem.mImageResource)
                Picasso.get().load(currentItem.mImageResource).placeholder(R.drawable.ic_broken_image_black_200dp).error(R.drawable.ic_broken_image_red_24dp).into(holder.ivIngImage)

                holder.cvIngCard.setOnClickListener{
                        clickListener(currentItem)
                    }
                }
            }


        }
    }


    class IngViewHolder (view: View) : RecyclerView.ViewHolder(view) {

        val tvIngList: TextView = view.id_text_ing
        val ivIngImage: ImageView = view.id_img_ing
        val cvIngCard: MaterialCardView = view.id_cv_ing_list
    }

我个人认为如果项目很少,SharedPreference 中的 Json/Gson 是最简单的方法。我的处理方式是在应用程序启动时将列表存储在内存中,并在应用程序关闭时将列表保存回 SharedPreference。此外,当应用程序停止时,因为您不能 100% 确定 onDestroy 将被调用。

所以首先我会制作一个 class 来存储数据。如果您使用的 Fragment 都在同一个 Activity 中,您可以将它放在 ViewModel 中。但由于它们是独立的活动,因此您需要为它们创建一个单例。 (Google 不建议使用多个 Activity,因为它们之间很难共享数据。但这并非不可能。我们在 Fragments 之前就是这样做的。)

要以单例方式执行此操作,您可以 class 像这样:

class IngredientsRepo private constructor (application: Application) {

    companion object {
        private val INSTANCE: IngredientsRepo? = null
        fun getInstance(application: Application) = 
            INSTANCE ?: IngredientsRepo(application).also { INSTANCE = it }

        private const KEY_JSON_PREF = "ingredientsJson"
    }

    private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application)

    val herbsList: MutableList<IngCat>
    val liquidsList: MutableList<IngCat>
    val solidsList: MutableList<IngCat>
    val sidesList: MutableList<IngCat>

    init {
        val json = sharedPreferences.getString(KEY_JSON_PREF, null)
        if (json == null) {
            // initialize your list contents for the first time
        } else {
            // convert your json and fill the data into your lists
        }
    }

    fun save {
        val jsonString = // Convert your lists to Json
        sharedPreferences.edit().putString(KEY_JSON_PREF, jsonString).apply()
    }
}

此 class 负责设置您的列表。您可以使用 IngredientsRepo.getInstance(this) 从任何 Activity 检索它,并且您可以随时在列表中添加和删除项目。您也可以随时调用 save 来保存最新数据。在修改列表的任何 Activity 的 onStop() 中执行此操作可能就足够了。

更恰当地说,这个 class 中的数据只会与不可变列表一起公开,并且您将添加用于添加和删除项目的函数,因此只有这个 class 直接修改列表。我不想使示例过于复杂,但是封装最好不要让 Activity(应该是纯 UI 组件)直接修改数据结构。