1

I have this json:

{
  "title": "Sample title of this json",
  "excerpt": "Sample excerpt for the json. Random alphabets: yehodjjd uushdkjnjk jghdd",
  "tags": {
    "chlorine": {
      "name": "chlorine",
      "post_count": 32,
    },
    "flourine": {
      "name": "flourine",
      "post_count": 78,
    }
  },
}

"title", "excerpt" and "tags" are known keys. They are always "title", "excerpt" and "tags". On other hand, the JsonObjects in "tags" are dynamic. Let me explain.

The number of the objects are two in this json, but it might be one object, two or even seven. There are just "chlorine" and ""flourine" here but the objects might be just anything. So in other words I don't know before hand the objects in the "tags".

What I want to do is display "title" and "except" in a TextView, so far that is working.

I also want to display the dynamic objects in "tags" in a kind of dynamic list, I chose RecyclerView and this seems not to be working. The objects is being parsed quite alright but the problem is displaying it.

These are my codes:

fragment_details.xml

<android.support.v4.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:scrollbars="vertical"
    android:fillViewport="true"
    android:id="@+id/details_layout"
    tools:context=".Fragment.DetailsFragment">

    <RelativeLayout

        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/details_relat">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:id="@+id/dpost_title"
            android:visibility="gone"
            android:layout_margin="5dp"
            android:textStyle="bold"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/dpost_title"
            android:textColorLink="@color/textlink"
            android:visibility="invisible"
            android:id="@+id/dpost_content"
            android:layout_margin="5dp"/>

        <android.support.v7.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/tag_cat_recy"/>

    </RelativeLayout>

</android.support.v4.widget.NestedScrollView>

tags_cats_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:id="@+id/tag_cat_lin"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/tag_cat_text"/>

</LinearLayout>

TagCatItem

public class TagCatItem {
    private String tagCatName;
    private int tagCatCount;

    public String getTagCatName() {
        return tagCatName;
    }

    public void setTagCatName(String tagCatName) {
        this.tagCatName = tagCatName;
    }


    public int getTagCatCount() {
        return tagCatCount;
    }

    public void setTagCatCount(int tagCatCount) {
        this.tagCatCount = tagCatCount;
    }
}

DetailsFragment

public class DetailsFragment extends Fragment{

    private List<TagCatItem> mTagCatItem;
    RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    LinearLayoutManager mLayoutManager;

    TextView postTitle, postContent;

    public DetailsFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView called");
        // Inflate the layout for this fragment
        view =  inflater.inflate(R.layout.fragment_details, container, false);

        postTitle = (TextView)view.findViewById(R.id.dpost_title);
        postContent = (TextView) view.findViewById(R.id.dpost_content);

        mRecyclerView = (RecyclerView) view.findViewById(R.id.tag_cat_recy);

        getPost();

        return view;
    }


    private void  getPost() {
        Log.d(TAG, "getPost called"); 

        JsonObjectRequest postDetails = new JsonObjectRequest(Method.GET, url, null,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        Log.d(TAG, "onResponse for getPOst called");
                        parseJson(response);
                    }
                },
                new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.d(TAG,  "onErrorResponse for getPost called");
            }
        });

        //Creating requestqueue
        RequestQueue requestQueue = Volley.newRequestQueue(getActivity());

        //Adding request queue
        requestQueue.add(postDetails);
    }
    private void parseJson(JSONObject object) {
        Log.d(TAG, "Parsing Json");
        try {
            mTitle = String.valueOf(Html.fromHtml(object.getString("title")));
            postTitle.setText(mTitle);

            mLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
            mRecyclerView.setLayoutManager(mLayoutManager);
            mAdapter = new TagCatAdapter(mTagCatItem, getActivity());
            mRecyclerView.setAdapter(mAdapter);

            String content = object.getString("content");
            postContent.setText(content);

            JSONObject tags = object.getJSONObject("tags");
            Iterator tagsObjs = tags.keys();

            while (tagsObjs.hasNext()) {
                // loop to get dynamic tag objects
                String tagObj = (String) tagsObjs.next();
                Log.d(TAG, "tagObj is " + tagObj);

                JSONObject tagObjDetails = tags.getJSONObject(tagObj);
                TagCatItem tagCatItem = new TagCatItem();
                String tagName = tagObjDetails.getString("name");
                tagCatItem.setTagCatName(tagName);
                Log.d(TAG, "Tag Name is " + tagName);

                int tagDetailsNum = tagObjDetails.getInt("post_count");
                tagCatItem.setTagCatCount(tagDetailsNum);
                Log.d(TAG, "Number of posts " + tagDetailsNum);
                mTagCatItem.add(tagCatItem);
                mAdapter.notifyItemRangeChanged(0, mAdapter.getItemCount());
            }

                //Unhiding views
                postTitle.setVisibility(View.VISIBLE);
                postContent.setVisibility(View.VISIBLE);
            }

        } catch (JSONException w) {
            w.printStackTrace();

        }
    }
    }

