使用分页库时,观察者显示列表大小为零

Posted

技术标签:

【中文标题】使用分页库时,观察者显示列表大小为零【英文标题】:When using Paging Library, observer showing list size as zero 【发布时间】:2018-12-30 18:39:50 【问题描述】:

我正在尝试使用更新版本的分页库。我正在使用 volley 从网络中获取我的数据。每当我从网络获取数据时,都会获取数据,但是当调用到观察者时,显示的列表大小为零。我不确定自己做错了什么。

我是 android 新手,所以对分页不太了解。我已经好几天没有尝试了,但仍然无法弄清楚。

我的gradle如下

apply plugin: 'com.android.application'

android 
compileSdkVersion 27
defaultConfig 
    applicationId "com.example.athansys.mypagingapplication"
    minSdkVersion 22
    targetSdkVersion 27
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

buildTypes 
    release 
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    



dependencies 
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:support-v4:27.1.1'
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'com.android.support:recyclerview-v7:27.1.1'
implementation 'com.android.volley:volley:1.1.0'
implementation 'com.google.code.gson:gson:2.8.1'
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

implementation "android.arch.paging:runtime:1.0.1"
implementation "android.arch.lifecycle:runtime:1.1.1"
implementation "android.arch.lifecycle:extensions:1.1.1"
annotationProcessor "android.arch.lifecycle:compiler:1.1.1"

我的 MainActivity 看起来像:

package com.example.athansys.mypagingapplication;

import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.arch.paging.PagedList;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class MainActivity extends AppCompatActivity 

private MyPatientAdapter mPatientAdapter;
private RecyclerView mRecyclerView;

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

    FilterModel viewModel = ViewModelProviders.of(this).get(FilterModel.class);
    viewModel.getData(this).observe(this, new Observer<PagedList<FilterPatientList>>() 
        @Override
        public void onChanged(@Nullable PagedList<FilterPatientList> results) 
            mPatientAdapter.submitList(results);
            //mPatientAdapter.setList(results);
            mPatientAdapter.notifyDataSetChanged();
            mRecyclerView.setAdapter(mPatientAdapter);
        
    );


private void setAdapter() 

    mRecyclerView = findViewById(R.id.my_patient_recycler_view);
    RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
    mPatientAdapter = new MyPatientAdapter();
    mRecyclerView.setLayoutManager(layoutManager);
    mRecyclerView.setAdapter(mPatientAdapter);



FilterModel类如下:

package com.example.athansys.mypagingapplication;


import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.ViewModel;
import android.arch.paging.LivePagedListBuilder;
import android.arch.paging.PagedList;
import android.content.Context;
import android.util.Log;

public class FilterModel extends ViewModel 
private LiveData<PagedList<FilterPatientList>> listLiveData;

LiveData<PagedList<FilterPatientList>> getData(Context context) 
    if (listLiveData == null) 

        PagedList.Config pagedListConfig =
                (new PagedList.Config.Builder()).setEnablePlaceholders(false)
                        .setPrefetchDistance(5)
                        .setPageSize(1).build();

        listLiveData = new LivePagedListBuilder<>
                (new MyPatientPagedListProvider(context)
                        .getAll()
                        , pagedListConfig).
                build();

    
    return listLiveData;


MyProvider 类是:

    package com.example.athansys.mypagingapplication;

import android.arch.paging.DataSource;
import android.arch.paging.LivePagedListProvider;
import android.content.Context;
import android.util.Log;

import java.util.List;


public class MyPatientPagedListProvider 

Context mBaseContext;

public MyPatientPagedListProvider(Context context) 
    mBaseContext = context;
    dataClass.getContextInstance(context);


private static final String TAG = MyPatientPagedListProvider.class.getName();

MyPatientData dataClass = new MyPatientData() 
    @Override
    public List<FilterPatientList> convertToItems(List<FilterPatientList> result, int size) 
        return result;
    
;

