Android Jetpack pagination not showing data of more than 2 pages - android

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.

Related

Remove item PagedListAdapter, getting IndexOutOfBoundsException error second time I remove list item

I am removing an item in PagedListAdapter. I am not using room, simply I store a cached list inMemoryElements of all the elements I have loaded. When I remove one item, I just delete that item from the list, and then call DataSource.invalidate(), which calls again LoadInitialCallback.onResult() where I pass my cached list.
Everything works fine the first time I delete one item, but the second time I delete other item, I get:
java.lang.IndexOutOfBoundsException: Index: 18, Size: 18 at java.util.ArrayList.get(ArrayList.java:437) at androidx.paging.PagedStorage.get(PagedStorage.java:174) at androidx.paging.PagedStorageDiffHelper$1.areItemsTheSame(PagedStorageDiffHelper.java:77)
Theese are my components:
In my Fragment, where I have the paginated RecyclerView, here I call the ViewModel method makeCallGetArticleListPaging for loading the elements into LiveData wrap. I then call function getPagedList for subscribing to LiveData<PagedList<ArticleDto>> values.
In DataSourceFactory: I have a reference of current datasource for calling .invalidate()
Reposiroty: where I create the pagedList.
Finally when item is deleted, I call ViewModel method invalidateDatasource()
DataSource:
public class ArticleDataSource extends PageKeyedDataSource<Integer, ArticleDto> {
ApiService apiService;
private List<ArticleDto> inMemoryElements;
public ArticleDataSource(ApiService apiService, String apiServiceMethod, List<ArticleDto> inMemoryElements, int currentPage) {
this.apiService = apiService;
this.inMemoryElements = inMemoryElements;
this.currentPage = currentPage;
}
#Override
public void loadInitial(#NonNull LoadInitialParams<Integer> params, #NonNull LoadInitialCallback<Integer, ArticleDto> callback) {
queryArticleDto.setPageIndex(currentPage);
HashMap<String,String> headers = new HashMap<String, String>();
headers.put("Content-Type", HttpConstants.HTTP_CONTENT_TYPE);
if(inMemoryElements.isEmpty()){
apiService.search(headers,queryArticleDto).enqueue(new Callback<PageResultDto<ArticleDto>>() {
#Override
public void onResponse(Call<PageResultDto<ArticleDto>> call, Response<PageResultDto<ArticleDto>> response) {
inMemoryElements.addAll(response.body().getElements());
callback.onResult(response.body().getElements(), null, 1);
}
#Override
public void onFailure(Call<PageResultDto<ArticleDto>> call, Throwable t) {
callback.onResult(new ArrayList<>(), null, 0);
}
});
}else{
callback.onResult(inMemoryElements,null,currentPage + 1);
}
}
#Override
public void loadAfter(#NonNull LoadParams<Integer> params, #NonNull final LoadCallback<Integer, ArticleDto> callback) {
int currentPage = params.key;
queryArticleDto.setPageIndex(currentPage);
apiService.search(headers,queryArticleDto).enqueue(new Callback<PageResultDto<ArticleDto>>() {
#Override
public void onResponse(Call<PageResultDto<ArticleDto>> call, Response<PageResultDto<ArticleDto>> response) {
if (response.isSuccessful()) {
inMemoryElements.addAll(response.body().getElements());
callback.onResult(response.body().getElements(), currentPage + 1);
}
}
#Override
public void onFailure(Call<PageResultDto<ArticleDto>> call, Throwable t) {
callback.onResult(new ArrayList<>(), currentPage);
}
});
}
}
DataSourceFactory:
public class ArticleDataSourceFactory extends DataSource.Factory<Integer, ArticleDto> {
//To perform network calls
private ApiService apiService;
private UserSessionLiveData userSessionLiveData;
private ArticleDto queryArticleDto;
private ArticleDataSource articleDataSource;
#Inject
public ArticleDataSourceFactory(ApiService apiService, UserSessionLiveData userSessionLiveData) {
this.apiService = apiService;
this.userSessionLiveData = userSessionLiveData;
}
public void setQueryArticleDto(ArticleDto queryArticleDto) {
this.queryArticleDto = queryArticleDto;
if(articleDataSource != null)
articleDataSource.setQueryArticleDto(queryArticleDto);
}
public void setApiServiceMethod(String apiServiceMethod) {
this.apiServiceMethod = apiServiceMethod;
}
//Factory method pattern implemented below
//Where a create method does the job of initializing the objects for client
#NonNull
#Override
public DataSource<Integer, ArticleDto> create() {
articleDataSource = new ArticleDataSource(apiService,
apiServiceMethod,
userSessionLiveData,
articleDataSource == null ? new ArrayList<>() : articleDataSource.getInMemoryElements(),
articleDataSource == null ? 0 : articleDataSource.getCurrentPage()
);
articleDataSource.setQueryArticleDto(queryArticleDto);
return articleDataSource;
}
public ArticleDataSource getArticleDataSource() {
return articleDataSource;
}}
PagedList:
public class ArticleRepository {
public LiveData<PagedList<ArticleDto>> getPagedList(ArticleDto articleDto, String apiServiceMethod) {
PagedList.Config config = new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPageSize(5)
.setPrefetchDistance(1)
.setMaxSize(10)
.build();
return new LivePagedListBuilder<>(articleDataSourceFactory, config)
.setFetchExecutor(Executors.newFixedThreadPool(5)) // Use five threads to do the fetching operations
.build();
}}
ViewModel:
public class ArticleViewModel extends ViewModel {
private final MutableLiveData<PagedList<ArticleDto>> _pagedList = new MutableLiveData<>();
private LiveData<PagedList<ArticleDto>> pagedList = (LiveData<PagedList<ArticleDto>>) _pagedList;
public void makeCallGetArticleListPaging(ArticleDto articleDto, String apiServiceMethod){
pagedList = articleRepository.getPagedList(articleDto,apiServiceMethod);
}
public LiveData<PagedList<ArticleDto>> getPagedList() {
return pagedList;
}}
Fragment:
public class ArticleListFragment extends Fragment {
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
articleViewModel.makeCallGetArticleListPaging(articleDto, ArticleDataSource.API_SERVICE_METHOD_SEARCH);
observeData();
}
private void observeData() {
articleViewModel.getPagedList().observe(getViewLifecycleOwner(), new Observer<PagedList<ArticleDto>>() {
#Override
public void onChanged(PagedList<ArticleDto> posts) {
postsPagedListAdapter.submitList(posts);
}
});
}
public void invalidateDatasource(int rowPosition){
articleDataSourceFactory.getArticleDataSource().getInMemoryElements().remove(rowPosition);
articleDataSourceFactory.getArticleDataSource().invalidate();
}}
DiffUtil.ItemCallback
new DiffUtil.ItemCallback<ArticleDto>() {
#Override
public boolean areItemsTheSame(#NonNull ArticleDto oldItem, #NonNull ArticleDto newItem) {
return oldItem.id == newItem.id;
}
#Override
public boolean areContentsTheSame(#NonNull ArticleDto oldItem, #NonNull ArticleDto newItem) {
return oldItem.equals(newItem);
}
};