**TagCatAdapter**

public class TagCatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private Context mContext;
private List<TagCatItem> mTagCatItems;

public TagCatAdapter(List<TagCatItem> tagCatItems, Context context) {
    super();
    this.mTagCatItems = tagCatItems;
    this.mContext = context;
}


@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.tags_cats_item, parent, false);
    return new TextViewHolder(v);
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    TagCatItem tagCatItem = mTagCatItems.get(position);
    ((TextViewHolder) holder).tagCatName.setText(tagCatItem.getTagCatName());
}


public class TextViewHolder extends RecyclerView.ViewHolder{
    public TextView tagCatName;

    public TextViewHolder(View mView) {
        super(mView);
        tagCatName = (TextView) view.findViewById(R.id.tag_cat_text);
    }
}


@Override
public int getItemCount() {
    return mTagCatItems.size();
}

}

StackTrace

07-01 20:06:25.563 21725-21725/com.ozuf.poster E/AndroidRuntime: FATAL EXCEPTION: main
                                                                     Process: com.ozuf.poster, PID: 21725
                                                                     java.lang.NullPointerException: Attempt to invoke interface method 'boolean java.util.List.add(java.lang.Object)' on a null object reference
                                                                         at com.ozuf.poster.Fragment.DetailsFragment.parseJson(DetailsFragment.java:492)
                                                                         at com.ozuf.poster.Fragment.DetailsFragment.access$600(DetailsFragment.java:73)
                                                                         at com.ozuf.poster.Fragment.DetailsFragment$7.onResponse(DetailsFragment.java:395)
                                                                         at com.ozuf.poster.Fragment.DetailsFragment$7.onResponse(DetailsFragment.java:391)
                                                                         at com.android.volley.toolbox.JsonRequest.deliverResponse(JsonRequest.java:65)
                                                                         at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:99)
                                                                         at android.os.Handler.handleCallback(Handler.java:739)
                                                                         at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                         at android.os.Looper.loop(Looper.java:135)
                                                                         at android.app.ActivityThread.main(ActivityThread.java:5910)
                                                                         at java.lang.reflect.Method.invoke(Native Method)
                                                                         at java.lang.reflect.Method.invoke(Method.java:372)
                                                                         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1405)
                                                                         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1200)

From the stacktrace, Line 492 is mTagCatItem.add(tagCatItem);

Line 73 is public class DetailsFragment extends Fragment {

Line 395 is parseJson(response);

Line 391 is new Response.Listener<JSONObject>() {

Attempted Solution

I have tried moving:

mLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
            mRecyclerView.setLayoutManager(mLayoutManager);
            mAdapter = new TagCatAdapter(mTagCatItem, getActivity());
            mRecyclerView.setAdapter(mAdapter);

to onCreateView of DetailsFragment but it crashed with the error that I am trying to call mTagCatItems.size() on a null object reference.

X09
  • 3,827
  • 10
  • 47
  • 92

1 Answers1

1

You are not initializing mTagCatItems which is why it is null (hence the NPEs). To fix this you need to change the line where you declare mTagCatItems to also initialize it:

private List<TagCatItem> mTagCatItem = new ArrayList<TagCatItem>();
Dr. Nitpick
  • 1,662
  • 1
  • 12
  • 16
  • Thanks, that fixed the NPE issue. But the only problem left is that only the first object is displayed. I.e only "chlorine" is displayed in the textview, fluorine is not displayed. – X09 Jul 02 '16 at 08:17
  • Glad that helped with the NPE, now for the other issues Id recommend this: One, can you post your logcat output? Itd be nice to see how many times the while loop executes by looking at the log messaged. Two, can you post the json that you are getting down from the server? (easy way to do this is to log the whole JSON object before you get into the loop and then copy that). Three, try removing the line: `mAdapter.notifyItemRangeChanged(0, mAdapter.getItemCount());` and after the while loop call: `mAdapter.notifyDataSetChanged()`. – Dr. Nitpick Jul 02 '16 at 15:58
  • Sorry, that has been resolved. The problem was that the width of the LinearLayout in **tags_cats_item.xml** was set to `match_parent` so all the items were hidden till I scroll. I have set it to `wrap_content` and is okay. And please why should I use `mAdapter.notifyDataSetChanged()` instead of `mAdapter.notifyItemRangeChanged(0, mAdapter.getItemCount());`/ – X09 Jul 02 '16 at 17:58
  • It depends what you want to do. If you are updating only a few items in the list then `notifyItemRangeChanged` is the right method to go with. If you are changing the items in the list then `notifyDataSetChanged` (or `notifyItemInserted`) is better. Since I was suggesting notifying the adapter after the loop where you added your items to the list I was recommending `notifyDataSetChanged`. More info here about the different methods to update your listview and when to use them: http://stackoverflow.com/a/33792079/6526330 – Dr. Nitpick Jul 02 '16 at 19:23