如何更新RecyclerView - notifyDataSetChanged()

时间:2015-11-15 22:59:41

标签: android adapter android-volley android-adapter android-recyclerview

我有一个活动,显示包含多个类别项目的垂直RecyclerView列表。此列表中的每个项目都是一个类别,显示"类别标题"及其相应的"图像ID"。持有此RecyclerView的活动是用户在移动设备上启动应用程序后显示的第一个活动。此外,项目类别信息是通过JSON获取的,我使用" Volley"用于从Web上的URL获取数据的库。我的问题是我的RecyclerView显示未填充和空,即使信息正在网上完美提取。我怀疑这种情况正在发生,原因如下:

  • 用户启动应用
  • OnCreate显示RecyclerView
  • 获取JSON Feed
  • RecyclerView显示为空,因为JSON获取过程在后台进行,并且在获取数据后从不更新RecyclerView。

如何在完成JSON请求后更新RecyclerView以显示所有项目?我听说在RecyclerView适配器中使用 notifyDataSetChanged(),但我不知道如何使用此功能,最重要的是将它放在我的代码中。

以下是我的代码。任何有关如何在这些情况下执行RecyclerView更新的详细帮助将不胜感激。

活动

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);
        }
    }
}

2 个答案:

答案 0 :(得分:1)

在适配器中,声明一个方法,在获取新数据集或初始数据集后,您将使用该方法更新适配器。看起来应该是这样的:

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);

答案 1 :(得分:0)

我认为你的怀疑是正确的。初始化Recyclerview后,JSON请求正在完成,因此在实例化时,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 as和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。

我建议在一个单独的类中使用AsyncTask来填充groups列表和一个接口,以通知主要活动JSON提取已完成。完成AsyncTask中的JSON提取后,使用界面填充列表,然后设置适配器。祝你好运,如果你需要更多帮助,请告诉我们。