public LivePagedListProvider<Integer, FilterPatientList> getAll() 
    return new LivePagedListProvider<Integer, FilterPatientList>() 
        @Override
        protected DataSource<Integer, FilterPatientList> createDataSource() 
            return dataClass;
        
    ;


MyDataSource如下:

    package com.example.athansys.mypagingapplication;

import android.arch.paging.TiledDataSource;
import android.content.Context;

import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class MyPatientData extends TiledDataSource<FilterPatientList> 

private Context mContext;
private List<FilterPatientList> patientList;

@Override
public int countItems() 
    return 10;


@Override
public List<FilterPatientList> loadRange(int startPosition, int count) 
    String url = "My URl";
    return postStringRequestForMyPatient(url);


public List<FilterPatientList> postStringRequestForMyPatient(String url) 
    StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() 
        @Override
        public void onResponse(String response) 
            JSONObject jsonObject = null;
            try 
                jsonObject = new JSONObject(response);
                String data = jsonObject.getString("patientsfordoctor");
                mPatientsForDoctor = new Gson().fromJson(data, PatientsForDoctor.class);
                mPatientList = new ArrayList<>();

                mPatientList.addAll(mPatientsForDoctor.getPatientList());

             catch (JSONException e) 
                e.printStackTrace();
            

        
    , new Response.ErrorListener() 
        @Override
        public void onErrorResponse(VolleyError error) 

        
    ) 

        @Override
        protected Map<String, String> getParams() throws AuthFailureError 
            return super.getParams();
        

        @Override
        public String getBodyContentType() 
            return super.getBodyContentType();
        

        @Override
        public Map<String, String> getHeaders() throws AuthFailureError 
            return new HashMap<>();
        
    ;
    stringRequest.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS,
            DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
            DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
    Volley.newRequestQueue(mContext).add(stringRequest);


void getContextInstance(Context context) 
    mContext = context;


public abstract List<FilterPatientList> convertToItems(List<FilterPatientList> result, int size);

我的适配器类

    package com.example.athansys.mypagingapplication;
import android.arch.paging.PagedListAdapter;
import android.support.annotation.NonNull;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class MyPatientAdapter extends PagedListAdapter<FilterPatientList, MyPatientAdapter.PatientViewHolder> 

protected MyPatientAdapter() 
    super(FilterPatientList.DIFF_CALLBACK);


@NonNull
@Override
public PatientViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) 
    return null;


@Override
public void onBindViewHolder(@NonNull PatientViewHolder holder, int position) 




class PatientViewHolder extends RecyclerView.ViewHolder 
    TextView patientName, patientPhoneNumber, genderAge;

    PatientViewHolder(View itemView) 
        super(itemView);
    


我的 POJO(模型类如下)

FilterPatientList 类:

    package com.example.athansys.mypagingapplication;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v7.util.DiffUtil;

import com.google.gson.annotations.SerializedName;

public class FilterPatientList implements Parcelable 

private long patientId;

private Patient patientDetails;

protected FilterPatientList(Parcel in) 
    patientId = in.readLong();
    patientDetails = in.readParcelable(Patient.class.getClassLoader());


public static final Creator<FilterPatientList> CREATOR = new Creator<FilterPatientList>() 
    @Override
    public FilterPatientList createFromParcel(Parcel in) 
        return new FilterPatientList(in);
    

    @Override
    public FilterPatientList[] newArray(int size) 
        return new FilterPatientList[size];
    
;

public long getPatientId() 
    return patientId;


public void setPatientId(long patientId) 
    this.patientId = patientId;


public Patient getPatientDetails() 
    return patientDetails;


public void setPatientDetails(Patient patientDetails) 
    this.patientDetails = patientDetails;



@Override
public int describeContents() 
    return 0;


@Override
public void writeToParcel(Parcel dest, int flags) 
    dest.writeLong(patientId);
    dest.writeParcelable(patientDetails, flags);


