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 组件)直接修改数据结构。
我的应用程序在 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 组件)直接修改数据结构。