android architecture components ViewModel & PageKeyedDataSource issue - android

What I want: I want to implement google paging library. Which loads some number of data (let's say first 10). And while you scroll up It makes API call and loads more data.
What's issue: It is loading all the data instead of some data (let's say first 10). I have used ViewModel & PageKeyedDataSource for that.
What I tried: Code is below
InProgressFragment.java
public class InProgressFragment extends BaseFragment implements SalesDashboardView {
#BindView(R.id.recyclerview_applicants_list)
RecyclerView applicantsListRecyclerView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_applicants, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
setupRecyclerView();
}
private void setupRecyclerView() {
ApplicantsDataAdapter2 adapter = new ApplicantsDataAdapter2();
applicantsListRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
applicantsListRecyclerView.setHasFixedSize(true);
applicantsListRecyclerView.setAdapter(adapter);
SearchLoansInput searchLoansInput = new SearchLoansInput();
searchLoansInput.setUserName(sessionManager.getMobileNumber());
adapter.submitList(salesDashboardViewModel.getLoanDetailOutput(searchLoansInput));
}
}
SalesDashboardViewModel.java
public class SalesDashboardViewModel extends ViewModel {
private static final String TAG = SalesDashboardViewModel.class.getSimpleName();
private int DEFAULT_PER_PAGE = 3;
private MainThreadExecutor executor;
private SalesLoginInquiryUseCase mSalesInquiryUseCase;
#Inject
public SalesDashboardViewModel(SalesLoginInquiryUseCase salesLoginInquiryUseCase) {
mSalesInquiryUseCase = salesLoginInquiryUseCase;
}
public PagedList<LoanDetailOutput> getLoanDetailOutput(SearchLoansInput searchLoansInput) {
executor = new MainThreadExecutor();
SearchDataSource dataSource = new SearchDataSource(searchLoansInput, mSalesInquiryUseCase);
PagedList.Config config = new PagedList.Config.Builder()
.setPageSize(DEFAULT_PER_PAGE)
.setInitialLoadSizeHint(DEFAULT_PER_PAGE * 2)
.setEnablePlaceholders(true)
.build();
PagedList<LoanDetailOutput> loanDetailOutputPagedList =
new PagedList.Builder<>(dataSource, config)
.setFetchExecutor(executor)
.setNotifyExecutor(executor)
.build();
return loanDetailOutputPagedList;
}
}
SearchDataSource.java
public class SearchDataSource extends PageKeyedDataSource<Integer, LoanDetailOutput> {
private static final String TAG = SearchDataSource.class.getSimpleName();
private SearchLoansInput searchLoansInput;
private SalesLoginInquiryUseCase mSalesInquiryUseCase;
public SearchDataSource(SearchLoansInput searchLoansInput, SalesLoginInquiryUseCase mSalesInquiryUseCase) {
this.searchLoansInput = searchLoansInput;
this.mSalesInquiryUseCase = mSalesInquiryUseCase;
}
#Override
public void loadInitial(#NonNull LoadInitialParams<Integer> params,
#NonNull LoadInitialCallback<Integer, LoanDetailOutput> callback) {
Log.e("mk", "loadInitial, requestedLoadSize: " + params.requestedLoadSize);
final int page = 1;
mSalesInquiryUseCase.setSalesLoginInquiryBody(searchLoansInput);
mSalesInquiryUseCase.execute()
.subscribe(new DisposableObserver<SearchLoansOutput>() {
#Override
public void onNext(SearchLoansOutput value) {
callback.onResult(
value.getLoanDetailList(),
0,
value.getCountLA(),
null,
page + 1);
Log.e("mk", "loadInitial, List Size: " + value.getLoanDetailList().size());
Log.e("mk", "loadInitial, TotalCount: " + value.getCountLA());
}
#Override
public void onError(Throwable throwable) {
throwable.printStackTrace();
}
#Override
public void onComplete() {
}
});
}
#Override
public void loadBefore(#NonNull LoadParams<Integer> params, #NonNull LoadCallback<Integer, LoanDetailOutput> callback) {
}
#Override
public void loadAfter(#NonNull LoadParams<Integer> params, #NonNull LoadCallback<Integer, LoanDetailOutput> callback) {
Log.e("mk", "loadAfter, requestedLoadSize: " + params.requestedLoadSize);
final int page = params.key;
mSalesInquiryUseCase.setSalesLoginInquiryBody(searchLoansInput);
mSalesInquiryUseCase.execute()
.subscribe(new DisposableObserver<SearchLoansOutput>() {
#Override
public void onNext(SearchLoansOutput value) {
callback.onResult(
value.getLoanDetailList(),
page + 1);
Log.e("mk", "loadAfter, List Size: " + value.getLoanDetailList().size());
}
#Override
public void onError(Throwable throwable) {
throwable.printStackTrace();
}
#Override
public void onComplete() {
}
});
}
}
ApplicantsDataAdapter2.java
public class ApplicantsDataAdapter2 extends PagedListAdapter<LoanDetailOutput, ApplicantsDataAdapter2.ApplicantsViewHolder> {
public ApplicantsDataAdapter2() {
super(DIFF_CALLBACK);
}
#Override
public ApplicantsDataAdapter2.ApplicantsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_applicant, parent, false);
return new ApplicantsDataAdapter2.ApplicantsViewHolder(itemView);
}
#Override
public void onBindViewHolder(ApplicantsDataAdapter2.ApplicantsViewHolder holder, int position) {
holder.bindView(getItem(position));
}
public class ApplicantsViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.tv_item_applicant_name)
TextView mApplicantNameTextView;
#BindView(R.id.tv_item_applicant_status)
TextView mApplicantStatusTextView;
#BindView(R.id.tv_item_applicant_creation_date)
TextView mApplicantDateCreationTextView;
ApplicantsViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
void bindView(LoanDetailOutput loanDetailOutput) {
if (loanDetailOutput != null) {
mApplicantNameTextView.setText(loanDetailOutput.getFirstName());
mApplicantStatusTextView.setText(loanDetailOutput.getStatus().getDisplayName());
SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy");
String createdDate = null;
if (loanDetailOutput.getCreatedDate() != null) {
createdDate = String.valueOf(sdf.format(loanDetailOutput.getCreatedDate()));
}
mApplicantDateCreationTextView.setText(createdDate);
} else {
mApplicantNameTextView.setText("Loading...");
mApplicantStatusTextView.setText("Loading...");
mApplicantDateCreationTextView.setText("Loading...");
}
}
}
private static DiffUtil.ItemCallback<LoanDetailOutput> DIFF_CALLBACK = new DiffUtil.ItemCallback<LoanDetailOutput>() {
#Override
public boolean areItemsTheSame(LoanDetailOutput oldItem, LoanDetailOutput newItem) {
return oldItem.getId() == newItem.getId();
}
#Override
public boolean areContentsTheSame(LoanDetailOutput oldItem, LoanDetailOutput newItem) {
return true;
}
};
}
The main problem is method loadAfter of SearchDataSource class is never invoked.
Any help will be appreciated.
Edit: Added screenshot of logcat. It might be helpful.