public static final DiffUtil.ItemCallback<FilterPatientList> DIFF_CALLBACK = new DiffUtil.ItemCallback<FilterPatientList>() 
    @Override
    public boolean areItemsTheSame(FilterPatientList oldItem, FilterPatientList newItem) 
        return false;
    

    @Override
    public boolean areContentsTheSame(FilterPatientList oldItem, FilterPatientList newItem) 
        return false;
    
;

患者类别:

   package com.example.athansys.mypagingapplication;
import android.os.Parcel;
import android.os.Parcelable;

import com.google.gson.annotations.SerializedName;

public class Patient implements Parcelable

private long localPatientId;


private long apptDoctorId;

private long patientId;
private boolean isLocalPatient;

private String firstName;
private String lastName;
private String phoneNumber;
private String email;
private String dob;
private int age;
private int height;
private int weight;
private String patientSex;
private String patientSystemRegistrationDate;
private int bloodGroup;


protected Patient(Parcel in) 
    localPatientId = in.readLong();
    apptDoctorId = in.readLong();
    patientId = in.readLong();
    isLocalPatient = in.readByte() != 0;
    firstName = in.readString();
    lastName = in.readString();
    phoneNumber = in.readString();
    email = in.readString();
    dob = in.readString();
    age = in.readInt();
    height = in.readInt();
    weight = in.readInt();
    patientSex = in.readString();
    patientSystemRegistrationDate = in.readString();
    bloodGroup = in.readInt();


public static final Creator<Patient> CREATOR = new Creator<Patient>() 
    @Override
    public Patient createFromParcel(Parcel in) 
        return new Patient(in);
    

    @Override
    public Patient[] newArray(int size) 
        return new Patient[size];
    
;

public long getLocalPatientId() 
    return localPatientId;


public void setLocalPatientId(long localPatientId) 
    this.localPatientId = localPatientId;


public long getApptDoctorId() 
    return apptDoctorId;


public void setApptDoctorId(long apptDoctorId) 
    this.apptDoctorId = apptDoctorId;


public long getPatientId() 
    return patientId;


public void setPatientId(long patientId) 
    this.patientId = patientId;


public boolean isLocalPatient() 
    return isLocalPatient;


public void setLocalPatient(boolean localPatient) 
    isLocalPatient = localPatient;


public String getFirstName() 
    return firstName;


public void setFirstName(String firstName) 
    this.firstName = firstName;


public String getLastName() 
    return lastName;


public void setLastName(String lastName) 
    this.lastName = lastName;


public String getPhoneNumber() 
    return phoneNumber;


public void setPhoneNumber(String phoneNumber) 
    this.phoneNumber = phoneNumber;


public String getEmail() 
    return email;


public void setEmail(String email) 
    this.email = email;


public String getDob() 
    return dob;


public void setDob(String dob) 
    this.dob = dob;


public int getAge() 
    return age;


public void setAge(int age) 
    this.age = age;


public int getHeight() 
    return height;


public void setHeight(int height) 
    this.height = height;


public int getWeight() 
    return weight;


public void setWeight(int weight) 
    this.weight = weight;


public String getPatientSex() 
    return patientSex;


public void setPatientSex(String patientSex) 
    this.patientSex = patientSex;


public String getPatientSystemRegistrationDate() 
    return patientSystemRegistrationDate;


public void setPatientSystemRegistrationDate(String patientSystemRegistrationDate) 
    this.patientSystemRegistrationDate = patientSystemRegistrationDate;


public int getBloodGroup() 
    return bloodGroup;


public void setBloodGroup(int bloodGroup) 
    this.bloodGroup = bloodGroup;


@Override
public int describeContents() 
    return 0;


