如何更新 RecyclerView - notifyDataSetChanged()
How to Update RecyclerView - notifyDataSetChanged()
我有一个 activity,它显示一个包含多个类别项目的垂直 RecyclerView 列表。此列表中的每个项目都是一个类别,显示 "category title" 及其对应的 "image id"。持有此 RecyclerView 的 activity 是用户在移动设备上启动应用程序后出现的第一个 activity。此外,项目类别信息是通过 JSON 获取的,我使用 "Volley" 库在网络上的 URL 获取数据。我的问题是我的 RecyclerView 显示未填充且为空,即使在网络上完美获取信息也是如此。我怀疑这是由于以下原因造成的:
- 用户启动应用程序
- OnCreate 显示 RecyclerView
- JSON 提要已获取
- RecyclerView 显示为空,因为 JSON 获取过程在后台进行,一旦获取数据就不会更新 RecyclerView。
JSON 请求完成后,如何更新 RecyclerView 以显示所有项目?我听说在 RecyclerView 适配器中使用 notifyDataSetChanged(),但我不知道如何使用此功能,最重要的是不知道将它放在我的代码中的什么位置。
下面是我的代码。非常感谢任何有关如何在这些情况下执行 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 适配器
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);
}
}
}
在您的适配器中,声明一个方法,您将使用该方法在获取新的或初始数据集后更新适配器。它应该看起来像这样:
public void swapDataSet(list<Group> newData){
this.groupList = newData;
//now, tell the adapter about the update
notifyDataSetChanged();
}
上述方法可行,但它不一定是后续更改的最有效方法,因为假设整个数据集已更改,适配器将更新所有项目。首次设置适配器后,如果可能的话,使用更具体的更改通知(如 notifyItemInsered(int pos)
、notifyItemDeleted(int pos)
等)会更好、更高效。这样你也可以得到漂亮的动画,而不需要你付出额外的努力。
更具体地说,在您的情况下,您是在有任何标题或类别之前更新组。为什么不这样做:
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);
我认为你的怀疑是正确的。 JSON 请求在 Recyclerview 初始化后完成,因此数据在实例化时不可用于 RecyclerView。此外,您的某些列表未正确初始化。
让我们来看看代码中一些关键元素的顺序:
1. categoryTitles = new ArrayList<String>();
现在,categoryTitles
是空的,categoryTitles.size()
= 0,categoryImageId.size()
= 0。
2。 List item mRecyclerView = (RecyclerView) findViewById(R.id.cardList);
这个不错
3。 mGridLayoutManager = new GridLayoutManager(this, 1);
等。等等
这看起来不错。
4. for (int i = 0; categoryTitles.size() < i && categoryImageId.size() < i; i++) {...}
记住1. categoryTitles.size()
= 0 和categoryImageId.size()
= 0,所以这个循环不会执行。根本不会填充列表。
此外,您的条件句可能倒退了。将它们更改为 i < categoryTitles.size()
和 i < categoryImageId.size()
5. final List<Group> groups = new ArrayList<Group>();
groups
为空。
6. groupAdapter = new GroupAdapter(groups);
groups
当前为空,GroupAdapter
.
也为空
7. mRecyclerView.setAdapter(groupAdapter);
您正在向适配器传递一个空列表。
8. makeJsonArrayRequest();
这应该发生在之前设置适配器。
要解决问题,groups
应该在传递给适配器之前填充,即使您正确设置此代码的顺序,它也不会工作,因为很可能 JSON 获取将在适配器设置为 RecyclerView 后发生。
我建议在单独的 class 中使用 AsyncTask
来填充 groups
列表,并使用一个接口来通知主 activity JSON 获取完成。在您的 AsyncTask
中完成 JSON 提取后,使用界面填充列表并随后设置适配器。祝您好运,如果您需要更多帮助,请告诉我们。
我有一个 activity,它显示一个包含多个类别项目的垂直 RecyclerView 列表。此列表中的每个项目都是一个类别,显示 "category title" 及其对应的 "image id"。持有此 RecyclerView 的 activity 是用户在移动设备上启动应用程序后出现的第一个 activity。此外,项目类别信息是通过 JSON 获取的,我使用 "Volley" 库在网络上的 URL 获取数据。我的问题是我的 RecyclerView 显示未填充且为空,即使在网络上完美获取信息也是如此。我怀疑这是由于以下原因造成的:
- 用户启动应用程序
- OnCreate 显示 RecyclerView
- JSON 提要已获取
- RecyclerView 显示为空,因为 JSON 获取过程在后台进行,一旦获取数据就不会更新 RecyclerView。
JSON 请求完成后,如何更新 RecyclerView 以显示所有项目?我听说在 RecyclerView 适配器中使用 notifyDataSetChanged(),但我不知道如何使用此功能,最重要的是不知道将它放在我的代码中的什么位置。
下面是我的代码。非常感谢任何有关如何在这些情况下执行 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 适配器
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);
}
}
}
在您的适配器中,声明一个方法,您将使用该方法在获取新的或初始数据集后更新适配器。它应该看起来像这样:
public void swapDataSet(list<Group> newData){
this.groupList = newData;
//now, tell the adapter about the update
notifyDataSetChanged();
}
上述方法可行,但它不一定是后续更改的最有效方法,因为假设整个数据集已更改,适配器将更新所有项目。首次设置适配器后,如果可能的话,使用更具体的更改通知(如 notifyItemInsered(int pos)
、notifyItemDeleted(int pos)
等)会更好、更高效。这样你也可以得到漂亮的动画,而不需要你付出额外的努力。
更具体地说,在您的情况下,您是在有任何标题或类别之前更新组。为什么不这样做:
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);
我认为你的怀疑是正确的。 JSON 请求在 Recyclerview 初始化后完成,因此数据在实例化时不可用于 RecyclerView。此外,您的某些列表未正确初始化。
让我们来看看代码中一些关键元素的顺序:
1. categoryTitles = new ArrayList<String>();
现在,categoryTitles
是空的,categoryTitles.size()
= 0,categoryImageId.size()
= 0。
2。 List item mRecyclerView = (RecyclerView) findViewById(R.id.cardList);
这个不错
3。 mGridLayoutManager = new GridLayoutManager(this, 1);
等。等等
这看起来不错。
4. for (int i = 0; categoryTitles.size() < i && categoryImageId.size() < i; i++) {...}
记住1. categoryTitles.size()
= 0 和categoryImageId.size()
= 0,所以这个循环不会执行。根本不会填充列表。
此外,您的条件句可能倒退了。将它们更改为 i < categoryTitles.size()
和 i < categoryImageId.size()
5. final List<Group> groups = new ArrayList<Group>();
groups
为空。
6. groupAdapter = new GroupAdapter(groups);
groups
当前为空,GroupAdapter
.
7. mRecyclerView.setAdapter(groupAdapter);
您正在向适配器传递一个空列表。
8. makeJsonArrayRequest();
这应该发生在之前设置适配器。
要解决问题,groups
应该在传递给适配器之前填充,即使您正确设置此代码的顺序,它也不会工作,因为很可能 JSON 获取将在适配器设置为 RecyclerView 后发生。
我建议在单独的 class 中使用 AsyncTask
来填充 groups
列表,并使用一个接口来通知主 activity JSON 获取完成。在您的 AsyncTask
中完成 JSON 提取后,使用界面填充列表并随后设置适配器。祝您好运,如果您需要更多帮助,请告诉我们。