android architecture components ViewModel & PageKeyedDataSource issue

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.

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);
}

API successfull called, but not displayed

i am trying to call local API server using retrofit, in my Logcat shows that the API success called, but in my app there is nothing displayed. how i can fix this issue? and here is my code :
interface.java :
public interface Service {
#GET("vocabulary/id/*")
Call<APIResponse<List<VocabMaster>>> getVocabMaster();
}
Vocab Master.java to collect data from API :
public class VocabMaster implements Serializable {
private BigDecimal vocab_id;
private String hanzi_t;
private String hanzi_s;
private String pinyin;
private BigDecimal level_id;
private List<VocabMeaning> meaning;
public BigDecimal getVocab_id() {
return vocab_id;
}
public void setVocab_id(BigDecimal vocab_id) {
this.vocab_id = vocab_id;
}
public String getHanzi_t() {
return hanzi_t;
}
public void setHanzi_t(String hanzi_t) {
this.hanzi_t = hanzi_t;
}
public String getHanzi_s() {
return hanzi_s;
}
public void setHanzi_s(String hanzi_s) {
this.hanzi_s = hanzi_s;
}
public String getPinyin() {
return pinyin;
}
public void setPinyin(String pinyin) {
this.pinyin = pinyin;
}
public List<VocabMeaning> getMeaning() {
return meaning;
}
public void setMeaning(List<VocabMeaning> meaning) {
this.meaning = meaning;
}
public BigDecimal getLevel_id() {
return level_id;
}
public void setLevel_id(BigDecimal level_id) {
this.level_id = level_id;
}
#Override
public String toString() {
return "VocabMaster{" +
"vocab_id=" + vocab_id +
", hanzi_t='" + hanzi_t + '\'' +
", hanzi_s='" + hanzi_s + '\'' +
", pinyin='" + pinyin + '\'' +
", level_id=" + level_id +
", meaning=" + meaning +
'}';
}
}
And this is MainActivity.java :
public class MainActivity extends AppCompatActivity {
RecyclerView rvReligiVideo;
List<VocabMaster> vocabMasters = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rvReligiVideo = findViewById(R.id.rvReligi);
initReligiVideo();
loadReligiVideo();
}
private void initReligiVideo() {
LinearLayoutManager layout = new LinearLayoutManager(this);
layout.setOrientation(LinearLayoutManager.HORIZONTAL);
rvReligiVideo.setLayoutManager(layout);
VocabAdapter ar = new VocabAdapter(vocabMasters);
rvReligiVideo.setAdapter(ar);
}
//=========== Request to API ==========
private void loadReligiVideo() {
Call<APIResponse<List<VocabMaster>>> call = ServicesFactory.getService().getVocabMaster();
call.enqueue(new Callback<APIResponse<List<VocabMaster>>>() {
#Override
public void onResponse(Call<APIResponse<List<VocabMaster>>> call, Response<APIResponse<List<VocabMaster>>> response) {
if (response.isSuccessful() && response.body().isSuccessful()) {
List<VocabMaster> data = response.body().data;
if (data != null) {
vocabMasters.clear();
vocabMasters.addAll(data);
rvReligiVideo.getAdapter().notifyDataSetChanged();
}
} else {
Toast.makeText(MainActivity.this, response.errorBody().toString(), Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<APIResponse<List<VocabMaster>>> call, Throwable t) {
Toast.makeText(MainActivity.this, t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
}
});
}
}
logcat on android studio :
JSON Data Structure :
Here is my Adapter :
public class VocabAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<VocabMaster> data;
public VocabAdapter(List<VocabMaster> data) {
this.data = data;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.vocabulary_model, null);
return new VH(v);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
VocabMaster vm = data.get(position);
VH vh = (VH) holder;
vh.hanziS.setText(vm.getHanzi_s());
vh.hanziT.setText(vm.getHanzi_t());
vh.pinyin.setText(vm.getPinyin());
vh.sound.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Sound", Toast.LENGTH_SHORT).show();
}
});
vh.favorite.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Favorite", Toast.LENGTH_SHORT).show();
}
});
vh.share.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Share", Toast.LENGTH_SHORT).show();
}
});
}
#Override
public int getItemCount() {
return (data == null) ? 0 : data.size();
}
public class VH extends RecyclerView.ViewHolder {
TextView hanziS;
TextView hanziT;
TextView pinyin;
TextView desc;
ImageView sound, share, favorite;
Context context;
public VH(View itemView) {
super(itemView);
hanziS = itemView.findViewById(R.id.hanziS);
hanziT = itemView.findViewById(R.id.hanziT);
pinyin = itemView.findViewById(R.id.pinyin);
desc = itemView.findViewById(R.id.txtDesc);
sound = itemView.findViewById(R.id.imgSpeaker);
share = itemView.findViewById(R.id.imgShare);
favorite = itemView.findViewById(R.id.imgFavotite);
}
}
}
i did some changes in your code... use this
private VocabAdapter ar;
private void initReligiVideo() {
LinearLayoutManager layout = new LinearLayoutManager(this);
layout.setOrientation(LinearLayoutManager.HORIZONTAL);
rvReligiVideo.setLayoutManager(layout);
ar = new VocabAdapter(vocabMasters);
rvReligiVideo.setAdapter(ar);
}
private void loadReligiVideo() {
Call<APIResponse<List<VocabMaster>>> call = ServicesFactory.getService().getVocabMaster();
call.enqueue(new Callback<APIResponse<List<VocabMaster>>>() {
#Override
public void onResponse(Call<APIResponse<List<VocabMaster>>> call, Response<APIResponse<List<VocabMaster>>> response) {
if (response.isSuccessful() && response.body().isSuccessful()) {
List<VocabMaster> data = response.body().data;
if (data != null) {
ar.setVocoList(data);
}
} else {
Toast.makeText(MainActivity.this, response.errorBody().toString(), Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<APIResponse<List<VocabMaster>>> call, Throwable t) {
Toast.makeText(MainActivity.this, t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
}
});
}
Add this method in your adapter Class
public void setVocoList(List<VocabMaster> list){
if(list!=null)
data=list;
notifyDataSetChanged();
}
Do not change adapter's list when you have new list.
//incorrect except that the adapter is first time set to recyclerview and setVocoList is called before recyclerview.setAdater
public void setVocoList(List<VocabMaster> list){
if(list!=null)
data=list;
notifyDataSetChanged();
}
//correct
private List<VocabMaster> datas = new ArrayList()
public void setVocoList(List<VocabMaster> list){
if(list==null) return;
datas.clear();
datas.addAll(list);
notifyDataSetChanged();
}

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