简体   繁体   English

如何更新RecyclerView-notifyDataSetChanged()

[英]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: 我怀疑这是由于以下原因造成的:

  • User launches the app 用户启动该应用
  • OnCreate displays the RecyclerView OnCreate显示RecyclerView
  • JSON feed is fetched 提取了JSON提要
  • RecyclerView appears empty since the JSON fetching process takes place in the background and never updates the RecyclerView once the data has been fetched. RecyclerView显示为空,因为JSON提取过程在后台进行,并且一旦提取数据就永远不会更新RecyclerView。

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.

相关问题 如何使用notifyDataSetChanged以最简单的方式更新recyclerview - How to update recyclerview the simplest way using notifyDataSetChanged 如何在 notifyDataSetChanged 上为 recyclerView 设置动画? - How to animate recyclerView on notifyDataSetChanged? 在RecyclerView中通过notifyDataSetChanged发布更新数据 - Issue on update data by notifyDataSetChanged in RecyclerView NotifyDataSetChanged无法正确更新RecyclerView - NotifyDataSetChanged does not update the RecyclerView correctly RecyclerView中的notifyDataSetChanged时如何使指定的项目不更新 - How to make specified item not update when notifyDataSetChanged in RecyclerView 如何在RecyclerView中更新适配器notifydatasetchanged后最后设置滚动位置 - How to set scroll position at last after update adapter notifydatasetchanged in RecyclerView 如何更新片段中的 recyclerview 列表? (notifyDataSetChanged 不起作用) - How do I update the recyclerview list in the fragment? (notifyDataSetChanged not working) 如何在recyclerview gridlayout中使用notifydatasetchanged - How to use notifydatasetchanged in recyclerview gridlayout 为什么在没有notifyDataSetChanged()的情况下更新RecyclerView? - Why Does RecyclerView Update Without notifyDataSetChanged()? RecyclerView适配器不会在notifyDataSetChanged()(不是Collection)上更新 - RecyclerView adapter doesn't update on notifyDataSetChanged() (not Collection)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM