0

I am populating RecyclerViews with CardViews holding two TextViews and an ImageButton (an arrow). In the items I'm fetching from JSON, it has a title and an excerpt but the excerpt is too long so I want to collapse (View.GONE) and expand (View.VISIBLE) it when an arrow button is clicked. The problem I'm having is that the app is crashing with a Null Pointer Exception.

MainActivity

public class MainActivity extends AppCompatActivity {

    private final String TAG = "MainActivity";

    ImageButton arrowbutton;
    TextView news_excerpt;

  //Creating a list of newss
    private List<NewsItems> mNewsItemsList;

    //Creating Views
    private RecyclerView recyclerView;
    private RecyclerView.Adapter adapter;
    private ProgressDialog mProgressDialog;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Initializing Views
        recyclerView = (RecyclerView) findViewById(R.id.news_recycler);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);


        //Initializing the newslist
        mNewsItemsList = new ArrayList<>();
        adapter = new NewsAdapter(mNewsItemsList, this);

        recyclerView.setAdapter(adapter);

        arrowbutton = (ImageButton) findViewById(R.id.arrow_button);
        news_excerpt = (TextView) findViewById(R.id.news_body);
        arrowbutton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int visibilty = news_excerpt.getVisibility();
                if (visibilty == View.VISIBLE) {
                    news_excerpt.setVisibility(View.GONE);
                } else {
                    news_excerpt.setVisibility(View.VISIBLE);
                }
            }
        });





        if (NetworkCheck.isAvailableAndConnected(this)) {

            //Caling method to get data
            getData();
        } else {
            final Context mContext;
            mContext = this;
            final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
            alertDialogBuilder.setTitle(R.string.alert_titl);
            alertDialogBuilder.setCancelable(false);
            alertDialogBuilder.setIcon(R.mipmap.ic_launcher);
            alertDialogBuilder.setMessage(R.string.alert_mess);
            alertDialogBuilder.setPositiveButton(R.string.alert_retry, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    if (!NetworkCheck.isAvailableAndConnected(mContext)) {
                        alertDialogBuilder.show();
                    } else {
                        getData();
                    }

                }
            });
            alertDialogBuilder.setNegativeButton(R.string.alert_cancel, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    finish();

                }
            });
            alertDialogBuilder.show();


        }


    //This method will get data from the web api

    private void getData(){


        Log.d(TAG, "getData called");
        //Showing progress dialog
        mProgressDialog = new ProgressDialog(MainActivity.this);
        mProgressDialog.setCancelable(false);
        mProgressDialog.setMessage(this.getResources().getString(R.string.load_news));
        mProgressDialog.show();


        //Creating a json request
        JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(ConfigNews.GET_URL,
                new Response.Listener<JSONArray>() {
                    @Override
                    public void onResponse(JSONArray response) {
                        Log.d(TAG, "onResponse called");
                        //Dismissing the progress dialog
                        if (mProgressDialog != null) {
                            mProgressDialog.hide();
                        }
                        /*progressDialog.dismiss();*/


                        //calling method to parse json array
                        parseData(response);

                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {

                    }
                });

        //Creating request queue
        RequestQueue requestQueue = Volley.newRequestQueue(this);

        //Adding request to the queue
        requestQueue.add(jsonArrayRequest);

    }

    //This method will parse json data
    private void parseData(JSONArray array){
        Log.d(TAG, "Parsing array");

        for(int i = 0; i<array.length(); i++) {
            NewsItems newsItem = new NewsItems();
            JSONObject jsonObject = null;
            try {
                jsonObject = array.getJSONObject(i);
                newsItem.setNews_title(jsonObject.getString(ConfigNews.TAG_VIDEO_TITLE));
                newsItem.setNews_body(jsonObject.getString(ConfigNews.TAG_VIDEO_BODY));


            } catch (JSONException w) {
                w.printStackTrace();
            }
            mNewsItemsList.add(newsItem);


        }

        adapter.notifyItemRangeChanged(0, adapter.getItemCount());


    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy called");
        if (mProgressDialog != null){
            mProgressDialog.dismiss();
            Log.d(TAG, "mProgress dialog dismissed");

        }
    }

}

