[英]How to Update RecyclerView - notifyDataSetChanged()
I have an activity which displays a vertical RecyclerView list containing multiple category items. 我有一个活动,其中显示了包含多个类别项目的垂直RecyclerView列表。 Each item in this list is a category which displays the "category title" and its corresponding "image id".
此列表中的每个项目都是一个类别,其中显示“类别标题”及其对应的“图像ID”。 The activity holding this RecyclerView is the first activity which appears after the user has launched the app on the mobile.
拥有此RecyclerView的活动是用户在移动设备上启动应用程序后出现的第一个活动。 Additionally, the item category information is being acquired via JSON where I make use of the "Volley" library to fetch the data at a URL on the web.
另外,商品类别信息是通过JSON来获取的,其中我利用“ Volley”库在Web上的URL处获取数据。 My problem is that my RecyclerView shows unpopulated and empty even though the information is being fetched on the web perfectly.
我的问题是,即使信息已完美地从Web上获取,我的RecyclerView仍显示未填充且为空。 I suspect this is happening due to the following:
我怀疑这是由于以下原因造成的:
How can I update the RecyclerView to display all the items once the JSON request has been completed? JSON请求完成后,如何更新RecyclerView以显示所有项目? I heard of using the notifyDataSetChanged() in the RecyclerView adapter but I do not know how to make use of this feature and most importantly where to place it in my code.
我听说在RecyclerView适配器中使用notifyDataSetChanged() ,但是我不知道如何利用此功能,最重要的是,不知道如何在我的代码中放置它。
Below is my code. 下面是我的代码。 Any detailed help on how to perform a RecyclerView update under these circumstances would be greatly appreciated.
在这种情况下如何执行RecyclerView更新的任何详细帮助将不胜感激。
Activity 活动
package example.co.uk.vapp;
import android.app.ProgressDialog;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import example.co.uk.vapp.adapters.GroupAdapter;
import example.co.uk.vapp.utils.RecyclerItemClickListener;
public class CategoryListActivity extends AppCompatActivity {
// Declare variables
private RecyclerView mRecyclerView;
private GridLayoutManager mGridLayoutManager;
// Tag for logging possible errors onto the Log
private static String TAG = CategoryListActivity.class.getSimpleName();
// Progress dialog
private ProgressDialog pDialog;
// JSON Array response url - ****fictitious url****
private static final String URL = "https://www.someurl.com";
// JSON Array containing the response
static JSONArray jsonArrayResponse;
// Json Object containing the category fields such as title and image-id
static JSONObject category;
// ArrayList containing the category titles
static ArrayList<String> categoryTitles;
// ArrayList containing the category image ids
private ArrayList<String> categoryImageId;
static GroupAdapter groupAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setContentView(R.layout.activity_category_list);
// Initialize ArrayList to contain category titles
categoryTitles = new ArrayList<String>();
categoryImageId = new ArrayList<String>();
// Reference variable with layout view
mRecyclerView = (RecyclerView) findViewById(R.id.cardList);
// Use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
// Display under one column
mGridLayoutManager = new GridLayoutManager(this, 1);
mRecyclerView.setLayoutManager(mGridLayoutManager);
// Set orientation
mGridLayoutManager.setOrientation(GridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(mGridLayoutManager);
// Create list of "Group" type
final List<Group> groups = new ArrayList<Group>();
for (int i = 0; categoryTitles.size() < i && categoryImageId.size() < i; i++) {
String categoryName = categoryTitles.get(i);
int categoryImage = Integer.getInteger(categoryImageId.get(i));
groups.add(new Group(categoryName, categoryImage));
}
// Set Adapter
groupAdapter = new GroupAdapter(groups);
mRecyclerView.setAdapter(groupAdapter);
// Create click listener for each item on the list
mRecyclerView.addOnItemTouchListener(
new RecyclerItemClickListener(this, new RecyclerItemClickListener.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Intent intent = new Intent(CategoryListActivity.this, groups.get(position).getClass());
startActivity(intent);
}
})
);
// Shows message to user while makeJsonObjectRequest() is still running
pDialog = new ProgressDialog(this);
pDialog.setMessage("Getting exchange rates...");
pDialog.setCancelable(false);
// Retrieves JSON format exchange rates from Yahoo Finance
makeJsonArrayRequest();
}
private void makeJsonArrayRequest() {
// Show dialog while the request is made
showpDialog();
final JsonArrayRequest jsonArrayReq = new JsonArrayRequest(URL,
new Response.Listener<JSONArray>() {
@Override
public void onResponse(JSONArray response) {
// Log response
Log.d(TAG, response.toString());
// Set "response" to jsonArrayResponse global variable so that we can use it on other methods in this class
jsonArrayResponse = response;
try {
// Get array with all categories (raw)
for (int i = 0; i < jsonArrayResponse.length(); i++) {
category = jsonArrayResponse.getJSONObject(i);
String title = category.getString("category-localized-title");
categoryTitles.add(title);
String imageId = category.getString("product-category-image-id");
categoryImageId.add(imageId);
}
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(gevapplicationContext(),
"Error: " + e.getMessage(),
Toast.LENGTH_LONG).show();
}
for (int i = 0; i < categoryTitles.size(); i++) {
Log.i("categoryTitles", categoryTitles.get(i).toString());
Log.i("categoryImageIds", categoryImageId.get(i).toString());
}
// Hide dialog after information has been requested
hidepDialog();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// Warn user
Toast.makeText(gevapplicationContext(),
"No internet connection", Toast.LENGTH_LONG).show();
// Log error
Log.e("test", error.getMessage());
// hide the progress dialog
hidepDialog();
}
});
// Adding request to request queue
AppController.getInstance().addToRequestQueue(jsonArrayReq);
}
/**
* Method for showing dialog
*/
private void showpDialog() {
if (!pDialog.isShowing())
pDialog.show();
}
/**
* Method for hiding dialog
*/
private void hidepDialog() {
if (pDialog.isShowing())
pDialog.dismiss();
}
}
RecyclerView Adapter RecyclerView适配器
package example.co.uk.vapp.adapters;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import example.co.uk.vapp.Group;
import example.co.uk.vapp.R;
public class GroupAdapter extends RecyclerView.Adapter<GroupAdapter.GroupViewHolder> {
private List<Group> groupList;
public GroupAdapter(List<Group> groupList) {
this.groupList = groupList;
}
@Override
public int getItemCount() {
return groupList.size();
}
@Override
public void onBindViewHolder(GroupViewHolder holder, int position) {
Group group = groupList.get(position);
holder.vGroupTitle.setText(group.getGroupTitle());
holder.vGroupImage.setImageResource(group.getGroupImage());
}
@Override
public GroupViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.group_list_card_layout, parent, false);
return new GroupViewHolder(itemView);
}
// Create the ViewHolder class "pattern"
public static class GroupViewHolder extends RecyclerView.ViewHolder {
protected TextView vGroupTitle;
protected ImageView vGroupImage;
public GroupViewHolder(View v) {
super(v);
vGroupTitle = (TextView) v.findViewById(R.id.title);
vGroupImage = (ImageView) v.findViewById(R.id.image);
}
}
}
In your adapter, declare a method with which you will update the adapter once you fetched the new or initial data set. 在适配器中,声明一个方法,一旦获取新的或初始的数据集,便可以使用该方法来更新适配器。 It should look something like this:
它看起来应该像这样:
public void swapDataSet(list<Group> newData){
this.groupList = newData;
//now, tell the adapter about the update
notifyDataSetChanged();
}
The above method will work, but it won't necessary be the most efficient way of doing it for subsequent changes, because the adapter will update all the items assuming the entire dataset has changed. 上面的方法可以使用,但并不一定是进行后续更改的最有效方法,因为假定整个数据集已更改,适配器将更新所有项目。 Once your adapter is set for the first time, it is better, and more efficient, to use more specific change notifications like
notifyItemInsered(int pos)
, notifyItemDeleted(int pos)
, etc if possible. 首次设置适配器后,如果可能的话,使用更具体的更改通知(例如
notifyItemInsered(int pos)
, notifyItemDeleted(int pos)
等notifyItemDeleted(int pos)
会更好,更高效。 This way you will also get nice animations with no extra effort on your part. 这样一来,您就可以轻松获得出色的动画。
More specifically, in your case, you are updating the groups before there are any titles or categories. 更具体地说,根据您的情况,您要在没有任何标题或类别之前更新组。 Why not do this:
为什么不这样做:
for (int i = 0; i < jsonArrayResponse.length(); i++) {
category = jsonArrayResponse.getJSONObject(i);
String title = category.getString("category-localized-title");
String imageId = category.getString("product-category-image-id");
categories.add(new Group(title, imageId));
}
//now that you have fresh groups
GroupAdapter.swapDataSet(groups);
I think your suspicion is correct. 我认为您的怀疑是正确的。 The JSON request is completing after the Recyclerview is initialized, so the data is not available to the RecyclerView upon instantiation.
初始化Recyclerview后,JSON请求已完成,因此实例化后,数据对RecyclerView不可用。 Also, some of your lists are not being initialized correctly.
另外,您的某些列表未正确初始化。
Let's go through the order of some of the key elements of your code: 让我们看一下代码中一些关键元素的顺序:
1. categoryTitles = new ArrayList<String>();
1.
categoryTitles = new ArrayList<String>();
Right now, categoryTitles
is empty with categoryTitles.size()
= 0 and categoryImageId.size()
= 0. 现在,
categoryTitles
为空, categoryTitles.size()
= 0, categoryImageId.size()
= 0。
2. List item mRecyclerView = (RecyclerView) findViewById(R.id.cardList);
2.
List item mRecyclerView = (RecyclerView) findViewById(R.id.cardList);
This is good. 很好
3. mGridLayoutManager = new GridLayoutManager(this, 1);
3.
mGridLayoutManager = new GridLayoutManager(this, 1);
etc. etc. 等等等
This looks good. 看起来不错
4. for (int i = 0; categoryTitles.size() < i && categoryImageId.size() < i; i++) {...}
4.
for (int i = 0; categoryTitles.size() < i && categoryImageId.size() < i; i++) {...}
Remember from 1. categoryTitles.size()
= 0 as and categoryImageId.size()
= 0, so this loop won't execute. 请记住,从1开始,
categoryTitles.size()
= 0, categoryImageId.size()
= 0,因此不会执行此循环。 The lists won't be populated at all. 列表将完全不会填充。
Also, you probably have your conditionals backwards. 另外,您可能会使条件倒退。 Change them to
i < categoryTitles.size()
and i < categoryImageId.size()
将它们更改为
i < categoryTitles.size()
和i < categoryImageId.size()
5. final List<Group> groups = new ArrayList<Group>();
5.
final List<Group> groups = new ArrayList<Group>();
groups
is empty. groups
为空。
6. groupAdapter = new GroupAdapter(groups);
6.
groupAdapter = new GroupAdapter(groups);
groups
is currently empty, so is GroupAdapter
. groups
当前为空, GroupAdapter
为空。
7. mRecyclerView.setAdapter(groupAdapter);
7.
mRecyclerView.setAdapter(groupAdapter);
You are passing an empty list to the adapter. 您正在将一个空列表传递给适配器。
8. makeJsonArrayRequest();
8.
makeJsonArrayRequest();
This should happen before the adapter is set. 这应该在设置适配器之前发生。
To fix things, groups
should be populated before it is passed to the adapter, and even if you set the order of this code correctly, it won't work because most likely the JSON fetch will happen after the adapter is set to the RecyclerView. 为了解决问题,应该在将
groups
传递到适配器之前对其进行填充,即使您正确设置了此代码的顺序,也无法正常工作,因为在将适配器设置为RecyclerView之后,很可能会进行JSON提取。
I would suggest using an AsyncTask
in a separate class to populate the groups
list and an interface to inform the main activity that the JSON fetch is complete. 我建议在单独的类中使用
AsyncTask
来填充groups
列表和一个接口,以通知主要活动JSON提取已完成。 Upon completion of the JSON fetch in your AsyncTask
, use the interface to populate the lists and subsequently set the Adapter. 在
AsyncTask
完成JSON提取后,请使用接口填充列表,然后设置适配器。 Good luck and if you need more help, please let us know. 祝您好运,如果您需要更多帮助,请告诉我们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.