RecyclerView 在 NotifyDataSetChanged() 之后没有更新,打开和关闭屏幕后显示内容
Posted
技术标签:
【中文标题】RecyclerView 在 NotifyDataSetChanged() 之后没有更新,打开和关闭屏幕后显示内容【英文标题】:RecyclerView doesn't update after NotifyDataSetChanged() , content shows up after turning on and off Screen 【发布时间】:2016-02-09 20:56:37 【问题描述】:我对 android 还很陌生,我正在和一群大学朋友一起构建和应用程序。我们遇到的问题是,当通过从服务器获取数据来填充要显示的元素列表时,数据会加载并添加到列表中,但 recyclerView 从不显示它(即使在从我的自定义适配器调用 NotifyDataSetChanged 之后)。当我们使用硬编码数据来测试应用程序时,这种情况从未发生过。
奇怪的是,在我关闭显示器然后重新打开后,元素列表会显示在 recyclerView 上(当我重新打开屏幕时,我的自定义适配器的 onBindViewHolder 会被调用)。此外,如果我将包含 recyclerView 的片段替换为另一个片段,然后返回给它,数据已加载到列表中但未显示。
我想知道为什么会发生这种情况以及我能做些什么来解决它(通过解决它我的意思是在我启动片段时显示填充了正确项目的 recyclerView 并在我添加更多内容时更新 recyclerView列表中的元素)
我们正在使用自定义 adpater 和 Retrofit 从服务器获取数据。
这里是代码
包含 recyclerView 的片段
public class EventViewListFragment extends Fragment implements EventListAdapter.ClickListener
private RestClient restClient;
private Builder builder;
private String token ;
private boolean userScrolled = true;
int pastVisiblesItems, visibleItemCount, totalItemCount;
private LinearLayoutManager mLayoutManager;
private RecyclerView recyclerView;
private EventListAdapter eventListAdapter;
private Button btnNewEvent;
FloatingActionButton suggestFAB;
private int currentPage=1;
public EventViewListFragment()
restClient = new RestClient();
@Override
public void onCreate(Bundle savedInstanceState)
System.out.println("Se llamo al onCreate de EventViewListFragment");
super.onCreate(savedInstanceState);
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
System.out.println("se llamo al onCreateView de EvENT List Fragment");
// Inflate the layout for this fragment
View layout =inflater.inflate(R.layout.fragment_events_list, container, false);
setUpElements(layout);
addListeners();
return layout;
private void setUpElements(View layout)
recyclerView = (RecyclerView) layout.findViewById(R.id.eventList);
eventListAdapter = new EventListAdapter(getActivity());
eventListAdapter.setClickListener(this);
eventListAdapter.setData(getInitialData());
recyclerView.setAdapter(eventListAdapter);
mLayoutManager=new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(mLayoutManager);
suggestFAB = (FloatingActionButton) layout.findViewById(R.id.suggestFAB);
builder = new Builder();
private void addListeners()
addNewEventListener();
addScrollBottomListener();
private void addNewEventListener()
/*btnNewEvent.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
EventsActivity.getInstance().toNewEventForm();
);*/
suggestFAB.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
EventsActivity.getInstance().toNewEventForm();
currentPage=1;
);
public List<Event> getInitialData()
List<Event> data=new ArrayList<>();
data = getEvents(data);
return data;
@Override
public void itemClicked(View view, int position)
EventsActivity.getInstance().toEventPage(eventListAdapter.getItemAtPos(position));
currentPage=1;
private void addScrollBottomListener()
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener()
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState)
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL)
userScrolled = true;
@Override
public void onScrolled(RecyclerView recyclerView, int dx,
int dy)
super.onScrolled(recyclerView, dx, dy);
// Here get the child count, item count and visibleitems
// from layout manager
visibleItemCount = mLayoutManager.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();
if (userScrolled && (visibleItemCount + pastVisiblesItems) == totalItemCount)
userScrolled = false;
addNewElementsToList();
);
private void addNewElementsToList()
Toast.makeText(getActivity(), "Cargando Mas Elementos", Toast.LENGTH_SHORT).show();
eventListAdapter.setData(getEvents(eventListAdapter.getData()));
private List<Event> getEvents(final List<Event> eventsList)
System.out.println("Asignando CALL");
Call<JsonElement> eventPage = restClient.getConsumerService().getEvents(token, "", currentPage, 10);
System.out.println("Enquequeing");
eventPage.enqueue(new Callback<JsonElement>()
@Override
public void onResponse(Response<JsonElement> response)
JsonObject responseBody = response.body().getAsJsonObject();
if (responseBody.has("events"))
JsonArray jsonArray = responseBody.getAsJsonArray("events");
System.out.println(jsonArray.size());
for (int i = 0; i < jsonArray.size(); i++)
JsonObject storedObject = jsonArray.get(i).getAsJsonObject();
Event current = new Event();
current.setEventId(storedObject.get("id").getAsInt());
current.setName(storedObject.get("title").getAsString());
Calendar startCal = new GregorianCalendar();
startCal.setTimeInMillis((storedObject.get("starts_at").getAsLong()) * 1000);
current.setStartDateTime(startCal);
Calendar endCal = new GregorianCalendar();
endCal.setTimeInMillis((storedObject.get("ends_at").getAsLong()) * 1000);
current.setFinishDateTime(endCal);
current.setImgUrl(storedObject.get("image").getAsString());
eventsList.add(current);
else
if (responseBody.has("error"))
System.out.println("ERROR");
wan.wanmarcos.models.Error error = builder.buildError(responseBody.get("error").getAsJsonObject());
Toast.makeText(getActivity(), "Error : " + error.toString(), Toast.LENGTH_SHORT).show();
else
@Override
public void onFailure(Throwable t)
);
eventListAdapter.notifyDataSetChanged();
currentPage++;
return eventsList;
RecyclerView 的自定义适配器
public class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.EventListViewHolder>
private LayoutInflater inflater;
private List<Event> data = Collections.emptyList();
private Context context;
private ClickListener clickListener;
public EventListAdapter(Context context)
inflater = LayoutInflater.from(context);
this.context=context;
@Override
public EventListViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
View view = inflater.inflate(R.layout.event_list_item, parent, false);
EventListViewHolder holder = new EventListViewHolder(view);
return holder;
@Override
public void onBindViewHolder(EventListViewHolder holder, int position)
System.out.println("se llamo al onBindViewHolder de ELA");
Event current = getData().get(position);
holder.title.setText(current.getName());
Picasso.with(context)
.load(current.getImgUrl())
.into(holder.img);
String startDateAndTime = current.CalendarToString(current.getStartDateTime())+" - "+current.CalendarToString(current.getFinishDateTime());
holder.dateAndTime.setText(startDateAndTime);
public void setClickListener(ClickListener clickListener)
this.clickListener=clickListener;
@Override
public int getItemCount()
return getData().size();
public List<Event> getData()
return data;
public void setData(List<Event> data)
this.data = data;
class EventListViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
TextView title;
TextView dateAndTime;
ImageView img;
public EventListViewHolder(View itemView)
super(itemView);
itemView.setOnClickListener(this);
title = (TextView) itemView.findViewById(R.id.eventListTitle);
img = (ImageView) itemView.findViewById(R.id.eventListImage);
dateAndTime =(TextView) itemView.findViewById(R.id.eventListDateAndTime);
@Override
public void onClick(View v)
if(clickListener!=null)
clickListener.itemClicked(v,getPosition());
public Event getItemAtPos(int pos)
return getData().get(pos);
public interface ClickListener
public void itemClicked(View view,int position);
Fragment XML File
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_
android:layout_>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
tools:context="wan.wanmarcos.fragments.EventViewListFragment">
<android.support.v7.widget.RecyclerView
android:id="@+id/eventList"
android:layout_
android:layout_
android:scrollbars="vertical"
>
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/suggestFAB"
android:layout_
android:layout_
android:layout_gravity="bottom|right"
app:layout_anchorGravity="bottom|right|end"
android:layout_margin="16dp"
android:clickable="true"
android:src="@mipmap/ic_add_white_48dp" />
</android.support.design.widget.CoordinatorLayout>
【问题讨论】:
【参考方案1】:Activity
中的eventsList
和适配器中的data
是两个不同的集合。你正在填补前者,而不是后者。
使用new ArrayList<>
而不是Collections.emptyList();
初始化data
,然后在您的适配器中添加一个方法,调用addAll,如下所示:
public void addAll(final List<Event> new events)
final int currentCount = data.size();
synchronized(data)
data.addAll(events);
if (Looper.getMainLooper() == Looper.myLooper())
notifyItemRangeInserted(currentCount, events.size());
else
new Handler(Looper.getMainLooper()).post(new Runnable()
@Override
public void run()
notifyItemRangeInserted(currentCount, events.size());
);
if else 检查您是从 ui 线程还是从其他线程调用 addAll 并以安全的方式调用 notifyItemRangeInserted
。在Activity
中调用onResponse
时,你填满eventsList
后,直接调用eventListAdapter.addAll(eventsList)
【讨论】:
成功了!!!非常感谢,你让我和我的朋友们免于熬夜直到日出。你是最棒的!!!! 我不喜欢这种解决方案的风格……但问题似乎出在框架上。无论如何它有效,谢谢!以上是关于RecyclerView 在 NotifyDataSetChanged() 之后没有更新,打开和关闭屏幕后显示内容的主要内容,如果未能解决你的问题,请参考以下文章
在recyclerview的嵌套recyclerview中的嵌套列表中添加项目
在垂直 recyclerView 中带有 wrap_content 的水平 recyclerView
RecyclerView | 在 RecyclerView 中使用 ListAdapter