使用 android 分页库的动态查询
dynamic query using android paging library
我正在制作一个新闻应用程序,我正在为我的新闻使用分页库 recyclerview.I 想根据 categories.I 显示新闻,我正在从对话框中选择类别并将其分配给 main 中的 texview activity.Here 是我的 app.I 想要观察该值并根据该值发送请求 dynamically.I 使用改造作为网络库。
MainActivity.java
public class MainActivity extends AppCompatActivity{
private MainActivityViewModel mViewModel;
private NewsAdapter mAdapter;
//widgets
@BindView(R.id.recyclerView)
RecyclerView mRecyclerView;
@BindView(R.id.toolbar)
MaterialToolbar toolbar;
@BindView(R.id.searchView)
SearchView mSearchView;
@BindView(R.id.categoryTv)
TextView categoryTv;
private String [] categoryArray;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mAdapter = new NewsAdapter(this);
categoryArray=getResources().getStringArray(R.array.category_array);
mViewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(this
.getApplication())).get(MainActivityViewModel.class);
setSupportActionBar(toolbar);
initRecyclerView();
observeViewModel();
mRecyclerView.setAdapter(mAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_toolbar_menu,menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int id=item.getItemId();
if(id==R.id.category){
Dialog d=new Dialog(this);
d.setContentView(R.layout.number_picker);
Button selectBtn=d.findViewById(R.id.select);
NumberPicker np=d.findViewById(R.id.numberPicker);
np.setMinValue(0);
np.setTextColor(getResources().getColor(R.color.colorPrimary));
np.setMaxValue(categoryArray.length-1);
np.setDisplayedValues(categoryArray);
np.setWrapSelectorWheel(false);
selectBtn.setOnClickListener(v -> {
categoryTv.setText(categoryArray[np.getValue()]);
d.dismiss();
});
d.show();
}
return super.onOptionsItemSelected(item);
}
private void initRecyclerView(){
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layoutManager);
}
private void observeViewModel() {
mViewModel.newsPagedList.observe(this, newsModels -> {
mAdapter.submitList(newsModels);
});
mRecyclerView.setAdapter(mAdapter);
}
}
MainActivityViewModel.java
public class MainActivityViewModel extends AndroidViewModel {
private LiveData<NewsDataSource> liveDataSource;
public LiveData<PagedList<NewsModel>> newsPagedList;
public MainActivityViewModel(Application application) {
super(application);
init();
}
private void init() {
NewsDataSourceFactory newsFactory=new NewsDataSourceFactory();
liveDataSource=newsFactory.getNewsDataSourceMutableLiveData();
PagedList.Config config=new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPageSize(PAGE_SIZE)
.build();
newsPagedList=new LivePagedListBuilder<>(newsFactory,config).build();
}
}
NewsDataSource.java
public class NewsDataSource extends PageKeyedDataSource<Integer, NewsModel> {
Single<Response> mResponse;
List<NewsModel> newsResponse;
@Override
public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, NewsModel> callback) {
RetroService service = RetroService.getInstance();
mResponse = service.getData(FIRST_PAGE, PAGE_SIZE, "us", API_KEY,"general");
mResponse.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Response>() {
@Override
public void onSubscribe(@io.reactivex.rxjava3.annotations.NonNull Disposable d) {
}
@Override
public void onSuccess(@io.reactivex.rxjava3.annotations.NonNull Response response) {
newsResponse = response.getNewsList();
callback.onResult(newsResponse, null, FIRST_PAGE + 1);
}
@Override
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
}
});
}
@Override
public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, NewsModel> callback) {
RetroService service = RetroService.getInstance();
mResponse = service.getData(FIRST_PAGE, PAGE_SIZE, "us", API_KEY,"general");//Want to change that general data dynamically.
mResponse.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Response>() {
@Override
public void onSubscribe(@io.reactivex.rxjava3.annotations.NonNull Disposable d) {
}
@Override
public void onSuccess(@io.reactivex.rxjava3.annotations.NonNull Response response) {
newsResponse = response.getNewsList();
if (newsResponse != null) {
int key;
if (params.key > 1) {
key = params.key - 1;
} else {
key = 0;
}
callback.onResult(newsResponse, key);
}
}
@Override
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
}
});
}
@Override
public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, NewsModel> callback) {
RetroService service = RetroService.getInstance();
mResponse = service.getData(params.key, PAGE_SIZE, "us",API_KEY,"general");
mResponse.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Response>() {
@Override
public void onSubscribe(@io.reactivex.rxjava3.annotations.NonNull Disposable d) {
}
@Override
public void onSuccess(@io.reactivex.rxjava3.annotations.NonNull Response response) {
newsResponse = response.getNewsList();
if (newsResponse != null) {
callback.onResult(newsResponse, params.key + 1);
}
}
@Override
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
}
});
}
}
NewsDataSourceFactory.java
public class NewsDataSourceFactory extends DataSource.Factory<Integer, NewsModel> {
private MutableLiveData<NewsDataSource> newsDataSourceMutableLiveData;
NewsDataSource newsDataSource;
@NonNull
@Override
public DataSource<Integer, NewsModel> create() {
newsDataSource = new NewsDataSource();
newsDataSourceMutableLiveData=new MutableLiveData<>();
newsDataSourceMutableLiveData.postValue(newsDataSource);
return newsDataSource;
}
public MutableLiveData<NewsDataSource> getNewsDataSourceMutableLiveData() {
return newsDataSourceMutableLiveData;
}
}
RetroApi.java
public interface RetroApi {
@GET("top-headlines")
Single<Response> getAllNews(@Query("page") int page,
@Query("pageSize") int pageSize,
@Query("country") String country,
@Query("apiKey") String apiKey,
@Query("category")String category);
}
RetroService.java
public class RetroService {
private static OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
private static RetroApi api;
private static RetroService instance;
public static RetroService getInstance(){
if(instance == null){
instance=new RetroService();
}
return instance;
}
private RetroService() {
api = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build()
.create(RetroApi.class);
}
public Single<Response> getData(int page,int pageSize,String country,String key,String category){
return api.getAllNews(page,pageSize,country,key,category);
}
}
NewsAdapter.java
public class NewsAdapter extends PagedListAdapter<NewsModel,NewsAdapter.NewsViewHolder> {
private Context context;
private List<NewsModel> mNews;
private static final DiffUtil.ItemCallback<NewsModel> NEWS_COMPARATOR=new DiffUtil.ItemCallback<NewsModel>() {
@Override
public boolean areItemsTheSame(@NonNull NewsModel oldItem, @NonNull NewsModel newItem) {
return oldItem.getTitle() == newItem.getTitle();
}
@SuppressLint("DiffUtilEquals")
@Override
public boolean areContentsTheSame(@NonNull NewsModel oldItem, @NonNull NewsModel newItem) {
return oldItem.equals(newItem);
}
};
public NewsAdapter(Context context) {
super(NEWS_COMPARATOR);
this.context=context;
}
@NonNull
@Override
public NewsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater mInflater= LayoutInflater.from(parent.getContext());
NewsItemBinding binding=DataBindingUtil.inflate(mInflater,R.layout.news_item,parent,false);
return new NewsViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull NewsViewHolder holder, int position) {
holder.bindData(getItem(position));
holder.binding.setClickListener(news -> {
setupBottomSheetDialog(news);
});
}
private void setupBottomSheetDialog(NewsModel mNew) {
LayoutBottomSheetBinding binding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.layout_bottom_sheet, null, false);
BottomSheetDialog dialogBox=new BottomSheetDialog(context);
View v=binding.getRoot();
dialogBox.setContentView(v);
binding.setMNew(mNew);
dialogBox.show();
}
class NewsViewHolder extends RecyclerView.ViewHolder{
NewsItemBinding binding;
public NewsViewHolder(@NonNull NewsItemBinding binding) {
super(binding.getRoot());
this.binding=binding;
}
public void bindData(NewsModel model){
binding.setMNew(model);
binding.executePendingBindings();
}
}
}
我解决了我的问题,但我不建议way.I认为this.So可能有更好的解决方案如果你有更好的解决方案请让我know.Here是我的解决方案。首先,我将 public MutableLiveData<String> categoryLiveData=new MutableLiveData<>();
添加到我的 MainActivityViewModel class.Then 我将我的 init() 方法更改为
public void init(String category) {
NewsDataSourceFactory newsFactory=new NewsDataSourceFactory(category);
liveDataSource=newsFactory.getNewsDataSourceMutableLiveData();
PagedList.Config config=new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPageSize(PAGE_SIZE)
.build();
newsPagedList=new LivePagedListBuilder<>(newsFactory,config).build();
}
我将这些行添加到我的 MainActivity 以观察 categoryData,然后在内部 onOptionsItemSelected()
将选定的值分配给 categoryLiveData。
主要活动
mViewModel.categoryLiveData.postValue(categoryTv.getText().toString().toLowerCase());
mViewModel.categoryLiveData.observe(mActivity, s -> {
mViewModel.init(s);
observeViewModel();
});
selectBtn.setOnClickListener(v -> {
categoryTv.setText(categoryArray[np.getValue()]);
mViewModel.categoryLiveData.postValue(categoryTv.getText().toString().toLowerCase());
d.dismiss();
});
我使用类别参数向 NewsDataSourceFactory 添加了一个构造函数。
public NewsDataSourceFactory(String category) {
newsDataSource = new NewsDataSource(category);
}
最后在 NewsDataSource 中创建了一个构造函数并将类别作为参数传递。
private String category;
public NewsDataSource(String category){
this.category=category;
}
并将该类别传递给我的请求方法。
mResponse = service.getData(FIRST_PAGE, PAGE_SIZE, "us", API_KEY,category);
我正在制作一个新闻应用程序,我正在为我的新闻使用分页库 recyclerview.I 想根据 categories.I 显示新闻,我正在从对话框中选择类别并将其分配给 main 中的 texview activity.Here 是我的 app.I 想要观察该值并根据该值发送请求 dynamically.I 使用改造作为网络库。
MainActivity.java
public class MainActivity extends AppCompatActivity{
private MainActivityViewModel mViewModel;
private NewsAdapter mAdapter;
//widgets
@BindView(R.id.recyclerView)
RecyclerView mRecyclerView;
@BindView(R.id.toolbar)
MaterialToolbar toolbar;
@BindView(R.id.searchView)
SearchView mSearchView;
@BindView(R.id.categoryTv)
TextView categoryTv;
private String [] categoryArray;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mAdapter = new NewsAdapter(this);
categoryArray=getResources().getStringArray(R.array.category_array);
mViewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(this
.getApplication())).get(MainActivityViewModel.class);
setSupportActionBar(toolbar);
initRecyclerView();
observeViewModel();
mRecyclerView.setAdapter(mAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_toolbar_menu,menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int id=item.getItemId();
if(id==R.id.category){
Dialog d=new Dialog(this);
d.setContentView(R.layout.number_picker);
Button selectBtn=d.findViewById(R.id.select);
NumberPicker np=d.findViewById(R.id.numberPicker);
np.setMinValue(0);
np.setTextColor(getResources().getColor(R.color.colorPrimary));
np.setMaxValue(categoryArray.length-1);
np.setDisplayedValues(categoryArray);
np.setWrapSelectorWheel(false);
selectBtn.setOnClickListener(v -> {
categoryTv.setText(categoryArray[np.getValue()]);
d.dismiss();
});
d.show();
}
return super.onOptionsItemSelected(item);
}
private void initRecyclerView(){
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layoutManager);
}
private void observeViewModel() {
mViewModel.newsPagedList.observe(this, newsModels -> {
mAdapter.submitList(newsModels);
});
mRecyclerView.setAdapter(mAdapter);
}
}
MainActivityViewModel.java
public class MainActivityViewModel extends AndroidViewModel {
private LiveData<NewsDataSource> liveDataSource;
public LiveData<PagedList<NewsModel>> newsPagedList;
public MainActivityViewModel(Application application) {
super(application);
init();
}
private void init() {
NewsDataSourceFactory newsFactory=new NewsDataSourceFactory();
liveDataSource=newsFactory.getNewsDataSourceMutableLiveData();
PagedList.Config config=new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPageSize(PAGE_SIZE)
.build();
newsPagedList=new LivePagedListBuilder<>(newsFactory,config).build();
}
}
NewsDataSource.java
public class NewsDataSource extends PageKeyedDataSource<Integer, NewsModel> {
Single<Response> mResponse;
List<NewsModel> newsResponse;
@Override
public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, NewsModel> callback) {
RetroService service = RetroService.getInstance();
mResponse = service.getData(FIRST_PAGE, PAGE_SIZE, "us", API_KEY,"general");
mResponse.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Response>() {
@Override
public void onSubscribe(@io.reactivex.rxjava3.annotations.NonNull Disposable d) {
}
@Override
public void onSuccess(@io.reactivex.rxjava3.annotations.NonNull Response response) {
newsResponse = response.getNewsList();
callback.onResult(newsResponse, null, FIRST_PAGE + 1);
}
@Override
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
}
});
}
@Override
public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, NewsModel> callback) {
RetroService service = RetroService.getInstance();
mResponse = service.getData(FIRST_PAGE, PAGE_SIZE, "us", API_KEY,"general");//Want to change that general data dynamically.
mResponse.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Response>() {
@Override
public void onSubscribe(@io.reactivex.rxjava3.annotations.NonNull Disposable d) {
}
@Override
public void onSuccess(@io.reactivex.rxjava3.annotations.NonNull Response response) {
newsResponse = response.getNewsList();
if (newsResponse != null) {
int key;
if (params.key > 1) {
key = params.key - 1;
} else {
key = 0;
}
callback.onResult(newsResponse, key);
}
}
@Override
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
}
});
}
@Override
public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, NewsModel> callback) {
RetroService service = RetroService.getInstance();
mResponse = service.getData(params.key, PAGE_SIZE, "us",API_KEY,"general");
mResponse.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Response>() {
@Override
public void onSubscribe(@io.reactivex.rxjava3.annotations.NonNull Disposable d) {
}
@Override
public void onSuccess(@io.reactivex.rxjava3.annotations.NonNull Response response) {
newsResponse = response.getNewsList();
if (newsResponse != null) {
callback.onResult(newsResponse, params.key + 1);
}
}
@Override
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
}
});
}
}
NewsDataSourceFactory.java
public class NewsDataSourceFactory extends DataSource.Factory<Integer, NewsModel> {
private MutableLiveData<NewsDataSource> newsDataSourceMutableLiveData;
NewsDataSource newsDataSource;
@NonNull
@Override
public DataSource<Integer, NewsModel> create() {
newsDataSource = new NewsDataSource();
newsDataSourceMutableLiveData=new MutableLiveData<>();
newsDataSourceMutableLiveData.postValue(newsDataSource);
return newsDataSource;
}
public MutableLiveData<NewsDataSource> getNewsDataSourceMutableLiveData() {
return newsDataSourceMutableLiveData;
}
}
RetroApi.java
public interface RetroApi {
@GET("top-headlines")
Single<Response> getAllNews(@Query("page") int page,
@Query("pageSize") int pageSize,
@Query("country") String country,
@Query("apiKey") String apiKey,
@Query("category")String category);
}
RetroService.java
public class RetroService {
private static OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
private static RetroApi api;
private static RetroService instance;
public static RetroService getInstance(){
if(instance == null){
instance=new RetroService();
}
return instance;
}
private RetroService() {
api = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build()
.create(RetroApi.class);
}
public Single<Response> getData(int page,int pageSize,String country,String key,String category){
return api.getAllNews(page,pageSize,country,key,category);
}
}
NewsAdapter.java
public class NewsAdapter extends PagedListAdapter<NewsModel,NewsAdapter.NewsViewHolder> {
private Context context;
private List<NewsModel> mNews;
private static final DiffUtil.ItemCallback<NewsModel> NEWS_COMPARATOR=new DiffUtil.ItemCallback<NewsModel>() {
@Override
public boolean areItemsTheSame(@NonNull NewsModel oldItem, @NonNull NewsModel newItem) {
return oldItem.getTitle() == newItem.getTitle();
}
@SuppressLint("DiffUtilEquals")
@Override
public boolean areContentsTheSame(@NonNull NewsModel oldItem, @NonNull NewsModel newItem) {
return oldItem.equals(newItem);
}
};
public NewsAdapter(Context context) {
super(NEWS_COMPARATOR);
this.context=context;
}
@NonNull
@Override
public NewsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater mInflater= LayoutInflater.from(parent.getContext());
NewsItemBinding binding=DataBindingUtil.inflate(mInflater,R.layout.news_item,parent,false);
return new NewsViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull NewsViewHolder holder, int position) {
holder.bindData(getItem(position));
holder.binding.setClickListener(news -> {
setupBottomSheetDialog(news);
});
}
private void setupBottomSheetDialog(NewsModel mNew) {
LayoutBottomSheetBinding binding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.layout_bottom_sheet, null, false);
BottomSheetDialog dialogBox=new BottomSheetDialog(context);
View v=binding.getRoot();
dialogBox.setContentView(v);
binding.setMNew(mNew);
dialogBox.show();
}
class NewsViewHolder extends RecyclerView.ViewHolder{
NewsItemBinding binding;
public NewsViewHolder(@NonNull NewsItemBinding binding) {
super(binding.getRoot());
this.binding=binding;
}
public void bindData(NewsModel model){
binding.setMNew(model);
binding.executePendingBindings();
}
}
}
我解决了我的问题,但我不建议way.I认为this.So可能有更好的解决方案如果你有更好的解决方案请让我know.Here是我的解决方案。首先,我将 public MutableLiveData<String> categoryLiveData=new MutableLiveData<>();
添加到我的 MainActivityViewModel class.Then 我将我的 init() 方法更改为
public void init(String category) {
NewsDataSourceFactory newsFactory=new NewsDataSourceFactory(category);
liveDataSource=newsFactory.getNewsDataSourceMutableLiveData();
PagedList.Config config=new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPageSize(PAGE_SIZE)
.build();
newsPagedList=new LivePagedListBuilder<>(newsFactory,config).build();
}
我将这些行添加到我的 MainActivity 以观察 categoryData,然后在内部 onOptionsItemSelected()
将选定的值分配给 categoryLiveData。
主要活动
mViewModel.categoryLiveData.postValue(categoryTv.getText().toString().toLowerCase());
mViewModel.categoryLiveData.observe(mActivity, s -> {
mViewModel.init(s);
observeViewModel();
});
selectBtn.setOnClickListener(v -> {
categoryTv.setText(categoryArray[np.getValue()]);
mViewModel.categoryLiveData.postValue(categoryTv.getText().toString().toLowerCase());
d.dismiss();
});
我使用类别参数向 NewsDataSourceFactory 添加了一个构造函数。
public NewsDataSourceFactory(String category) {
newsDataSource = new NewsDataSource(category);
}
最后在 NewsDataSource 中创建了一个构造函数并将类别作为参数传递。
private String category;
public NewsDataSource(String category){
this.category=category;
}
并将该类别传递给我的请求方法。
mResponse = service.getData(FIRST_PAGE, PAGE_SIZE, "us", API_KEY,category);