@Override
public void writeToParcel(Parcel dest, int flags) 
    dest.writeLong(localPatientId);
    dest.writeLong(apptDoctorId);
    dest.writeLong(patientId);
    dest.writeByte((byte) (isLocalPatient ? 1 : 0));
    dest.writeString(firstName);
    dest.writeString(lastName);
    dest.writeString(phoneNumber);
    dest.writeString(email);
    dest.writeString(dob);
    dest.writeInt(age);
    dest.writeInt(height);
    dest.writeInt(weight);
    dest.writeString(patientSex);
    dest.writeString(patientSystemRegistrationDate);
    dest.writeInt(bloodGroup);


当我在数据源类 (MyPatientData) 中从网络接收数据时,调用会自动转到主要活动中的观察者。显示的列表大小为零,而从网络获取的列表大小为 10 项。

你能帮帮我吗?我真的被困了好几天,不知道下一步该做什么。 提前非常感谢。 :)

【问题讨论】:

我知道的! PagedList 有大小,但总是返回 0。我必须使用 AdapterDataObserver 来实际获取列表项的大小。你找到解决这个问题的方法了吗? 列出零,因为分页的工作方式与您想象的有点不同。您应该使用 PagedListAdapter.submitList 方法将列表提供给适配器。 PagedList 将处理获取数据。如果要调试是否成功获取数据,可以在 onBindViewHolder 方法中输入日志。希望这可以帮助其他人。 【参考方案1】:

您可以在 LivePagedListBuilder 中实现 BoundaryCallback。它包含方法 onZeroItemsLoaded。

listLiveData = new LivePagedListBuilder<>
                (new MyPatientPagedListProvider(context)
                        .getAll()
                        , pagedListConfig)
                        .setBoundaryCallback(new PagedList.BoundaryCallback() 
                    @Override
                    public void onZeroItemsLoaded() 
                        super.onZeroItemsLoaded();
 // do smth here. For example, post boolean value to MutableLiveData to notify activity //that result is empty
                    
                )
                .build();

【讨论】:

【参考方案2】:

这里是分页的简单解决方案。

第一次private void jsonRequestList(int pageCount) 将调用pageCount=1,下一次以2,3,4 递增...

private void jsonRequestList(int pageCount) method (JSON REQUEST) 内部,第一次调用if (!isScrollCalled) ,下一次调用else 块。

活动/片段:

     private boolean loading = false;
     private boolean isScrollCalled;
     int isLastPage = 10;
     int pageCount = 1;

     //Paigination
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() 
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) 
                super.onScrollStateChanged(recyclerView, newState);

                int lastvisibleitemposition = linearLayoutManager.findLastVisibleItemPosition();

                if (lastvisibleitemposition == adapter.getItemCount() - 1) 

                    if (!loading && pageCount != isLastPage) 
                        loading = true;
                        jsonRequestList(++pageCount);
                        isScrollCalled = Boolean.TRUE;
                    


                
            
        );


 private void jsonRequestList(int pageCount) 
  //Json Request

  if (!isScrollCalled) 
          adapter = new FlowerListAdapter(FlowerListActivity.this, list);
          recyclerView.setAdapter(adapter);
          adapter.notifyDataSetChanged();
               //  Log.e("LIST_SIZE", "" + list.size());
           else 
          adapter.updateList(list);
          //Log.e("LIST_SIZE", "" + list.size());

        

       loading = false;

回收站适配器:

 ArrayList<YourModelClass> list;
 Context context;

 //Pagination
public void updateList(ArrayList<YourModelClass> list) 
    this.list.addAll(list);
    this.notifyDataSetChanged();

【讨论】:

感谢您的回复,但我仍然很困惑 1. 当从网络列表中获取数据大小为 10 并且从 MyPatientData 类返回列表时,调用会自动转到主 Activity 中的观察者块,其中列表大小变为零。如果我使用旧的分页 API(如 net 的示例代码所示),我会得到正确的大小 2。为什么我们使用滚动侦听器进行回收视图,当我们滚动时分页不会自动重新加载数据。谢谢。 我们使用了scrollListner,因为这是获取recycler items最后位置的唯一方法,所以当最后位置再次出现时,我们必须点击API获取下一个数据列表。使用我的代码非常简单,如果 cmets 有任何问题。谢谢 感谢 Abhishek,这真的很简单。我会试试看。但我还需要使用上面提到的 API 来完成。所以我也需要这样解决。如果有人知道解决方案,请回答。谢谢 我的回答是仅使用 API,请尝试确保它对您有用,如果您会得到评论,任何问题。 @Matrix 我遇到了同样的问题。你找到解决办法了吗?【参考方案3】:

