android - 无法根据用户的位置更新recycleview的内容
问题描述
在检索用户位置并根据在回收站视图中检索到的位置显示数据时,我遇到了一个奇怪的错误。对于上下文,每当应用程序从新启动(未授予权限)时,都可以检索该位置。但是,除非我关闭应用程序或按下底部导航栏,否则它不会在回收站视图中显示预期的内容。
示范:
https://i.imgur.com/9kc1Zxc.gif
分段
const val TAG = "ForecastFragment"
@AndroidEntryPoint
class ForecastFragment : Fragment(), SearchView.OnQueryTextListener,
SwipeRefreshLayout.OnRefreshListener{
private val viewModel: WeatherForecastViewModel by viewModels()
private var _binding: FragmentForecastBinding? = null
private val binding get() = _binding!!
private lateinit var forecastAdapter: ForecastAdapter
private lateinit var searchMenuItem: MenuItem
private lateinit var searchView: SearchView
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
private var currentQuery: String? = null
private lateinit var client: FusedLocationProviderClient
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.forecast_list_menu, menu)
searchMenuItem = menu.findItem(R.id.menu_search)
searchView = searchMenuItem.actionView as SearchView
searchView.isSubmitButtonEnabled = true
searchView.setOnQueryTextListener(this)
return super.onCreateOptionsMenu(menu, inflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentForecastBinding.inflate(inflater, container, false)
binding.lifecycleOwner = this
client = LocationServices.getFusedLocationProviderClient(requireActivity())
swipeRefreshLayout = binding.refreshLayoutContainer
setupRecyclerView()
getLastLocation()
updateSupportActionbarTitle()
swipeRefreshLayout.setOnRefreshListener(this)
return binding.root
}
private fun updateSupportActionbarTitle() {
viewModel.queryMutable.observe(viewLifecycleOwner, Observer { query ->
(activity as AppCompatActivity).supportActionBar?.title = query
})
}
private fun setupRecyclerView() {
forecastAdapter = ForecastAdapter()
binding.forecastsRecyclerView.apply {
adapter = forecastAdapter
layoutManager = LinearLayoutManager(activity)
}
}
private fun requestWeatherApiData() {
lifecycleScope.launch {
viewModel.weatherForecast.observe(viewLifecycleOwner, Observer { response ->
when (response) {
is Resource.Success -> {
response.data?.let { forecastResponse ->
forecastAdapter.diff.submitList(forecastResponse.list)
swipeRefreshLayout.isRefreshing = false
}
}
is Resource.Error -> {
response.msg?.let { msg ->
Log.e(TAG, "An error occurred : $msg")
swipeRefreshLayout.isRefreshing = false
}
}
is Resource.Loading -> {
// nothing for now
swipeRefreshLayout.isRefreshing = false
}
}
})
}
}
private fun searchWeatherApiData(searchQuery: String) {
viewModel.searchWeatherForecast(viewModel.applySearchQuery(searchQuery))
viewModel.searchWeatherForecastResponse.observe(viewLifecycleOwner, Observer { response ->
when (response) {
is Resource.Success -> {
response.data?.let { forecastResponse ->
forecastAdapter.diff.submitList(forecastResponse.list)
swipeRefreshLayout.isRefreshing = false
}
}
is Resource.Error -> {
response.msg?.let { msg ->
Log.e(TAG, "An error occurred : $msg")
swipeRefreshLayout.isRefreshing = false
}
}
is Resource.Loading -> {
swipeRefreshLayout.isRefreshing = false
}
}
})
}
private fun getWeatherApiDataLocation(city: String) {
viewModel.weatherForecastLocation(viewModel.applyLocationQuery(city))
viewModel.weatherForecastLocation.observe(viewLifecycleOwner, Observer { response ->
when (response) {
is Resource.Success -> {
response.data?.let { forecastResponse ->
forecastAdapter.diff.submitList(forecastResponse.list)
forecastAdapter.notifyDataSetChanged()
swipeRefreshLayout.isRefreshing = false
}
}
is Resource.Error -> {
response.msg?.let { msg ->
Log.e(TAG, "An error occurred : $msg")
swipeRefreshLayout.isRefreshing = false
}
}
is Resource.Loading -> {
swipeRefreshLayout.isRefreshing = false
}
}
})
}
private fun fetchForecastAsync(query: String?) {
if (query == null)
requestWeatherApiData()
else
searchWeatherApiData(query)
}
override fun onQueryTextSubmit(query: String?): Boolean {
if (query != null) {
currentQuery = query
viewModel.queryMutable.value = query
searchWeatherApiData(query)
}
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
return true
}
override fun onRefresh() {
currentQuery = viewModel.queryMutable.value
fetchForecastAsync(currentQuery)
}
private fun isLocationEnabled(): Boolean {
val locationManager: LocationManager =
requireActivity().getSystemService(Context.LOCATION_SERVICE) as LocationManager
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
|| locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
}
private fun checkPermissions(): Boolean {
if (ActivityCompat.checkSelfPermission(requireContext(),
Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(requireContext(),
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED){
return true
}
return false
}
private fun requestPermissions() {
ActivityCompat.requestPermissions(
requireActivity(),
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION), PERMISSION_ID)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == PERMISSION_ID) {
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
getLastLocation()
}
}
}
@SuppressLint("MissingPermission")
private fun getLastLocation() {
if (checkPermissions()) {
if (isLocationEnabled()) {
client.lastLocation.addOnCompleteListener(requireActivity()) { task ->
val location: Location? = task.result
if (location == null) {
requestNewLocationData()
} else {
val geoCoder = Geocoder(requireContext(), Locale.getDefault())
val addresses = geoCoder.getFromLocation(
location.latitude, location.longitude, 1)
val city = addresses[0].locality
viewModel.queryMutable.value = city
getWeatherApiDataLocation(city)
}
}
} else {
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
startActivity(intent)
}
} else {
requestPermissions()
}
}
@SuppressLint("MissingPermission")
private fun requestNewLocationData() {
val mLocationRequest = LocationRequest.create().apply {
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
interval = 0
fastestInterval = 0
numUpdates = 1
}
client = LocationServices.getFusedLocationProviderClient(requireActivity())
client.requestLocationUpdates(
mLocationRequest, mLocationCallback,
Looper.myLooper()
)
}
private val mLocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
val mLastLocation: Location = locationResult.lastLocation
mLastLocation.latitude.toString()
mLastLocation.longitude.toString()
}
}
}
查看模型
@HiltViewModel
class WeatherForecastViewModel
@Inject constructor(
private val repository: WeatherForecastRepository,
application: Application) :
AndroidViewModel(application) {
private val unit = "imperial"
private var query = "Paris"
val weatherForecast: MutableLiveData<Resource<WeatherForecastResponse>> = MutableLiveData()
var searchWeatherForecastResponse: MutableLiveData<Resource<WeatherForecastResponse>> = MutableLiveData()
val weatherForecastLocation: MutableLiveData<Resource<WeatherForecastResponse>> = MutableLiveData()
var queryMutable: MutableLiveData<String> = MutableLiveData()
init {
//queryMutable.value = query
//getWeatherForecast(queryMutable.value.toString(), unit)
}
private fun getWeatherForecast(query: String, units: String) =
viewModelScope.launch {
weatherForecast.postValue(Resource.Loading())
val response = repository.getWeatherForecast(query, units)
weatherForecast.postValue(weatherForecastResponseHandler(response))
}
private suspend fun searchWeatherForecastSafeCall(searchQuery: Map<String, String>) {
searchWeatherForecastResponse.postValue(Resource.Loading())
val response = repository.searchWeatherForecast(searchQuery)
searchWeatherForecastResponse.postValue(weatherForecastResponseHandler(response))
}
private suspend fun getWeatherForecastLocationSafeCall(query: Map<String, String>) {
weatherForecastLocation.postValue(Resource.Loading())
val response = repository.getWeatherForecastLocation(query)
weatherForecastLocation.postValue(weatherForecastResponseHandler(response))
}
fun searchWeatherForecast(searchQuery: Map<String, String>) =
viewModelScope.launch {
searchWeatherForecastSafeCall(searchQuery)
}
fun weatherForecastLocation(query: Map<String, String>) =
viewModelScope.launch {
getWeatherForecastLocationSafeCall(query)
}
fun applySearchQuery(searchQuery: String): HashMap<String, String> {
val queries: HashMap<String, String> = HashMap()
queries[QUERY_CITY] = searchQuery
queries[QUERY_UNITS] = unit
queries[QUERY_COUNT] = API_COUNT
queries[QUERY_API] = API_KEY
return queries
}
fun applyLocationQuery(query: String): HashMap<String, String> {
val queries: HashMap<String, String> = HashMap()
queries[QUERY_CITY] = query
queries[QUERY_UNITS] = unit
queries[QUERY_COUNT] = API_COUNT
queries[QUERY_API] = API_KEY
return queries
}
private fun weatherForecastResponseHandler(
response: Response<WeatherForecastResponse>): Resource<WeatherForecastResponse> {
if (response.isSuccessful) {
response.body()?.let { result ->
return Resource.Success(result)
}
}
return Resource.Error(response.message())
}
}
解决方案
我觉得问题出在方式上,你正在设置你的观察者,你是在一个函数调用中设置它们,我认为这是错误的,那就是为同一件事设置多个观察者。它应该只设置一次。
所以我建议你把你的观察者带到一个单独的函数中,然后像这样调用它observeProperties()
所以你的代码会是这样的
private fun observeProperties() {
viewModel.searchWeatherForecastResponse.observe(viewLifecycleOwner, Observer { response ->
when (response) {
is Resource.Success -> {
response.data?.let { forecastResponse ->
forecastAdapter.diff.submitList(forecastResponse.list)
swipeRefreshLayout.isRefreshing = false
}
}
is Resource.Error -> {
response.msg?.let { msg ->
Log.e(TAG, "An error occurred : $msg")
swipeRefreshLayout.isRefreshing = false
}
}
is Resource.Loading -> {
swipeRefreshLayout.isRefreshing = false
}
}
})
viewModel.weatherForecastLocation.observe(viewLifecycleOwner, Observer { response ->
when (response) {
is Resource.Success -> {
response.data?.let { forecastResponse ->
forecastAdapter.diff.submitList(forecastResponse.list)
forecastAdapter.notifyDataSetChanged()
swipeRefreshLayout.isRefreshing = false
}
}
is Resource.Error -> {
response.msg?.let { msg ->
Log.e(TAG, "An error occurred : $msg")
swipeRefreshLayout.isRefreshing = false
}
}
is Resource.Loading -> {
swipeRefreshLayout.isRefreshing = false
}
}
})
viewModel.searchWeatherForecastResponse.observe(viewLifecycleOwner, Observer { response ->
when (response) {
is Resource.Success -> {
response.data?.let { forecastResponse ->
forecastAdapter.diff.submitList(forecastResponse.list)
swipeRefreshLayout.isRefreshing = false
}
}
is Resource.Error -> {
response.msg?.let { msg ->
Log.e(TAG, "An error occurred : $msg")
swipeRefreshLayout.isRefreshing = false
}
}
is Resource.Loading -> {
swipeRefreshLayout.isRefreshing = false
}
}
})
}
现在您可以在顶部调用此方法
setupRecyclerView()
getLastLocation()
updateSupportActionbarTitle()
swipeRefreshLayout.setOnRefreshListener(this)
observeProperties()
您的其他方法现在将是这样的查询
private fun getWeatherApiDataLocation(city: String) {
viewModel.weatherForecastLocation(viewModel.applyLocationQuery(city))
}
private fun searchWeatherApiData(searchQuery: String) {
viewModel.searchWeatherForecast(viewModel.applySearchQuery(searchQuery))
}
推荐阅读
- spring-data-jpa - 使用 queryDSL 和 Spring Data JPA 查询同一属性的多个值
- kubernetes - 在不破坏集群的情况下更改容器运行时
- vue.js - v-for:数组元素和属性的解构
- python - matplotlib:插入轴的事件处理和导航
- sql - 日期转换问题 ORACLE SQL
- excel - 在锁定单元格上粘贴excel中的值
- java - log4j2 - 一个附加程序可以“同时”管理多个文件吗?
- sql - 计算两个单独 SQL 表的总数
- c - 如何将返回的数组分配给 main() 上的数组
- typescript - 如何让函数传递具有相同重载的参数?