Related

Unable to get an instance of ViewModel

I am trying to get some data from a website and load it into my app.
The app has 1 MainActivity and 3 fragments and uses ViewBinding.
Unfortunately I can not create a instance of viewmodel in the 1st Fragment.
I already searched stackoverflow and the internet (a lot) but I somehow can not get all the solutions given working.
I get the exception on materialViewModel.getMaterials().observe in the HomeFragment.
Any help is highly appreciated.
My HomeFragment:
public class HomeFragment extends Fragment {
private MaterialViewModel materialViewModel;
private FragmentHomeBinding binding;
ArrayList<MaterialModel> posts;
public HomeFragment() {
}
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentHomeBinding.inflate(inflater, container, false);
materialViewModel = new ViewModelProvider(getActivity()).get(MaterialViewModel.class);
initializeRecyclerView();
getPosts();
return binding.getRoot();
}
private void initializeRecyclerView() {
posts = new ArrayList<>();
binding.rvMain.setLayoutManager(new LinearLayoutManager(getActivity()));
}
private void getPosts() {
materialViewModel.getMaterials().observe(getActivity(), new Observer<List<MaterialModel>>() {
#Override
public void onChanged(#Nullable List<MaterialModel> posts) {
binding.rvMain.setAdapter(new ItemAdapter((ArrayList<MaterialModel>) posts));
}
});
getIsLoaded();
}
public void getIsLoaded(){
materialViewModel.getIsLoaded().observe(getActivity(), new Observer<Boolean>() {
#Override
public void onChanged(#Nullable Boolean aBoolean) {
if (aBoolean == true){
//activityMainBinding.progressBarID.setVisibility(View.GONE);
Log.w("PAR-LOADED ", "DATA IS LOADED");
}
}
});
}
#Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
My MaterialViewmodel:
public class MaterialViewModel extends AndroidViewModel {
private MutableLiveData<List<MaterialModel>> matts;
private MaterialRepository materialRepository;
public MaterialViewModel(Application application) {
super(application);
materialRepository = new MaterialRepository(application);
}
public void init() {
if (this.matts == null) {
matts = materialRepository.getAllMaterials();
}
}
public LiveData<List<MaterialModel>> getMaterials() {
return this.matts;
}
public MutableLiveData<Boolean> getIsLoaded() {
return materialRepository.dataIsLoaded;
}
}
My MaterialRepository:
public class MaterialRepository {
API_Service service;
private String posts;
ArrayList<String> arrayLiNames = new ArrayList<>();
ArrayList<String> arrayLiLinks = new ArrayList<>();
public ArrayList<MaterialModel> materialData = new ArrayList<>();
public MutableLiveData<List<MaterialModel>> mattis;
public MutableLiveData<Boolean> dataIsLoaded;
public MaterialRepository(Application application) {
super();
service = RetrofitClass.getClient(application).create(API_Service.class);
}
public MutableLiveData<List<MaterialModel>> getAllMaterials() {
Call<List<MaterialModel>> postResponseCall = service.getMaterials();
postResponseCall.enqueue(new Callback<List<MaterialModel>>() {
#Override
public void onResponse(Call<List<MaterialModel>> call, Response<List<MaterialModel>> response) {
if (response.code() == 200) {
Log.w("PAR-RESPONSE-CODE ", String.valueOf(response.code()));
posts = String.valueOf(response.body());
//new MyTask().execute();
}
}
#Override
public void onFailure(Call<List<MaterialModel>> call, Throwable t) {
}
});
return mattis;
}
private class MyTask extends AsyncTask<Void, Void, List<MaterialModel>> {
...
#Override
protected List<MaterialModel> doInBackground(Void... params) {
#Override
protected void onPostExecute(List<MaterialModel> result) {
mattis.setValue(result);
dataIsLoaded.setValue(true);
}
}
}

Fetching data with retrofit2 and saving in room

I am using retrofit2 for fetching data from the server and after fetching saving data in room database and then showing in recycler view. But it is no displayed (my data what I get using retrofit). I try display it in my fragment. In file ...data/data/databese/source.db these data are saved. I see it. So, that means that my code works. But I can't understand why it is not displayed.
my database class:
#Database(entities = {Source.class}, exportSchema = false, version = 1)
public abstract class SourceDatabase extends RoomDatabase {
private static final String DB_NAME = "source.db";
public abstract SourceDao sourceDao();
private static SourceDatabase instance;
public static SourceDatabase getInstance(Context context) {
if (instance == null) {
instance =buildDatabaseInstance(context);
}
return instance;
}
private static SourceDatabase buildDatabaseInstance(Context context) {
return Room.databaseBuilder(context,
SourceDatabase.class,
DB_NAME).build();
}
}
repository:
public class DataBaseRepository {
private static DataBaseRepository dataBaseRepository;
private SourceDao sourceDao;
private LiveData<List<Source>> allSourcestoDb;
private Context context;
public static DataBaseRepository getInstance(Context context) {
if (dataBaseRepository == null) {
dataBaseRepository = new DataBaseRepository(context);
}
return dataBaseRepository;
}
public DataBaseRepository(Context context) {
this.context = context;
SourceDatabase db = SourceDatabase.getInstance(context);
sourceDao = db.sourceDao();
allSourcestoDb = sourceDao.getSources();
}
public void getSourceListTodb(String key) {//отправка данных в LiveData
RestClient restClient = RestClient.getInstance();
restClient.startRetrofit();
restClient.getServerApi().getNews(key).enqueue(new Callback<News>() {
#Override
public void onResponse(Call<News> call, Response<News> response) {
Completable.fromAction(new Action (){
#Override
public void run() throws Exception {
if (response.body() != null) {
List<Source> list = response.body().getSources();
sourceDao.insert(list);
}
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onComplete() {
}
#Override
public void onError(Throwable e) {
}
});
}
#Override
public void onFailure(Call<News> call, Throwable t) {
Log.d("error", "Can't parse data " + t);
}
});
}
public LiveData<List<Source>> getAllSourcestoDb() {
return allSourcestoDb;
}
}
dao:
#Dao
public interface SourceDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(List<Source> sources);
#Query("SELECT * FROM source")
LiveData<List<Source>> getSources();
}
viewModel:
public class SourceViewModel extends AndroidViewModel {
private DataBaseRepository dataBaseRepository;
private LiveData<List<Source>> allSources; //for db
public SourceViewModel(#NonNull Application application) {
super(application);
dataBaseRepository =DataBaseRepository.getInstance(application); //for db
allSources = dataBaseRepository.getAllSourcestoDb();
}
public LiveData<List<Source>> getAllSources() {
return allSources;
}
}
and fragment:
public class SavedDataFragment extends Fragment {
private SourceViewModel sourceViewModel;
private DataBaseRepository dataBaseRepository;
private RecyclerView recyclerView;
private List<Source> sourceList;
private SavedDataAdapter adapter;
public SavedDataFragment() {
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.saved_data,container,false);
DataSharedPreference sharedPreference = DataSharedPreference.getSPInstance();
String api_key = sharedPreference.loadText(getActivity());
dataBaseRepository = new DataBaseRepository(getActivity());
sourceViewModel = ViewModelProviders.of(this).get(SourceViewModel.class);
recyclerView = view.findViewById(R.id.recyclerViewSavedFragment);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
sourceList = new ArrayList<>();
adapter = new SavedDataAdapter(getActivity(), sourceList);
recyclerView.setAdapter(adapter);
sourceViewModel.getAllSources().observe(this, new Observer<List<Source>>() {
#Override
public void onChanged(List<Source> sources) {
adapter.setSourceList(sourceList);
}
});
dataBaseRepository.getSourceListTodb(api_key);
return view;
}
}
adapter:
public class SavedDataAdapter extends RecyclerView.Adapter<SavedDataAdapter.SourceSavedViewHolder> {
private LayoutInflater inflater;
private List<Source> sources;
public SavedDataAdapter(Context context, List<Source> sources) {
this.sources = sources;
this.inflater = LayoutInflater.from(context);
}
#NonNull
#Override
public SavedDataAdapter.SourceSavedViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.saved_item, parent, false);
return new SourceSavedViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final SavedDataAdapter.SourceSavedViewHolder holder, int position) {
final Source source = sources.get(position);
holder.sourceId.setText(source.getId());
holder.sourceName.setText(source.getName());
holder.sourceDescription.setText(source.getDescription());
holder.sourceURL.setText(source.getUrl());
holder.sourceCategory.setText(source.getCategory());
holder.sourceLanguage.setText(source.getLanguage());
holder.sourceCountry.setText(source.getCountry());
}
#Override
public int getItemCount() {
return sources.size();
}
public void setSourceList(List<Source> sources) {
this.sources = sources;
notifyDataSetChanged();
}
public static class SourceSavedViewHolder extends RecyclerView.ViewHolder {
TextView sourceName, sourceId, sourceDescription, sourceURL, sourceCategory, sourceLanguage, sourceCountry;
public SourceSavedViewHolder(View view) {
super(view);
sourceName = view.findViewById(R.id.sourceName);
sourceId = view.findViewById(R.id.sourceIdItem);
sourceDescription = view.findViewById(R.id.sourceDescription);
sourceURL = view.findViewById(R.id.sourceURL);
sourceCategory = view.findViewById(R.id.sourceCategory);
sourceLanguage = view.findViewById(R.id.sourceLanguage);
sourceCountry = view.findViewById(R.id.sourceCountry);
}
}
}
In your Fragment inside onChanged,
you're setting adapter.setSourceList(sourceList) where sourceList is an empty arrayList.
You should instead setSourceList to sources which is the updated list passed as an argument to onChanged method
That is :-
sourceViewModel.getAllSources().observe(this, new Observer<List<Source>>() {
#Override
public void onChanged(List<Source> sources) {
adapter.setSourceList(sources); // sources and not sourceList
}
});
Also there are few more things that should be taken care of.
For ex- in your observe method, you have passed this as first argument which is wrong when using Fragments as it may causes memory leaks. Instead you should pass viewLifeOwner..
More can found on this link Use viewLifecycleOwner as the LifecycleOwner
Try ti change this:
#Query("SELECT * FROM source")
To:
#Query("SELECT * FROM Source")

