0

Halo, I have LoadMore RecyclerView in my app. it's work, but when i'm load more item, the recyclerview always keep showing the top of the list. I mean, it should be shown the last item loaded.

Anyone, would you like to help me? thanks.

my screenshoot :

  1. 1 - 5 is the first list loaded:

enter image description here

  1. 6 - 10 shown after scrolling the recyclerView, but after 6 - 10 loaded, recyclerView always keep showing the top of the list (1-5) :

enter image description here

this is my code :

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_job_company);

    //intent untuk nerima value namaAdver dan handling jika tdk ada list
    TextView namaCompany = (TextView) findViewById(R.id.tv_companyname);
    TextView emptyList = (TextView) findViewById(R.id.emptylist);

    loading = (ProgressBar) findViewById(R.id.loading);
    loading.getIndeterminateDrawable().setColorFilter(getResources().getColor(R.color.colorPrimary), PorterDuff.Mode.MULTIPLY);

    Intent intentGet = getIntent();
    companyName = intentGet.getStringExtra("namaCompany");
    idComp = intentGet.getStringExtra("idCompany");

    try {
        compID = Integer.parseInt(idComp);
    } catch (NumberFormatException nfe) {

    }

    namaCompany.setText(companyName);
    setTitle(intentGet.getStringExtra("namaCompany"));

    PaginationJobCompany(compID, pageNum);

    recyclerView = (RecyclerView) findViewById(R.id.rv_job_company2);
    recyclerView.setHasFixedSize(true);
    mLayoutManager = new LinearLayoutManager(getApplicationContext());
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.addItemDecoration(new DividerItemDecoration(getApplicationContext(), null)); //untuk divider

}

private void PaginationJobCompany(final int compID, final int pageNumber) {
    try {
        loading.getIndeterminateDrawable().setColorFilter(getResources().getColor(R.color.colorPrimary), PorterDuff.Mode.MULTIPLY);
        loading.setVisibility(View.GONE);


        //authorization JWT pref_token berdasarkan string yg disimpan di preferenceManager pada class login.
        Authorization = (PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString(
                getResources().getString(R.string.pref_token), ""));

        //production
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(getResources().getString(R.string.base_url))
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        //assign variabel request ke class interface TabAdverRequest
        final APIInterfaces request = retrofit.create(APIInterfaces.class);

        Call<ReportJobModel> call = request.getReportPagination(compID, pageNum, length, Authorization); //ngirim ke API
        call.enqueue(new Callback<ReportJobModel>() {
            @Override
            public void onResponse(Call<ReportJobModel> call, Response<ReportJobModel> response) {
                loading.getIndeterminateDrawable().setColorFilter(getResources().getColor(R.color.colorPrimary), PorterDuff.Mode.MULTIPLY);
                loading.setVisibility(View.GONE);
                if (response.isSuccessful()) {
                    companyResult = response.body().getResult();
                    if (!companyResult.isEmpty()) {
                        company.addAll(companyResult);

                        for (int i = 0; i < companyResult.size(); i++) {
                            if (company.get(i).getCompanyID() == compID) {
                                jobItemResult = response.body().getResult().get(i).getJobs();
                                jobItem.addAll(jobItemResult);
                            }
                        }
                    }
                    else {
                        for (int j = 0; j < companyResult.size(); j++) {
                            if (company.get(j).getCompanyID() == compID) {
                                lastId = jobItem.size()-1;
                            }
                        }

                    }

                    adapter = new JobCompanyAdapter(jobItem, recyclerView);
                    recyclerView.setAdapter(adapter);

                    adapter.setOnLoadMoreListener(new OnLoadMoreListener() {
                        @Override
                        public void onLoadMore() {
                            //add null , so the adapter will check view_type and show progress bar at bottom
                            jobItem.add(null);
                            adapter.notifyItemInserted(jobItem.size() - 1);
                            loading.getIndeterminateDrawable().setColorFilter(getResources().getColor(R.color.colorPrimary), PorterDuff.Mode.MULTIPLY);
                            loading.setVisibility(View.GONE);

                            handler.postDelayed(new Runnable() {
                                @Override
                                public void run() {

                                    jobItem.remove(jobItem.size() - 1);
                                    adapter.notifyItemRemoved(jobItem.size());
                                    loading.setVisibility(View.GONE);

                                        pageNum++;
                                        loading.setVisibility(View.GONE);
                                        PaginationJobCompany(compID, pageNum);
                                        adapter.notifyDataSetChanged();
                                }
                            }, 2000);
                        }
                    });

                } else if (response.errorBody() != null) {
                    loading.setVisibility(View.GONE);
                    Toast.makeText(getApplicationContext(), "Gagal Memuat. Periksa Koneksi Anda!", Toast.LENGTH_LONG).show();
                } else if (response.code() == 400) {
                    loading.setVisibility(View.GONE);
                    Toast.makeText(getApplicationContext(), "Gagal Memuat. Periksa Koneksi Anda!", Toast.LENGTH_LONG).show();
                } else {
                    loading.setVisibility(View.GONE);
                    Toast.makeText(getApplicationContext(), "Gagal Memuat. Periksa Koneksi Anda! 1", Toast.LENGTH_LONG).show();
                }
            }

            @Override
            public void onFailure(Call<ReportJobModel> call, Throwable t) {
                Toast.makeText(getApplicationContext(), "Gagal Memuat. Periksa Koneksi Anda! 1", Toast.LENGTH_LONG).show();
            }
        });
    } catch (Exception e) {
        Toast.makeText(getApplicationContext(), "Gagal Memuat. Periksa Koneksi Anda! 1", Toast.LENGTH_LONG).show();
    }
}
SonhnLab
  • 317
  • 1
  • 11

1 Answers1

0

Okay I'll assume that you already have an implementation of the EndlessScrollListener for the RecyclerView if not, I urge you check out this: https://gist.github.com/nesquena/d09dc68ff07e845cc622. Further to make your code more readable and adaptable, I would recommend that you use more encapsulation.

For example: Have a NetworkHandler that does the callbacks for you to the UI. Where you switch the UI behaviour accordingly. To do that you need a OnDataCallback interface.

// 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++;
    }
}

Create a RetrofitUtils class as a Singleton that could be used to creating the services.

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

Then inherit the NetworkHandler and override the methods according to your specifications for example have a JobNetworkHandler that does the request and paging for you. Create custom Throwable classes to handle error more efficiently like in the example it is ErrorBodyThrowable. All that is left is you have to implement the callback and set UI in fragment or activity.

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

Note that updating the pageIndex automatically triggers the network call so you avoid writing redundant calls.

Finally in your Fragment or Activity have something like this:

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

As you see the onScrolledToBottom() would be triggered through the CustomRecyclerScrollListener and this would then trigger the updatePageNumber() that would then call the fetchJobModel() and eventually you'll get a callback on your fragment.

HawkPriest
  • 262
  • 2
  • 8