将视图保留在最后一项 - LoadMore RecyclerView

Posted

技术标签:

【中文标题】将视图保留在最后一项 - LoadMore RecyclerView【英文标题】:keep the view in the last item - LoadMore RecyclerView 【发布时间】:2018-12-07 06:36:45 【问题描述】:

晕,我的应用中有 LoadMore RecyclerView。这是工作,但是当我加载更多项目时,recyclerview 总是保持显示列表的顶部。我的意思是,它应该显示最后加载的项目。

任何人,你愿意帮助我吗?谢谢。

我的屏幕截图:

    1 - 5 is the first list loaded:

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

这是我的代码:

 @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();
    

【问题讨论】:

那我该怎么办?你愿意帮助我吗@HawkPriest 【参考方案1】:

好的,我假设您已经为RecyclerView 实现了EndlessScrollListener,如果没有,我强烈建议您检查一下:https://gist.github.com/nesquena/d09dc68ff07e845cc622。为了进一步使您的代码更具可读性和适应性,我建议您使用更多的封装。

例如:有一个 NetworkHandler 为您回调 UI。相应地切换 UI 行为的位置。为此,您需要一个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 类作为可用于创建服务的单例。

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 一样。剩下的就是你必须实现回调并在片段或活动中设置 UI。

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 会自动触发网络调用,因此您可以避免编写冗余调用。

最后在你的FragmentActivity 有这样的东西:

// 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(),最终您将在片段上收到回调。

【讨论】:

谢谢你的帮助,我会试试@HawkPriest

以上是关于将视图保留在最后一项 - LoadMore RecyclerView的主要内容,如果未能解决你的问题,请参考以下文章

如何在 xamarin 表单的集合视图中的最后一项旁边添加图像/按钮?

Admob 横幅隐藏列表视图片段的最后一项

如何在 iPhone 应用程序中重新加载表格视图

在 AVQueuePlayer 中播放完最后一项后如何显示工作表视图?

更改影响第一项的列表视图中最后一项的颜色

SwiftUI 滚动视图部分显示 Mac 应用程序中的最后一项