使用分页库时,观察者显示列表大小为零
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您可以在 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 获取页面和大小