要获取插入项目的大小或该 RecyclerView 的大小,我们需要向我们的 RecyclerView 注册一个AdapterDataObserver

1) 创建一个 AdapterDataObserver 并覆盖你想要的函数。

public class SearchListAdapterDataObserver extends RecyclerView.AdapterDataObserver 

    private RecyclerView mRecyclerView;
    private ChangeListener mChangeListener;

    public SearchListAdapterDataObserver(RecyclerView view, ChangeListener changeListener)
        this.mRecyclerView = view;
        this.mChangeListener = changeListener;
    

    @Override
    public void onChanged() 
        super.onChanged();
        sendItemCount();
    

    private void sendItemCount() 
        if(mRecyclerView.getAdapter() != null) 
            mChangeListener.onChanged(getSize());
        
    

    @Override
    public void onItemRangeInserted(int positionStart, int itemCount) 
        super.onItemRangeInserted(positionStart, itemCount);
        sendItemCount();
    

    @Override
    public void onItemRangeChanged(int positionStart, int itemCount) 
        super.onItemRangeChanged(positionStart, itemCount);
        sendItemCount();
    

    @Override
    public void onItemRangeRemoved(int positionStart, int itemCount) 
        super.onItemRangeRemoved(positionStart, itemCount);
        sendItemCount();
    

    public int getSize() 
        if(mRecyclerView.getAdapter() != null) 
            return mRecyclerView.getAdapter().getItemCount();
        
        return 0;
    

    public interface ChangeListener 
        void onChanged(int size);
    

2) 将其注册到您的适配器并监听更改。

if(mViewModel.getSearchPaginatedAdapter() != null)
        mViewModel.getSearchPaginatedAdapter().registerAdapterDataObserver
                (new SearchListAdapterDataObserver(mRecyclerView, new SearchListAdapterDataObserver.ChangeListener() 
            @Override
            public void onChanged(int size) 
                onListChanges(size);
            
        ));
    

【讨论】:

【参考方案4】:

修改@Mike Makhovyk,这就是我最终想到的

在您的视图模型上

创建一个可变的布尔值

private MutableLiveData<Boolean> emptyListMutableLiveData = new MutableLiveData<>();

listLiveData = new LivePagedListBuilder<>
                (new MyPatientPagedListProvider(context)
                        .getAll()
                        , pagedListConfig)
                        .setBoundaryCallback(new PagedList.BoundaryCallback() 
                    @Override
                    public void onZeroItemsLoaded() 
                        super.onZeroItemsLoaded();
                        emptyListMutableLiveData.setValue(true)
                    

                    @Override
                    public void onItemAtFrontLoaded(@NonNull Complaint itemAtFront) 
                        super.onItemAtFrontLoaded(itemAtFront);
                        emptyListMutableLiveData.setValue(false);
                    

                )
                .build();

创建一个返回实时数据的方法

public LiveData<Boolean> isEmptyList() 
        return emptyListMutableLiveData;
    

在您的片段/活动上,您可以观察布尔结果然后更新您的 UI

【讨论】:

以上是关于使用分页库时,观察者显示列表大小为零的主要内容,如果未能解决你的问题,请参考以下文章

分页库 - 网络 + db 的边界回调,API 获取页面和大小

分页库 3.0:如何将项目总数传递给列表标题?

在分页库 3 中恢复滚动位置

使用分页库显示加载指示器

使用 Google 的分页库更新 PagedList 中的单个项目

分页库过滤器/搜索