Adapter

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

    private ImageLoader imageLoader;
    private Context mContext;

    //List of newss
    private List<NewsItems> mNewsItems;

    private final int VIEW_ITEM = 0;
    private final int VIEW_PROG = 1;

   public NewsAdapter(List<NewsItems> newsItems, Context context) {
       super();

       //Getting all newss
       this.mNewsItems = newsItems;
       this.mContext = context;
   }

    @Override
    public int getItemViewType(int position) {
        if (isPositionItem(position))
            return VIEW_ITEM;
        return VIEW_PROG;
    }

    private boolean isPositionItem(int position) {
       return position != getItemCount()-1;
    }



    @Override
    public RecyclerView.ViewHolder  onCreateViewHolder (ViewGroup parent, int viewType) {
        if (viewType == VIEW_ITEM) {
            View v =  LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.news_summ, parent, false);
            return new TextViewHolder(v);
        } else if (viewType == VIEW_PROG){
            View v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.recyclerfooter, parent, false);
            return new ProgressViewHolder(v);
        }

        return null;
    }



    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof TextViewHolder) {
            NewsItems newsList = mNewsItems.get(position);
            ((TextViewHolder) holder).newsTitle.setText(newsList.getNews_title());
            ((TextViewHolder) holder).newsBody.setText(newsList.getNews_body());

        } else {
            ((ProgressViewHolder) holder).progressBar.setIndeterminate(true);
            ((ProgressViewHolder) holder).loadButton.setText(R.string.reload);
        }


    }


    @Override
    public int getItemCount(){
        //Return the number of items in the data set
        return mNewsItems.size();
    }



    public static class TextViewHolder extends RecyclerView.ViewHolder{
        public TextView newsTitle, newsBody;

        TextViewHolder (View newsView) {
            super(newsView);
            newsTitle = (TextView) newsView.findViewById(R.id.news_title);
            newsBody = (TextView) newsView.findViewById(R.id.news_body);



        }
    }

    public static class ProgressViewHolder extends RecyclerView.ViewHolder {
        Button loadButton;
        ProgressBar progressBar;

        public ProgressViewHolder(View footerView){
            super(footerView);
            loadButton = (Button) footerView.findViewById(R.id.reload_button);
            progressBar = (ProgressBar) footerView.findViewById(R.id.progress_load);
        }
    }


}

Stacktrace

04-10 12:54:05.683 25474-25474/com.ozuf.newsusa E/AndroidRuntime: FATAL EXCEPTION: main
                                                                     Process: com.ozuf.newsusa, PID: 25474
                                                                     java.lang.RuntimeException: Unable to start activity ComponentInfo{com.ozuf.newsusa/com.ozuf.newsusa.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ImageButton.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
                                                                         at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2695)
                                                                         at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2769)
                                                                         at android.app.ActivityThread.access$900(ActivityThread.java:177)
                                                                         at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1430)
                                                                         at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                         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)
                                                                      Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ImageButton.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
                                                                         at com.ozuf.newsusa.MainActivity.onCreate(MainActivity.java:109)
                                                                         at android.app.Activity.performCreate(Activity.java:6178)
                                                                         at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
                                                                         at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2648)
                                                                         at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2769) 
                                                                         at android.app.ActivityThread.access$900(ActivityThread.java:177) 
                                                                         at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1430) 
                                                                         at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                                         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 109 coressponds to line arrowbutton.setOnClickListener(new View.OnClickListener() { in the MainActivity

X09
  • 3,827
  • 10
  • 47
  • 92

2 Answers2

0

Line with arrowbutton.setOnClickListener() was added in wrong place. View doesn't contain R.id.arrow_button control and (ImageButton) findViewById(R.id.arrow_button) return null.

You should move setOnClickListener() into NewsAdapter's item holder constructor (you said your recycleview item contain arrow ImageButton):

public static class TextViewHolder extends RecyclerView.ViewHolder{
    public TextView newsTitle, newsBody;
    public ImageButton arrowbutton;

    TextViewHolder (View newsView) {
        super(newsView);
        newsTitle = (TextView) newsView.findViewById(R.id.news_title);
        newsBody = (TextView) newsView.findViewById(R.id.news_body);
        arrowbutton = (ImageButton) findViewById(R.id.arrow_button);

        arrowbutton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int visibilty = newsBody.getVisibility();
                if (visibilty == View.VISIBLE) {
                    newsBody.setVisibility(View.GONE);
                } else {
                    newsBody.setVisibility(View.VISIBLE);
                }
            }
        });        
    }
} 
Alexandr Shutko
  • 1,857
  • 2
  • 20
  • 27
0

The mistake I made was adding the click listener in MainActivity when thenews_excerpt and arrowbutton views are not in activity_main.xml , it's supposed to be in PostAdapter. Alexandr Shutko, thanks for pointing this to me. And Mark Keen, thanks for supplying the link to the answer.

So, this is how I did it

I removed

arrowbutton = (ImageButton) findViewById(R.id.arrow_button);
        news_excerpt = (TextView) findViewById(R.id.news_body);
        arrowbutton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int visibilty = news_excerpt.getVisibility();
                if (visibilty == View.VISIBLE) {
                    news_excerpt.setVisibility(View.GONE);
                } else {
                    news_excerpt.setVisibility(View.VISIBLE);
                }
            }
        });

from MainActivity altogether and placed in NewsAdapter.

In the NewsAdapter, I only modified the TextViewHolder ViewHolder thus:

public static class TextViewHolder extends RecyclerView.ViewHolder  implements View.OnClickListener {
        public TextView newsTitle, newsBody;
        public ImageButton arrowbutton;


        public TextViewHolder (final View newsView) {
            super(newsView);
            newsTitle = (TextView) newsView.findViewById(R.id.news_title);
            newsBody = (TextView) newsView.findViewById(R.id.news_body);
            arrowbutton = (ImageButton) newsView.findViewById(R.id.arrow_button);

            arrowbutton.setOnClickListener(this);

        }

        @Override
                public void onClick(View v) {
                    if (v.getId() == arrowbutton.getId()) {
                        int visibilty = news_body.getVisibility();
                        if (visibilty == View.VISIBLE) {
                         news_body.setVisibility(View.GONE);
                         } else {
                              news_body.setVisibility(View.VISIBLE);
                          }
                    }
                }
    }
X09
  • 3,827
  • 10
  • 47
  • 92