Хорошо, я предполагаю, что у вас уже есть реализация EndlessScrollListener
для RecyclerView
, если нет, я призываю вас проверить это: https://gist.github.com/nesquena/d09dc68ff07e845cc622. Далее, чтобы сделать ваш код более читабельным и адаптируемым, Я бы порекомендовал вам использовать больше инкапсуляции.
Например: имейте NetworkHandler
, который выполняет обратные вызовы для вас в пользовательском интерфейсе. Где вы переключаете поведение пользовательского интерфейса соответственно. Для этого вам нужен интерфейс OnDataCallback
.
// OnDataCallback.java
interface OnDataCallback<T> {
void onData(T data);
void onError(Throwable error);
}
// NetworkHandler.java
public class NetworkHandler<T> {
@Nullable
protected OnDataCallback<T> dataCallback;
protected int pageIndex = 0;
public void setDataCallback(OnDataCallback<T> dataCallback) {
this.dataCallback = dataCallback;
}
public void removeDataCallback() {
dataCallback = null;
}
public void setPageIndex(int pageIndex) {
this.pageIndex = pageIndex;
}
public void updatePageNumber() {
pageIndex++;
}
}
Создайте класс RetrofitUtils
как Singleton, который можно использовать для создания служб.
public static class RetrofitUtils {
private static RetrofitUtils utils;
public static RetrofitUtils getInstance() {
if (utils == null) {
utils = new RetrofitUtils();
}
return utils;
}
private Retrofit retrofit;
public Retrofit getRetrofitInstance(){
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl("http://mybaseurl.api/v1/")
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
public JobService constructJobService(Class<JobService> uClass) {
return getRetrofitInstance().create(uClass);
}
public LoginService construcstLoginService(Class<LoginService> uClass) {
return getRetrofitInstance().create(uClass);
}
}
Затем унаследуйте NetworkHandler
и переопределите методы в соответствии с вашими спецификациями, например, получите JobNetworkHandler
, который выполняет запрос и пейджинг для вас. Создайте пользовательские классы Throwable
для более эффективной обработки ошибок, как в примере это ErrorBodyThrowable
. Осталось только реализовать обратный вызов и установить пользовательский интерфейс во фрагменте или в операции.
public class JobReportHandler extends NetworkHandler<ReportJobModel> {
int compID;
int length;
Authorization auth = AuthUtils.getAuth();
@Override
public void updatePageNumber() {
super.updatePageNumber();
fetchJobsModel(compID, length);
}
public void fetchJobsModel(int compID, int length) {
this.compID = compID;
this.length = length;
JobService request = RetrofitUtils.getInstance().constructJobService(JobService.class);
Call<ReportJobModel> call = request.getReportPagination(compID, pageIndex, length, auth); //ngirim ke API
call.enqueue(new Callback<JobModel>() {
@Override
public void onResponse(Call<JobModel> call, Response<JobModel> response) {
// manipulate data and pass the UI model
// that needs to be handled by the view
ReportJobModel reportJobModel = response.convertToReport();
if (dataCallback == null) return;
if (response.isSuccessful()) {
dataCallback.onData(reportJobModel);
} else if (response.errorBody() != null) {
dataCallback.onError(new ErrorBodyThrowable());
} else if (response.code() == 400) {
dataCallback.onError(new ApiError());
} else {
// do something else
}
}
@Override
public void onFailure(Call<ReportJobModel> call, Throwable t) {
if (dataCallback != null) {
dataCallback.onError(t);
}
}
});
}
public class ErrorBodyThrowable extends Throwable {
ErrorBodyThrowable() {
super("Gagal Memuat. Periksa Koneksi Anda!");
}
}
}
Обратите внимание, что обновление pageIndex автоматически запускает сетевой вызов, поэтому вы избегаете записи избыточных вызовов.
Наконец-то в вашем Fragment
или Activity
есть что-то вроде этого:
// TestFragment.java
public final class TestFragment extends Fragment implements OnDataCallback<ReportJobModel>, CustomRecyclerOnScrollListener {
@Bind(R.id.myRecyclerView)
RecyclerView myRecyclerView;
private JobsAdapter adapter;
private final JobReportHandler jobHandler = new JobReportHandler();
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
private MyCustomEndlessScrollListener endlessScroll;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.my_list_fragment, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
endlessScroll = = new MyCustomEndlessScrollListener(this);
setUi();
initializeNetwork();
}
private void initializeNetwork() {
// after setting the UI Parameters
jobHandler.setDataCallback(this);
jobHandler.fetchJobsModel(compID, length);
}
@Override
public void onData(ReportJobModel dataModel) {
// just a safety mechanism to handle threading
// use the main thread dispatcher
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
final ArrayList<JobItem> data = dataModel.getJobItems();
UiUtils.makeGone(loadingProgress);
if (myRecyclerView.getAdapter() == null || jobAdapter == null) {
jobAdapter = JobsAdapter(data);
myRecyclerView.setAdapter(jobAdapter);
myRecyclerView.setOnScrollChangeListener(endlessScroll);
} else {
jobAdapter.getItems().addAll(data);
jobAdapter.notifyItemRangeInserted(jobAdapter.getItems().size() -1, data.size());
}
}
});
}
@Override
public void onScrolledToBottom() {
jobHandler.updatePageNumber();
}
@Override
public void onError(final Throwable error) {
// just a safety mechanism to handle threading
// use the main thread dispatcher
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
if (error.getMessage() != null && !error.getMessage().isEmpty()) {
Toast.makeText(getContext(), error.getMessage(), Toast.LENGTH_LONG).show();
}
}
});
}
@Override
public void onDestroy() {
jobHandler.removeDataCallback();
super.onDestroy();
}
}
Как вы видите, onScrolledToBottom()
будет запускаться через CustomRecyclerScrollListener
, и это вызовет updatePageNumber()
, который затем вызовет fetchJobModel()
, и в итоге вы получите обратный вызов для вашего фрагмента.