问题
My app is a basic news app which fetches data from JSON provided by Guardian API. I parsed the values from JSON using raw java code (not using retrofit).
Then I get the LiveData in NewsFeedViewModel class which extends as AndroidViewModel.
And then in the fragment, I submit list to adapter.
These are the issues I'm facing: 1) at first, if the articles to show is set to 10, then if i go to settings and change it to 2, then the last 8 articles are disappearing but the white space /gap is not going. I can still scroll through the empty gap. 2) if i change the number of articles value constantly, then app is becoming un-scrollable.
And i have a few more doubts, how to refresh the data manually when swipeToRefresh is happened?
This is my project github link: https://github.com/sdzshn3/News24-7-RV
Video sample of the issue happening in app: https://drive.google.com/file/d/1gr_fabS2rqREuyecvGSG3IQ_jXOowlW7/view?usp=drivesdk
回答1:
You need to do exactly what I did in this Reddit post:
public class RefreshLiveData<T> extends MutableLiveData<T> {
public interface RefreshAction<T> {
private interface Callback<T> {
void onDataLoaded(T t);
}
void loadData(Callback<T> callback);
}
private final RefreshAction<T> refreshAction;
private final Callback<T> callback = new RefreshAction.Callback<T>() {
@Override
public void onDataLoaded(T t) {
postValue(t);
}
};
public RefreshLiveData(RefreshAction<T> refreshAction) {
this.refreshAction = refreshAction;
}
public final void refresh() {
refreshAction.loadData(callback);
}
}
Then you can do
public class YourViewModel extends ViewModel {
private final GithubRepository githubRepository;
public YourViewModel(GithubRepository githubRepository, SavedStateHandle savedStateHandle) {
this.githubRepository = githubRepository;
}
private final LiveData<String> userId = savedStateHandle.getLiveData("userId"); // from args
private final RefreshLiveData<List<Project>> refreshLiveData = Transformations.switchMap(userId, (uId) -> {
return githubRepository.getProjectList(uId);
});
public void refreshData() {
refreshLiveData.refresh();
}
public LiveData<List<Project>> getProjects() {
return refreshLiveData;
}
}
And then repository can do:
public RefreshLiveData<List<Project>> getProjectList(String userId) {
final RefreshLiveData<List<Project>> liveData = new RefreshLiveData<>((callback) -> {
githubService.getProjectList(userId).enqueue(new Callback<List<Project>>() {
@Override
public void onResponse(Call<List<Project>> call, Response<List<Project>> response) {
callback.onDataLoaded(response.body());
}
@Override
public void onFailure(Call<List<Project>> call, Throwable t) {
}
});
});
return liveData;
}
回答2:
In kotlin style:
class RefreshableLiveData<T>(
private val source: () -> LiveData<T>
) : MediatorLiveData<T>() {
private var liveData = source()
init {
this.addSource(liveData, ::observer)
}
private fun observer(data: T) {
value = data
}
fun refresh() {
this.removeSource(liveData)
liveData = source()
this.addSource(liveData, ::observer)
}
}
Example:
class MainActivity : AppCompatActivity() {
private val useCase = TestUseCase(DataSource())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
useCase.goals.observe(this) {
// update UI
}
// refresh
useCase.refresh()
}
}
class TestUseCase(dataSource: DataSource) : CommonUseCase() {
private val _goals = RefreshableLiveData {
callService { dataSource.loadData() }
}
val goals: LiveData<Resource<Boolean>> = _goals
fun refresh() {
_goals.refresh()
}
}
class DataSource {
fun loadData() = Resource.Success(true)
}
回答3:
You can do it like this
In your ViewModel
public class NewsViewModel extends AndroidViewModel {
private NewsRepository newsRepository;
private MutableLiveData<List<News>> news;
public NewsViewModel() {
super(application);
newsRepository = new NewsRepository(application);
news = newsRepository.getMutableLiveData();
}
public MutableLiveData<List<News>> getAllNews() {
return news;
}
public void Refresh() {
newsRepository = new NewsRepository(application);
news = newsRepository.getMutableLiveData();
}
}
Then in your Activity
swipeRefreshLayout.setOnRefreshListener(() -> {
viewModel.Refresh();
viewModel.getNewsData().observe(this, news -> mAdapter.setNews(news));
swipeRefreshLayout.setRefreshing(false);
});
来源:https://stackoverflow.com/questions/53467820/how-to-properly-reload-livedata-manually-in-android