使用 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);