我正在尝试使用改造库显示来自github的存储库列表,然后使用recyclerView库显示它。在添加recyclerView之前,我已经检查了是否成功获取了数据。但是在添加了recyclerView之后,我得到了NullPointerException。
下面是我的Viewmodel,片段,适配器和数据类的代码
ListViewModel.kt
package com.kunalrai.githubtrends
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class ListViewModel : ViewModel() {
lateinit var repoList: MutableLiveData<List<Repo>>
fun getRepos(): MutableLiveData<List<Repo>>{
repoList = MutableLiveData()
loadRepos()
return repoList
}
private fun loadRepos() {
Api.RETROFIT_SERVICE.getRepos().enqueue( object: Callback<List<Repo>> {
override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
Log.i("Failure: ", t.message)
}
override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo>>) {
if(response.body() != null){
repoList.value = response.body()!!
Log.i("response.body :",""+response.body()!!)
}
}
})
}
}
ListFragment.kt
package com.kunalrai.githubtrends
import androidx.lifecycle.ViewModelProviders
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.kunalrai.githubtrends.databinding.ListFragmentBinding
class ListFragment : Fragment() {
companion object {
fun newInstance() = ListFragment()
}
private val viewModel: ListViewModel by lazy {
ViewModelProviders.of(this).get(ListViewModel::class.java)
}
private lateinit var binding: ListFragmentBinding
var recyclerView: RecyclerView? = null
lateinit var recyclerAdapter: RecyclerAdapter
var repoList: List<Repo>? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = ListFragmentBinding.inflate(inflater, container, false)
binding.lifecycleOwner
binding.viewmodel = viewModel
setHasOptionsMenu(true)
recyclerView = view?.findViewById(R.id.rv_repo_list)
recyclerView?.layoutManager = LinearLayoutManager(context)
recyclerView?.setHasFixedSize(true)
viewModel.getRepos().observe(this,
Observer<List<Repo>> { repoList ->
recyclerAdapter = RecyclerAdapter(context, repoList!!)
recyclerView!!.adapter = recyclerAdapter
})
return binding.root
}
}
ListAdapter.kt
package com.kunalrai.githubtrends
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
class ListAdapter(private val context: Context?, private val repoList: List<Repo>) : RecyclerView.Adapter<ListAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.repo_item,parent,false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return repoList.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.author.text = repoList[position].author
holder.repo.text = repoList[position].name
Glide.with(context!!).load(repoList[position].avatar)
.apply(RequestOptions().centerCrop())
.into(holder.image)
}
class MyViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView!!) {
val author: TextView = itemView!!.findViewById(R.id.owner_name)
val image: ImageView = itemView!!.findViewById(R.id.owner_image)
val repo: TextView = itemView!!.findViewById(R.id.repo_name)
}
}
Repo.kt(数据类)
package com.kunalrai.githubtrends
import com.squareup.moshi.Json
data class Repo(
@Json(name = "author")
var author: String,
@Json(name = "name")
var name: String,
@Json(name = "description")
var desc: String,
@Json(name = "avatar")
var avatar: String,
@Json(name = "language")
var language: String,
@Json(name = "url")
var url: String,
@Json(name = "stars")
var stars: String,
@Json(name = "forks")
var forks: String
)
我的错误堆栈:- (已更新)
2019-11-29 16:43:57.686 20703-20703/? E/Zygote: isWhitelistProcess - Process is Whitelisted
2019-11-29 16:43:57.688 20703-20703/? E/Zygote: accessInfo : 1
2019-11-29 16:44:03.865 20703-20703/com.kunalrai.githubtrends E/RecyclerView: No adapter attached; skipping layout
答案 0 :(得分:1)
在Kotlin中使用!!
时要小心,因为它可能导致NullPointerException
。将其替换为variable?.let{}
和variable?.
对于此代码块:
viewModel.getRepos().observe(this,
Observer<List<Repo>> { repoList ->
recyclerAdapter = RecyclerAdapter(context, repoList!!)
recyclerView!!.adapter = recyclerAdapter
})
您可以更改为:
class ListFragment {
//Define adapter as global variable
RecyclerAdapter recyclerAdapter: RecyclerAdapter? = null
override fun onCreateView(...) {
...
//Init adapter only once
recyclerAdapter = RecyclerAdapter(context, repoList)
//Use binding to make sure the view exist. You can remove findViewById command
binding.recyclerView.adapter = recyclerAdapter
viewModel.getRepos().observe(this,
Observer<List<Repo>> {
it?.let { repoList ->
recyclerAdapter?.swapData(repoList)
}
})
}
}
variableName?.let{}
仅在variableName不为空时在{}
块内执行命令
编辑适配器:
class ListAdapter() {
private val repoList: ArrayList<Repo> = ArrayList()
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(repoList[position])
}
fun swapData(repoList: List<Repo>) {
this.repoList.clear()
this.repoList.addAll(repoList)
notifyDataSetChanged()
}
class MyViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView!!) {
fun bindView(repo: Repo) {
//Do you binding here, you can get context from itemView.context
}
}
}
答案 1 :(得分:0)
替换
recyclerView = view?.findViewById(R.id.rv_repo_list)
使用
val recyclerView = findViewById<RecyclerView>(R.id.rv_repo_list)
val adapter = ListAdapter(this)
recyclerView.adapter = adapter
因此您可以避免使用所有空安全代码,因为它会同时实例化布局和适配器