API 请求仅调用一次且未在 recyclerview 中显示结果
API Request called only once and not showing result in recyclerview
我是 MVVM 的新手,我很乐意得到一些帮助。它是非常基本的项目,只是为了了解 MVVM 中的工作方式。
因此,一旦应用程序启动,它就会用 API 中的所有员工填充 recyclerView。
我有一个浮动操作按钮,当我按下它时,会打开一个带有编辑文本的警告对话框,当我输入一个数字时,我想根据他的 ID 从 API 中获取特定员工并更新 recyclerview所以它只会显示 1 名员工。
问题是:
当我输入一个 ID 并按搜索时,服务器通过它的 ID 为我找到了一个特定的员工并且工作正常,但是当我再次搜索一个特定的员工时 我没有在 logcat 中获取员工详细信息,就像它没有在服务器中搜索它一样(我没有从 logcat 中收到任何错误或失败日志)。
我不确定我是否实现了 observables 和 MVVM 模式,所以如果您有任何反馈,我很乐意听到。
我正在使用这个虚拟机 API - https://dummy.restapiexample.com/
这是员工模型:
data class Employee (
@SerializedName("employee_name")
val employeeName:String,
@SerializedName("employee_salary")
val employeeSalary: String,
@SerializedName("employee_age")
val employeeAge: Int,
@SerializedName("id")
val employeeID: Int
)
data class EmployeesListResult(
@SerializedName("data")
val getEmployeesListResult : List<Employee>,
)
data class SingleEmployeeListResult(
@SerializedName("data")
val getSingleEmployeesListResult : Employee
)
这是API请求对象:
object APIRequest {
val baseUrl : String = "https://dummy.restapiexample.com/api/v1/"
var retrofit: Retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
val retrofitCallGetList : APICallRequest = retrofit.create(APICallRequest::class.java)
}
interface APICallRequest{
@GET("employees")
fun callEmployeeList() : Call<EmployeesListResult>
@GET("employee/{id}")
fun callSpecificEmployee(@Path(value = "id", encoded = false) key: Int, ): Call<SingleEmployeeListResult>
}
这是主存储库class:
class MainRepository {
val mutableListLiveData = MutableLiveData<List<Employee>>()
val mutableSingleLiveData = MutableLiveData<Employee>()
fun getEmployeeListFromAPI(): MutableLiveData<List<Employee>> {
val apiRequest: APICallRequest = APIRequest.retrofitCallGetList
apiRequest.callEmployeeList().enqueue(object : Callback<EmployeesListResult?> {
override fun onResponse(
call: Call<EmployeesListResult?>,
response: Response<EmployeesListResult?>
) {
if (response.isSuccessful) {
mutableListLiveData.value = response.body()?.getEmployeesListResult
Log.e("onResponse", "Success!")
Log.e("Response:", "${response.body()}")
}
}
override fun onFailure(call: Call<EmployeesListResult?>, t: Throwable) {
Log.e("onFailure", "Failed getting list: ${t.message}")
}
})
return mutableListLiveData
}
fun getSpecificEmployee(employeeID: Int): MutableLiveData<Employee> {
val apiRequest: APICallRequest = APIRequest.retrofitCallGetList
apiRequest.callSpecificEmployee(employeeID).enqueue(object : Callback<SingleEmployeeListResult?> {
override fun onResponse(
call: Call<SingleEmployeeListResult?>,
response: Response<SingleEmployeeListResult?>
) {
if (response.isSuccessful) {
mutableSingleLiveData.value = response.body()?.getSingleEmployeesListResult
Log.e("Single onResponse", "Success!")
Log.e("Response:", "${response.body()}")
}
}
override fun onFailure(call: Call<SingleEmployeeListResult?>, t: Throwable) {
Log.e("Single onResponse FAIL", "FAIL! ${t.message}")
}
})
return mutableSingleLiveData
}
这是 MainViewModel:
class MainViewModel : ViewModel() {
private var employeeMutableData : MutableLiveData<List<Employee>>? = null
private var specificEmployeeMutableData : MutableLiveData<Employee>? = null
fun getEmployeeListFromRepo() : LiveData<List<Employee>>{
if (employeeMutableData == null){
employeeMutableData = MainRepository().getEmployeeListFromAPI()
}
return employeeMutableData as LiveData<List<Employee>>
}
fun getSpecificEmployee(employeeID : Int) : LiveData<Employee> {
if (specificEmployeeMutableData == null){
specificEmployeeMutableData = MainRepository().getSpecificEmployee(employeeID)
}
return specificEmployeeMutableData as LiveData<Employee>
}
}
MainActivityclass:
class MainActivity : AppCompatActivity() {
private val mainViewModel : MainViewModel by viewModels()
private lateinit var recyclerView: RecyclerView
private lateinit var mainAdapter: MainRecyclerViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initRecycler()
val actionButton = findViewById<FloatingActionButton>(R.id.actionButton)
actionButton.setOnClickListener(View.OnClickListener {
searchEmployeeByIdDialog()
})
mainViewModel.getEmployeeListFromRepo().observe(this,object : Observer<List<Employee>> {
override fun onChanged(theListOfEmployees: List<Employee>) {
mainAdapter = MainRecyclerViewAdapter(theListOfEmployees)
recyclerView.adapter = mainAdapter
}
})
} // End of OnCreate
private fun initRecycler() {
recyclerView = findViewById<RecyclerView>(R.id.mainRecyclerView)
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = GridLayoutManager(this@MainActivity,2)
}
private fun searchEmployeeByIdDialog(){
val editTextForDialog = EditText(this)
editTextForDialog.maxLines = 1
editTextForDialog.setPadding(10)
editTextForDialog.inputType = InputType.TYPE_CLASS_NUMBER
val alertDialog = AlertDialog.Builder(this)
alertDialog.setTitle("Employee Search")
alertDialog.setMessage("What employee ID do you want to look for ?")
alertDialog.setView(editTextForDialog)
.setPositiveButton("Search", DialogInterface.OnClickListener { dialogInterface: DialogInterface?, i: Int ->
if (editTextForDialog.text.isNotEmpty()){
mainViewModel.getSpecificEmployee(editTextForDialog.text.toString().toInt()).observe(this,object : Observer<Employee?> {
override fun onChanged(t: Employee?) {
if (t != null) {
val list = listOf(t)
mainAdapter.updateEmployeeList(list)
}
}
})
}else{
Toast.makeText(this,"Please enter employee ID",Toast.LENGTH_SHORT).show()
}
})
.setNegativeButton("Cancel", DialogInterface.OnClickListener { dialogInterface, i ->
dialogInterface.dismiss()
})
.show()
}
}
最后,MainRecyclerViewAdapter class:
class MainRecyclerViewAdapter(var employeeList: List<Employee>) : RecyclerView.Adapter<MainRecyclerViewAdapter.EmployeesHolder>() {
inner class EmployeesHolder(var itemView : View) : RecyclerView.ViewHolder(itemView){
fun bindData(employee : Employee){
val nameTextView = itemView.findViewById<TextView>(R.id.nameTextView)
val ageTextView = itemView.findViewById<TextView>(R.id.ageTextView)
val salaryTextView = itemView.findViewById<TextView>(R.id.salaryTextView)
nameTextView.text = employee.employeeName
ageTextView.text = employee.employeeAge.toString()
salaryTextView.text = employee.employeeSalary
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmployeesHolder {
return EmployeesHolder(LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_row,parent,false))
}
override fun onBindViewHolder(holder: EmployeesHolder, position: Int) {
holder.bindData(employeeList[position])
}
override fun getItemCount(): Int {
return employeeList.size
}
fun updateEmployeeList(newList: List<Employee>) {
this.employeeList = newList
notifyDataSetChanged()
}
}
非常感谢!
在您的 MainViewModel 实现中,关于您如何实现 getSpecificEmployee(...)
方法的一些事情对我来说很突出。
目前的实现方式,第一次搜索特定员工时,specificEmployeeMutableData
实时数据将为空,因此将调用相关的存储库方法。
但是对于您尝试获取特定员工的后续时间,specificEmployeeMutableData
将不再为空,这意味着它不会触发存储库中的 API 调用。
这就是为什么在您的日志中,似乎什么都没有发生...因为什么都没有发生。
一个快速补丁可以是删除空检查,并在每次需要获取特定员工时调用存储库。
fun getSpecificEmployee(employeeID : Int) : LiveData<Employee> {
return MainRepository().getSpecificEmployee(employeeID) as LiveData<Employee>
}
我是 MVVM 的新手,我很乐意得到一些帮助。它是非常基本的项目,只是为了了解 MVVM 中的工作方式。
因此,一旦应用程序启动,它就会用 API 中的所有员工填充 recyclerView。 我有一个浮动操作按钮,当我按下它时,会打开一个带有编辑文本的警告对话框,当我输入一个数字时,我想根据他的 ID 从 API 中获取特定员工并更新 recyclerview所以它只会显示 1 名员工。
问题是:
当我输入一个 ID 并按搜索时,服务器通过它的 ID 为我找到了一个特定的员工并且工作正常,但是当我再次搜索一个特定的员工时 我没有在 logcat 中获取员工详细信息,就像它没有在服务器中搜索它一样(我没有从 logcat 中收到任何错误或失败日志)。
我不确定我是否实现了 observables 和 MVVM 模式,所以如果您有任何反馈,我很乐意听到。
我正在使用这个虚拟机 API - https://dummy.restapiexample.com/
这是员工模型:
data class Employee (
@SerializedName("employee_name")
val employeeName:String,
@SerializedName("employee_salary")
val employeeSalary: String,
@SerializedName("employee_age")
val employeeAge: Int,
@SerializedName("id")
val employeeID: Int
)
data class EmployeesListResult(
@SerializedName("data")
val getEmployeesListResult : List<Employee>,
)
data class SingleEmployeeListResult(
@SerializedName("data")
val getSingleEmployeesListResult : Employee
)
这是API请求对象:
object APIRequest {
val baseUrl : String = "https://dummy.restapiexample.com/api/v1/"
var retrofit: Retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
val retrofitCallGetList : APICallRequest = retrofit.create(APICallRequest::class.java)
}
interface APICallRequest{
@GET("employees")
fun callEmployeeList() : Call<EmployeesListResult>
@GET("employee/{id}")
fun callSpecificEmployee(@Path(value = "id", encoded = false) key: Int, ): Call<SingleEmployeeListResult>
}
这是主存储库class:
class MainRepository {
val mutableListLiveData = MutableLiveData<List<Employee>>()
val mutableSingleLiveData = MutableLiveData<Employee>()
fun getEmployeeListFromAPI(): MutableLiveData<List<Employee>> {
val apiRequest: APICallRequest = APIRequest.retrofitCallGetList
apiRequest.callEmployeeList().enqueue(object : Callback<EmployeesListResult?> {
override fun onResponse(
call: Call<EmployeesListResult?>,
response: Response<EmployeesListResult?>
) {
if (response.isSuccessful) {
mutableListLiveData.value = response.body()?.getEmployeesListResult
Log.e("onResponse", "Success!")
Log.e("Response:", "${response.body()}")
}
}
override fun onFailure(call: Call<EmployeesListResult?>, t: Throwable) {
Log.e("onFailure", "Failed getting list: ${t.message}")
}
})
return mutableListLiveData
}
fun getSpecificEmployee(employeeID: Int): MutableLiveData<Employee> {
val apiRequest: APICallRequest = APIRequest.retrofitCallGetList
apiRequest.callSpecificEmployee(employeeID).enqueue(object : Callback<SingleEmployeeListResult?> {
override fun onResponse(
call: Call<SingleEmployeeListResult?>,
response: Response<SingleEmployeeListResult?>
) {
if (response.isSuccessful) {
mutableSingleLiveData.value = response.body()?.getSingleEmployeesListResult
Log.e("Single onResponse", "Success!")
Log.e("Response:", "${response.body()}")
}
}
override fun onFailure(call: Call<SingleEmployeeListResult?>, t: Throwable) {
Log.e("Single onResponse FAIL", "FAIL! ${t.message}")
}
})
return mutableSingleLiveData
}
这是 MainViewModel:
class MainViewModel : ViewModel() {
private var employeeMutableData : MutableLiveData<List<Employee>>? = null
private var specificEmployeeMutableData : MutableLiveData<Employee>? = null
fun getEmployeeListFromRepo() : LiveData<List<Employee>>{
if (employeeMutableData == null){
employeeMutableData = MainRepository().getEmployeeListFromAPI()
}
return employeeMutableData as LiveData<List<Employee>>
}
fun getSpecificEmployee(employeeID : Int) : LiveData<Employee> {
if (specificEmployeeMutableData == null){
specificEmployeeMutableData = MainRepository().getSpecificEmployee(employeeID)
}
return specificEmployeeMutableData as LiveData<Employee>
}
}
MainActivityclass:
class MainActivity : AppCompatActivity() {
private val mainViewModel : MainViewModel by viewModels()
private lateinit var recyclerView: RecyclerView
private lateinit var mainAdapter: MainRecyclerViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initRecycler()
val actionButton = findViewById<FloatingActionButton>(R.id.actionButton)
actionButton.setOnClickListener(View.OnClickListener {
searchEmployeeByIdDialog()
})
mainViewModel.getEmployeeListFromRepo().observe(this,object : Observer<List<Employee>> {
override fun onChanged(theListOfEmployees: List<Employee>) {
mainAdapter = MainRecyclerViewAdapter(theListOfEmployees)
recyclerView.adapter = mainAdapter
}
})
} // End of OnCreate
private fun initRecycler() {
recyclerView = findViewById<RecyclerView>(R.id.mainRecyclerView)
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = GridLayoutManager(this@MainActivity,2)
}
private fun searchEmployeeByIdDialog(){
val editTextForDialog = EditText(this)
editTextForDialog.maxLines = 1
editTextForDialog.setPadding(10)
editTextForDialog.inputType = InputType.TYPE_CLASS_NUMBER
val alertDialog = AlertDialog.Builder(this)
alertDialog.setTitle("Employee Search")
alertDialog.setMessage("What employee ID do you want to look for ?")
alertDialog.setView(editTextForDialog)
.setPositiveButton("Search", DialogInterface.OnClickListener { dialogInterface: DialogInterface?, i: Int ->
if (editTextForDialog.text.isNotEmpty()){
mainViewModel.getSpecificEmployee(editTextForDialog.text.toString().toInt()).observe(this,object : Observer<Employee?> {
override fun onChanged(t: Employee?) {
if (t != null) {
val list = listOf(t)
mainAdapter.updateEmployeeList(list)
}
}
})
}else{
Toast.makeText(this,"Please enter employee ID",Toast.LENGTH_SHORT).show()
}
})
.setNegativeButton("Cancel", DialogInterface.OnClickListener { dialogInterface, i ->
dialogInterface.dismiss()
})
.show()
}
}
最后,MainRecyclerViewAdapter class:
class MainRecyclerViewAdapter(var employeeList: List<Employee>) : RecyclerView.Adapter<MainRecyclerViewAdapter.EmployeesHolder>() {
inner class EmployeesHolder(var itemView : View) : RecyclerView.ViewHolder(itemView){
fun bindData(employee : Employee){
val nameTextView = itemView.findViewById<TextView>(R.id.nameTextView)
val ageTextView = itemView.findViewById<TextView>(R.id.ageTextView)
val salaryTextView = itemView.findViewById<TextView>(R.id.salaryTextView)
nameTextView.text = employee.employeeName
ageTextView.text = employee.employeeAge.toString()
salaryTextView.text = employee.employeeSalary
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmployeesHolder {
return EmployeesHolder(LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_row,parent,false))
}
override fun onBindViewHolder(holder: EmployeesHolder, position: Int) {
holder.bindData(employeeList[position])
}
override fun getItemCount(): Int {
return employeeList.size
}
fun updateEmployeeList(newList: List<Employee>) {
this.employeeList = newList
notifyDataSetChanged()
}
}
非常感谢!
在您的 MainViewModel 实现中,关于您如何实现 getSpecificEmployee(...)
方法的一些事情对我来说很突出。
目前的实现方式,第一次搜索特定员工时,specificEmployeeMutableData
实时数据将为空,因此将调用相关的存储库方法。
但是对于您尝试获取特定员工的后续时间,specificEmployeeMutableData
将不再为空,这意味着它不会触发存储库中的 API 调用。
这就是为什么在您的日志中,似乎什么都没有发生...因为什么都没有发生。
一个快速补丁可以是删除空检查,并在每次需要获取特定员工时调用存储库。
fun getSpecificEmployee(employeeID : Int) : LiveData<Employee> {
return MainRepository().getSpecificEmployee(employeeID) as LiveData<Employee>
}