Android Jetpack pagination not showing data of more than 2 pages

I am using android jetpack's pagination library. I am able to get data till page 2 , but i cannot get data of further pages even though i am passing params.key in my api call from loadAfter() method in Datasource. I am simple not able to understand where the problem is.
Heres my DataSource:
public class ApprovedBillsDataSource extends PageKeyedDataSource<Integer, BillsModel.Bills> {
static int FIRST_PAGE = 1;
String sortOrder;
String searchParam;
EmptyHandler emptyHandler;
ApprovedBillsDataSource(String sortOrder, String searchParam, EmptyHandler emptyHandler) {
this.sortOrder = sortOrder;
this.searchParam = searchParam;
this.emptyHandler = emptyHandler;
}
#Override
public void loadInitial(#NonNull LoadInitialParams<Integer> params, #NonNull final LoadInitialCallback<Integer, BillsModel.Bills> callback) {
RestClient.webServices().getCompletePartnerBills(FIRST_PAGE, 1, sortOrder, searchParam).enqueue(new Callback<ApiResponse<BillsModel>>() {
#Override
public void onResponse(Call<ApiResponse<BillsModel>> call, Response<ApiResponse<BillsModel>> response) {
if (response.code() == 200) {
callback.onResult(response.body().getData().getBills(), null, FIRST_PAGE + 1);
if (response.body().getData().getBills() == null || response.body().getData().getBills().size() == 0) {
emptyHandler.onListEmpty();
}
}
}
#Override
public void onFailure(Call<ApiResponse<BillsModel>> call, Throwable t) {
}
});
}
#Override
public void loadBefore(#NonNull LoadParams<Integer> params, #NonNull LoadCallback<Integer, BillsModel.Bills> callback) {
}
#Override
public void loadAfter(#NonNull LoadParams<Integer> params, #NonNull final LoadCallback<Integer, BillsModel.Bills> callback) {
RestClient.webServices().getCompletePartnerBills(params.key, 1, sortOrder, searchParam).enqueue(new Callback<ApiResponse<BillsModel>>() {
#Override
public void onResponse(Call<ApiResponse<BillsModel>> call, Response<ApiResponse<BillsModel>> response) {
if (response.code() == 200) {
callback.onResult(response.body().getData().getBills(), null);
}
}
#Override
public void onFailure(Call<ApiResponse<BillsModel>> call, Throwable t) {
}
});
}
And here is my Adapter:
public class ApprovedBillsAdapter extends PagedListAdapter<BillsModel.Bills, ApprovedBillsAdapter.ItemViewHolder> {
Context context;
ApprovedBillsAdapter(Context context) {
super(DIFF_CALLBACK);
this.context = context;
}
#NonNull
#Override
public ApprovedBillsAdapter.ItemViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
return new ApprovedBillsAdapter.ItemViewHolder(LayoutInflater.from(context).inflate(R.layout.row_approved_bills, viewGroup, false));
}
#Override
public void onBindViewHolder(#NonNull ApprovedBillsAdapter.ItemViewHolder holder, int i) {
try {
final BillsModel.Bills bill = getItem(i);
holder.tv_bill_id.setText(bill.getBillId());
Picasso.with(context).load(bill.getCustomer_image()).placeholder(R.drawable.ic_avatar).into(holder.ivUserImage);
holder.tvCustomerName.setText(bill.getCustomer_name());
holder.tvAmount.setText("Rs." + bill.getBillAmount());
holder.tvMobileNumber.setText(bill.getCustomer_mobile());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(context, BillDetailsActivity.class);
intent.putExtra("id", bill.getId());
context.startActivity(intent);
}
});
holder.tvPendingSinceValue.setText(bill.getUpdated_at());
} catch (Exception e) {
}
}
private static DiffUtil.ItemCallback<BillsModel.Bills> DIFF_CALLBACK =
new DiffUtil.ItemCallback<BillsModel.Bills>() {
#Override
public boolean areItemsTheSame(BillsModel.Bills oldItem, BillsModel.Bills newItem) {
return oldItem.getBillId().equals(newItem.getBillId());
}
#Override
public boolean areContentsTheSame(BillsModel.Bills oldItem, BillsModel.Bills newItem) {
return oldItem.equals(newItem);
}
};
class ItemViewHolder extends RecyclerView.ViewHolder {
TextView tv_bill_id;
#BindView(R.id.iv_user_image)
ImageView ivUserImage;
#BindView(R.id.tv_customer_name)
TextView tvCustomerName;
#BindView(R.id.tv_mobile_number)
TextView tvMobileNumber;
#BindView(R.id.tv_bill_id)
TextView tvBillId;
#BindView(R.id.tv_amount)
TextView tvAmount;
#BindView(R.id.tv_pending_since_value)
TextView tvPendingSinceValue;
public ItemViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
tv_bill_id = itemView.findViewById(R.id.tv_bill_id);
}
}
}
It got solved, i needed to add
Integer key = params.key + 1;
callback.onResult(response.body().getData().getOffers(), key);
to my code. Incrementing params.key on every loadAfter call was the key. And then passing it to the onResult function as adjacent key.

