skip to Main Content

I’m trying to load a RecyclerView with JSON data in a local file located in ‘assets’.

Activity:

class RecipesActivity : AppCompatActivity(), RecipeAdapter.RecipeItemListener {

private lateinit var binding : ActivityRecipesBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityRecipesBinding.inflate(layoutInflater)
    setContentView(binding.root)

    setSupportActionBar(binding.mainToolBar.toolbar)
    supportActionBar?.setDisplayShowTitleEnabled(false)

    val viewModel : RecipeViewModel by viewModels()
    viewModel.getRecipes().observe(this) {
        binding.recipesRecyclerView.adapter = RecipeAdapter(this, it, this)
    }
}

Adapter:

class RecipeAdapter (val context : Context,
                 private val recipes : List<Recipe>,
                 private val itemListener : RecipeItemListener) : RecyclerView.Adapter<RecipeAdapter.RecipeViewHolder>() {

   inner class RecipeViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView) {
       var recipeTextView = itemView.findViewById<TextView>(R.id.recipeTextView)
   }

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeViewHolder {
    val inflator = LayoutInflater.from(parent.context)
    val view = inflator.inflate(R.layout.item_recipe, parent, false)
    return RecipeViewHolder(view)
}

override fun onBindViewHolder(viewHolder: RecipeViewHolder, position: Int) {
    val recipe = recipes[position]
    with (viewHolder) {
        recipeTextView.text = recipe.dish

        itemView.setOnClickListener {
            itemListener.recipeSelected(recipe)
        }
    }
}

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

interface RecipeItemListener {
    fun recipeSelected(recipe : Recipe)
}

ViewModel:

class RecipeViewModel : ViewModel() {
private var recipes = MutableLiveData<List<Recipe>>()

init {
    val gson = Gson()
    val inputStream: InputStream = assets.open("recipes.json")

    val jsonStr = inputStream.bufferedReader().use { it.readText() }

    val recipeList = gson.fromJson(jsonStr, Array<Recipe>::class.java).toList()
    recipes.value = recipeList
    Log.i("json string", "${recipeList.toString()}")
}

fun getRecipes() : LiveData<List<Recipe>> {
    return recipes
}

Everything works besides getting the data, just not sure how to do that. I tried passing the context in as a parameter to the constructor, but I don’t know where else I need to update because it says no empty constructor present. I added an empty constructor but it kept the recyclerview empty.

2

Answers


  1. I used this to load a JSON-list from assets:

        private val dataType: Type by lazy {
             object : TypeToken<Collection<DATA?>?>() {}.type
         }
    
        init {
             val gson = Gson()
             val assetManager = context.assets // probably same as your assets
    
             val data = runCatching {
                 val inputStream = assetManager.open("data_file.json")
                 val reader = JsonReader(InputStreamReader(inputStream))
    
                 val quotes: Collection<DATA> =
                     gson.fromJson(
                         reader,
                         storyMissionType
                     )
    
                 inputStream.close()
                 reader.close()
    
                 quotes.toList()
             }.getOrDefault(emptyList())
        }
    
    Login or Signup to reply.
  2. First you do not need to init RecipeAdapter each time the LiveData changed:

    viewModel.getRecipes().observe(this) {
        binding.recipesRecyclerView.adapter = RecipeAdapter(this, it, this)
    }
    

    You can call:

    binding.recipesRecyclerView.adapter?.notifyDataSetChanged()
    

    And init it before. Second in your RecyclerView check for:

    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search