I am making a news app and I am using paging library for my news recyclerview.I want to show news based on categories.I am choosing category from dialogbox and assigning it to texview inside main activity.Here is my app.I want to observe that value and sending request based on that value dynamically.I'm using retrofit as network library.
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();
}
}
}
I solved my problem but I don't recommend that way.I think there might be a better solution for this.So if you have a better solution please let me know.Here is my solution. Firstly I added public MutableLiveData<String> categoryLiveData=new MutableLiveData<>();
to my MainActivityViewModel class.Then ı changed my init() method to
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();
}
I added these lines to my MainActivity to observe categoryData and then inside onOptionsItemSelected()
assigned the selected value to categoryLiveData.
MainActivity
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();
});
I added a constructor to NewsDataSourceFactory with category parameter.
public NewsDataSourceFactory(String category) {
newsDataSource = new NewsDataSource(category);
}
Lastly created a constructor in NewsDataSource and passed category as an argument.
private String category;
public NewsDataSource(String category){
this.category=category;
}
and passed that category to my request method.
mResponse = service.getData(FIRST_PAGE, PAGE_SIZE, "us", API_KEY,category);