Android retrofit call bad behavior

Good day, so my problem as follows.
When I initialize the retrofit call it has following flow.
1.In the Presenterclass, it goes to getData method.
2.Then it goes to Interactorclass and creates a instance of itself.
3.Then it starts to fetch data from getMovieList method.
4.When it gets to call.enqueue it says OnDataCallback is null.
5.Goes back to Presenter and returns a null List.
6.It resumes call.enqueue does the api call as required.
7.Returns to Presenter and returns the List of objects.
I have tried so many things but non seem to work, maybe I'm understanding something incorrectly ,any help would be appreciated thanks.
Interactor Class.
public class MovieListInteractor implements IMovieContarctor.MovieListInteractor {
private InteractorListener mListener;
private List<MovieListModel> mList;
public MovieListInteractor(InteractorListener mListener) {
this.mListener = mListener;
}
public int getMovieSize() {
return mList.size();
}
#Override
public String getMoviePosterPath(int pos) {
return mList.get(pos).getPosterPath();
}
#Override
public MovieListModel getMovie(int pos) {
return mList.get(pos);
}
#Override
public List<MovieListModel> getMovieList() {
if (mList == null)
mList = new ArrayList<>();
ServerMovieCall mCall = new ServerMovieCall();
mCall.getMovieList(new IOnDataCallback<List<MovieListModel>>() {
#Override
public void onSuccess(List<MovieListModel> data) {
mList.addAll(data);
}
#Override
public void onFailure(String message) {
Log.d(TAG, "onFailure: " + message);
}
});
return mList;
}
}
Presenter class
public class MovieListPresenter implements IMovieContarctor.MovieListPresenter, InteractorListener {
private MovieListFragment mView;
private MovieListInteractor mInteractor;
private List<MovieListModel> mList;
public MovieListPresenter(MovieListFragment mView) {
this.mView = mView;
mInteractor = new MovieListInteractor(this);
getData();
}
//TODO: Add functionality so that the view could call mView.refreshAdapterList
//TODO: And from the view - adapter.notifiyDataSetChange;
#Override
public void onSuccess(List<MovieListModel> data, String msg) {
mView.onDataChange(data, msg);
}
#Override
public void onFailure(String msg) {
mView.onDataFailure(msg);
}
#Override
public void bindAtPosition(int position, MovieListViewHolder holder) {
holder.setValue(mInteractor.getMovie(position));
}
#Override
public void getData() {
mList = mInteractor.getMovieList();
}
#Override
public String getMoviePosterPath(int pos) {
return mInteractor.getMoviePosterPath(pos);
}
#Override
public int getSize() {
return mInteractor.getMovieSize();
}
}
Adapter
public class MovieListAdapter extends RecyclerView.Adapter<MovieListViewHolder> {
private MovieListPresenter mPresenter;
private Context ctx;
public MovieListAdapter(MovieListPresenter mPresenter, Context context) {
this.mPresenter = mPresenter;
this.ctx = context;
}
#Override
public MovieListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MovieListViewHolder(LayoutInflater.from(ctx).inflate(R.layout.main_view_movie_list, parent, false));
}
#Override
public void onBindViewHolder(MovieListViewHolder holder, int position) {
mPresenter.bindAtPosition(position, holder);
String image_url = IMAGE_URL_BASE_PATH + mPresenter.getMoviePosterPath(position);
Picasso.with(ctx).load(image_url).placeholder(android.R.drawable.sym_def_app_icon)
.error(android.R.drawable.sym_def_app_icon).into(holder.mImage);
}
#Override
public int getItemCount() {
return mPresenter.getSize();
}
}
MovieCall class
public class ServerMovieCall {
private Retrofit retrofit;
private static ServerMovieCall mInstance;
private Context ctx;
public static ServerMovieCall getInstance() {
if (mInstance == null) {
mInstance = new ServerMovieCall();
}
return mInstance;
}
public ServerMovieCall() {
configureClient();
}
private void configureClient() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(logging).addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl url = request.url().newBuilder().addQueryParameter("api_key", Constants.API_KEY).build();
request = request.newBuilder().url(url).build();
return chain.proceed(request);
}
}).build();
retrofit = new Retrofit.Builder().baseUrl(Constants.API_BASE_URL).client(client)
.addConverterFactory(GsonConverterFactory.create()).build();
}
public void getMovieList(final IOnDataCallback<List<MovieListModel>> onDataCallback) {
IServerMovieApiHelper iMovieReviewApiService = retrofit.create(IServerMovieApiHelper.class);
Call<MovieListResponse> call = iMovieReviewApiService.getMovieList();
call.enqueue(new Callback<MovieListResponse>() {
#Override
public void onResponse(Call<MovieListResponse> call, Response<MovieListResponse> response) {
if (response.isSuccessful()) {
onDataCallback.onSuccess(response.body().getMovies());
} else {
onDataCallback.onFailure(response.message());
}
}
#Override
public void onFailure(Call<MovieListResponse> call, Throwable t) {
onDataCallback.onFailure(t.getLocalizedMessage());
}
});
}
Contract interfaces
public interface IMovieContarctor {
public interface MovieListInteractor {
List<MovieListModel> getMovieList();
MovieListModel getMovie(int pos);
String getMoviePosterPath(int pos);
}
public interface MovieListPresenter {
void bindAtPosition(int position, MovieListViewHolder holder);
String getMoviePosterPath(int pos);
int getSize();
void getData();
}
public interface MovieListView {
void onDataChange(List<MovieListModel> data, String message);
void onDataFailure(String message);
}
}
DataCallback Listener.
public interface IOnDataCallback<T> {
void onSuccess(T data);
void onFailure(String message);
}
In Interactor class:
#Override
public List<MovieListModel> getMovieList() {
if (mList == null)
mList = new ArrayList<>();
ServerMovieCall mCall = new ServerMovieCall();
mCall.getMovieList(new IOnDataCallback<List<MovieListModel>>() {
#Override
public void onSuccess(List<MovieListModel> data) {
mList.addAll(data);
mListener.onSuccess(mList, "Your message");
}
#Override
public void onFailure(String message) {
Log.d(TAG, "onFailure: " + message);
}
});
return mList;
}
In Presenter class:
#Override
public void onSuccess(List<MovieListModel> data, String msg) {
mList = data;
mView.onDataChange(data, msg);
}

