Android中Kotlin使用Volley发起请求无响应问题求助
问题描述
我尝试通过RecyclerView动态创建CardView,再调用API接口获取数据填充TextView,但请求似乎未执行。此前手动创建TextView并填充数据的方式可以正常工作,但改用动态生成的CardView后无任何反应,恳请各位开发者提供指导。
目前可见Result1并未更新为请求响应,但在stringRequest外部使用jsonView.text = "something"可以修改内容。
相关代码
MainActivity代码
class MainActivity : AppCompatActivity() { // private var message: TextView = findViewById<TextView>(R.id.jsonParse) lateinit var weatherRV: RecyclerView lateinit var weatherModelArray: ArrayList<WeatherViewModel> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) supportActionBar?.hide() weatherRV = findViewById(R.id.mainView) weatherModelArray = ArrayList() val model1 = WeatherViewModel("Open weather", "") val model2 = WeatherViewModel("Yr.no", "") weatherModelArray.add(model1) weatherModelArray.add(model2) val weatherAdapter = WeatherAdapter(this, weatherModelArray) val linearLayoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) weatherRV.layoutManager = linearLayoutManager weatherRV.adapter = weatherAdapter apICall(this@MainActivity, model1) } }
apiCall函数代码
@RequiresApi(Build.VERSION_CODES.O) internal fun apICall(context: Context, jsonView: WeatherViewModel) { var lat: Double? = null var lon: Double? = null var url: String? = null val apiKey: String = "XXXX" lat = YYY.Y lon = YYY.Y url = "https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lon}&appid=${apiKey}&units=metric&exclude=minutely,hourly" // Instantiate the RequestQueue. val queue = Volley.newRequestQueue(context) val gsonPretty = GsonBuilder().setPrettyPrinting().create() // Request a string response from the provided URL. val stringRequest = StringRequest( Request.Method.GET, url, {response -> val gson = Gson() val openJSON = gson.fromJson(response.toString(), OpenWeatherJSON::class.java) val prettyJsonString = gsonPretty.toJson(openJSON) try { val dt = openJSON.daily[0].dt val sdf = SimpleDateFormat("dd-MM-yyyy") val date = Date(dt * 1000) // snackbar.dismiss() sdf.format(date) // jsonView.apiResult = sdf.format(date) // jsonView.apiResult.append(" ") jsonView.apiResult = openJSON.daily[0].temp.toString() } catch (e: Exception) { jsonView.apiResult = e.toString() } }, { jsonView.apiResult = "Error" }) // Add the request to the RequestQueue. queue.add(stringRequest) }
Update1:WeatherAdapter代码
class WeatherAdapter(context: Context, weatherModelArray: ArrayList<WeatherViewModel>): RecyclerView.Adapter<WeatherAdapter.ViewHolder>() { private val weatherModelArray: ArrayList<WeatherViewModel> // Constructor init { this.weatherModelArray = weatherModelArray } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WeatherAdapter.ViewHolder { val view: View = LayoutInflater.from(parent.context).inflate(R.layout.card_layout, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: WeatherAdapter.ViewHolder, position: Int) { val model = weatherModelArray[position] holder.apiName.text = model.apiName holder.apiResult.text = model.apiResult } override fun getItemCount(): Int { return weatherModelArray.size } inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { val apiName: TextView val apiResult: TextView init { apiName = itemView.findViewById(R.id.idApiName) apiResult = itemView.findViewById(R.id.idApiResult) } } }
解决方案
问题核心:API请求其实已经执行了,但你修改WeatherViewModel的apiResult后,RecyclerView的Adapter没有收到数据变更通知,所以不会重新绑定数据刷新UI。
步骤1:修改MainActivity,保留Adapter引用
把Adapter从局部变量改成成员变量,方便后续调用刷新方法:
class MainActivity : AppCompatActivity() { lateinit var weatherRV: RecyclerView lateinit var weatherModelArray: ArrayList<WeatherViewModel> lateinit var weatherAdapter: WeatherAdapter // 改为成员变量 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) supportActionBar?.hide() weatherRV = findViewById(R.id.mainView) weatherModelArray = ArrayList() val model1 = WeatherViewModel("Open weather", "") val model2 = WeatherViewModel("Yr.no", "") weatherModelArray.add(model1) weatherModelArray.add(model2) weatherAdapter = WeatherAdapter(this, weatherModelArray) // 初始化成员变量 val linearLayoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) weatherRV.layoutManager = linearLayoutManager weatherRV.adapter = weatherAdapter apICall(this@MainActivity, model1, 0) // 传入model在列表中的位置 } }
步骤2:修改apICall函数,更新数据后通知Adapter
在API回调中更新apiResult后,调用Adapter的notifyItemChanged方法刷新对应位置的UI:
@RequiresApi(Build.VERSION_CODES.O) internal fun apICall(context: Context, jsonView: WeatherViewModel, position: Int) { var lat: Double? = null var lon: Double? = null var url: String? = null val apiKey: String = "XXXX" lat = YYY.Y lon = YYY.Y url = "https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lon}&appid=${apiKey}&units=metric&exclude=minutely,hourly" val queue = Volley.newRequestQueue(context) val gsonPretty = GsonBuilder().setPrettyPrinting().create() val stringRequest = StringRequest( Request.Method.GET, url, {response -> val gson = Gson() val openJSON = gson.fromJson(response.toString(), OpenWeatherJSON::class.java) try { val dt = openJSON.daily[0].dt val sdf = SimpleDateFormat("dd-MM-yyyy") val date = Date(dt * 1000) sdf.format(date) jsonView.apiResult = openJSON.daily[0].temp.toString() // 通知Adapter刷新对应位置 (context as MainActivity).weatherAdapter.notifyItemChanged(position) } catch (e: Exception) { jsonView.apiResult = e.toString() (context as MainActivity).weatherAdapter.notifyItemChanged(position) } }, { jsonView.apiResult = "Error" (context as MainActivity).weatherAdapter.notifyItemChanged(position) }) queue.add(stringRequest) }
可选优化:给Adapter添加更新方法
可以在Adapter中封装一个更新单个item的方法,让代码更清晰:
class WeatherAdapter(context: Context, weatherModelArray: ArrayList<WeatherViewModel>): RecyclerView.Adapter<WeatherAdapter.ViewHolder>() { private val weatherModelArray: ArrayList<WeatherViewModel> init { this.weatherModelArray = weatherModelArray } // 添加单个item更新方法 fun updateItem(position: Int) { notifyItemChanged(position) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WeatherAdapter.ViewHolder { val view: View = LayoutInflater.from(parent.context).inflate(R.layout.card_layout, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: WeatherAdapter.ViewHolder, position: Int) { val model = weatherModelArray[position] holder.apiName.text = model.apiName holder.apiResult.text = model.apiResult } override fun getItemCount(): Int { return weatherModelArray.size } inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { val apiName: TextView val apiResult: TextView init { apiName = itemView.findViewById(R.id.idApiName) apiResult = itemView.findViewById(R.id.idApiResult) } } }
然后在apICall中调用:
(context as MainActivity).weatherAdapter.updateItem(position)
内容的提问来源于stack exchange,提问作者Jan Doležal
相关产品推荐
相关产品推荐