Cannot Set Data to RecyclerView via Retrofit in Android

I can normally get and show data on Toast message, but I cannot add them to RecyclerView. How can I do this? Please help me. Thanks. I am sharing my code below.
NewsCatalog.java
public class NewsCatalog {
public List<MainNodes> nodes;
}
MainNodes.java
public class MainNodes {
public SubNodes node;
}
SubNodes.java
public class SubNodes {
public String body;
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
NewsInterface
public interface NewsService {
String BASE_URL = "http://test.muhabirce.de/app/term/";
#GET("saglik")
Call<NewsCatalog> newsCatalog();
}
Adapter.java
public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {
private ArrayList<SubNodes> subNodes;
private Context context;
public NewsAdapter(ArrayList<SubNodes> subNodes, Context context) {
this.subNodes = subNodes;
this.context = context;
}
#Override
public NewsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context)
.inflate(R.layout.card_view_items, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(NewsAdapter.ViewHolder holder, int position) {
holder.tvText.setText(subNodes.get(position).getBody());
}
#Override
public int getItemCount() {
return subNodes.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView tvText;
public ViewHolder(View itemView) {
super(itemView);
tvText = (TextView) itemView.findViewById(R.id.tvText);
}
}
}
MainActivity.java
private void bindDatas() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(NewsService.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
NewsService newsService = retrofit.create(NewsService.class);
Call<NewsCatalog> requestCatalog = newsService.newsCatalog();
requestCatalog.enqueue(new Callback<NewsCatalog>() {
#Override
public void onResponse(Call<NewsCatalog> call, Response<NewsCatalog> response) {
SubNodes subNodess = new SubNodes();
if (!response.isSuccessful()) {
Log.i("TAG", "Error: " + response.code());
} else {
NewsCatalog catalog = response.body();
try {
for (MainNodes m : catalog.nodes
) {
subNodess.setBody(m.node.body);
}
} catch (Exception e) {
Log.i(TAG, "" + e.getMessage());
}
}
subNodes.add(subNodess);
adapter.notifyDataSetChanged();
}
#Override
public void onFailure(Call<NewsCatalog> call, Throwable t) {
Log.e(TAG, "Error: " + t.getMessage());
}
});
}
}
You should not create a new Subnodes object in response callback. Adapter does not have reference to this object and so the changes in data is not reflected. Update the subnodes object in the recyclerview adapter. Then only notifyDataSetChanged() will reflect the change in data.

Categories

Resources