androidandroid-listviewnullpointerexceptionparse-platformandroid-parsequeryadapter

NullPointerException on view.findViewById() with Parse Query Adapter/ListView


I've populated a ListView using a custom adapter with Parse Query Adapter. My issue is that I can only seem to access the first item of a list. Within the ListView, I have a button that I want to call a function.

    android:id="@+id/showGrunts"
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:layout_below="@id/buttonthang"
    android:clickable="true"
    android:onClick="displayGruntList"

And here's the function that is called upon clicking the button:

public void displayGruntList(View view) {
    TextView objectID = (TextView) findViewById(R.id.tvPostIDCheese);

    String id = objectID.getText().toString();

    //String id = ((TextView)(view.findViewById(R.id.tvPostIDCheese))).getText().toString();

    Intent i = new Intent(GruntrMain.this, GruntList.class);
    Bundle idData = new Bundle();
    idData.putString("id", id);
    i.putExtras(idData);
    startActivity(i);
}

Note the commented line. My issue is that if I click this button, it will only ever show the related items for the first item of the ListView, even if I click on the button from a different item in the list. I've been able to determine that by displaying the ID as a toast once it is acquired. No matter which one I click, it always shows the ID of the top item in the list. This remains true if I reorder them, and by displaying the IDs within each list item, I'm confident that it's not an issue in my query since they all show up with their proper ID's.

My understanding of the view that gets passed into the function is that it is from the actual item that is being selected. I've seen other code that uses view.findViewById(), but when I try this, the line gives me a NullPointerException. What I want to be able to do is grab the text (which represents an ID) from a TextView for that particular item. Again, with the code I currently have, it only ever grabs that text from the first item. So the issue is not within my Parse Query, but with how I'm accessing it. The entire .java file that this problem is happening in can be seen below.

package com.snowflakes.gruntr;

import java.util.List;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import com.parse.FindCallback;
import com.parse.GetCallback;
import com.parse.ParseException;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.ParseQueryAdapter;
import com.parse.ParseRelation;
import com.parse.ParseUser;

public class GruntrMain extends Activity {

private static final int LOGIN_REQUEST = 0;

private ListView listView;
private ParseQueryAdapter<ParseObject> mainAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
    setContentView(R.layout.gruntr_main);

    ImageView addF = (ImageView) findViewById(R.id.ivAddFriend);
    final ImageView fReq = (ImageView) findViewById(R.id.ivFriendRequest);
    ImageView mPost = (ImageView) findViewById(R.id.ivMakePost);

    mainAdapter = new CustomAdapter(this);

    listView = (ListView) findViewById(R.id.lvPostList);
    listView.setAdapter(mainAdapter);
    mainAdapter.loadObjects();

    Thread refreshFeed = new Thread() {
        public void run() {
            while (true) {

                try {
                    sleep(20000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {

                    mainAdapter.loadObjects();
                }

            }
        }// public void run()
    }; // new Thread()
    refreshFeed.start();

    // This thread is used for friend polling
    // In order to get it to run repeatedly, may need to make an infinite
    // loop
    // If a friend request is found to be pending, it will cause a button to
    // appear, allowing you to accept the friend request
    // For later implementation, this can be refined
    // For example: It can cause the notification area to show that you have
    // a friend request pending
    // Also, the ability to deny friend requests is already built in. I'll
    // actually code that in when it become relevant
    // -- Aaron
    Thread friendRequestPolling = new Thread() {
        public void run() {
            while (true) {

                try {
                    sleep(4000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {

                    ParseUser user = ParseUser.getCurrentUser();
                    ParseQuery<ParseObject> friendQuery = ParseQuery
                            .getQuery("FriendRequests");
                    friendQuery.whereEqualTo("Recipient", user);

                    friendQuery
                            .getFirstInBackground(new GetCallback<ParseObject>() {
                                public void done(ParseObject request,
                                        ParseException e) {
                                    if (request != null) {
                                        fReq.setImageResource(R.drawable.friendrequesttrue);
                                    } else {
                                        fReq.setImageResource(R.drawable.friendrequest);
                                    }

                                }
                            });

                }

            }
        }// public void run()
    }; // new Thread()
    friendRequestPolling.start();

    Thread friendAcceptedPolling = new Thread() {
        public void run() {
            while (true) {

                try {
                    sleep(4000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {

                    ParseUser user = ParseUser.getCurrentUser();
                    ParseQuery<ParseObject> friendQuery = ParseQuery
                            .getQuery("FriendRequests");
                    friendQuery.whereEqualTo("Sender", user);
                    friendQuery.whereEqualTo("Accepted", "true");

                    friendQuery
                            .findInBackground(new FindCallback<ParseObject>() {
                                public void done(
                                        List<ParseObject> returnedUsers,
                                        ParseException e) {
                                    for (ParseObject currentUser : returnedUsers) {
                                        if (currentUser != null) {
                                            ParseUser user = ParseUser
                                                    .getCurrentUser();
                                            ParseRelation<ParseUser> relation = user
                                                    .getRelation("friends");
                                            relation.add(currentUser
                                                    .getParseUser("Recipient"));
                                            user.saveInBackground();
                                            currentUser
                                                    .deleteInBackground();
                                        }

                                    }
                                }
                            });

                }

            }
        }// public void run()
    }; // new Thread()
    friendAcceptedPolling.start();

    mPost.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub

            Intent i = new Intent(GruntrMain.this, MakePost.class);
            startActivity(i);
        }
    });

    addF.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub

            Intent i = new Intent(GruntrMain.this, AddFriend.class);
            startActivity(i);

        }
    });

    fReq.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub

            Intent i = new Intent(GruntrMain.this, FriendRequests.class);
            startActivity(i);
        }
    });

}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == LOGIN_REQUEST) {
        if (resultCode == RESULT_OK) {
            Intent i = new Intent(GruntrMain.this, GruntrMain.class);
            i.putExtra("User", ParseUser.getCurrentUser().getString("name"));
            startActivity(i);
            finish();
        } else {
            Intent i = new Intent(GruntrMain.this, Goodbye.class);
            startActivity(i);
            finish();
        }
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main_menu, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    } else if (id == R.id.action_logout) {
        ParseUser.logOut();
        ParseUser currentUser = ParseUser.getCurrentUser();

        ParseLoginBuilder builder = new ParseLoginBuilder(GruntrMain.this);
        Intent parseLoginIntent = builder
                .setAppLogo(R.drawable.orangebanner)
                .setParseLoginEnabled(true)
                .setParseLoginButtonText("Go")
                .setParseSignupButtonText("Register")
                .setParseLoginHelpText("Forgot password?")
                .setParseLoginInvalidCredentialsToastText(
                        "Your email and/or password is not correct")
                .setParseLoginEmailAsUsername(true)
                .setParseSignupSubmitButtonText("Submit registration")
                .build();
        startActivityForResult(parseLoginIntent, 0);
    } else if (id == R.id.action_post) {
        Intent i = new Intent(GruntrMain.this, MakePost.class);
        startActivity(i);
    }
    return super.onOptionsItemSelected(item);
}

public void displayGruntList(View view) {
    TextView objectID = (TextView) findViewById(R.id.tvPostIDCheese);

    String id = objectID.getText().toString();

    // String id =
    // ((TextView)(view.findViewById(R.id.tvPostIDCheese))).getText().toString();

    Intent i = new Intent(GruntrMain.this, GruntList.class);
    Bundle idData = new Bundle();
    idData.putString("id", id);
    i.putExtras(idData);
    startActivity(i);
}

public void gruntAtIt(View view) {

    TextView objectID = (TextView) findViewById(R.id.tvPostIDCheese);

    String id = objectID.getText().toString();

    ParseQuery<ParseObject> query = new ParseQuery<ParseObject>("Post");

    query.whereEqualTo("objectId", id);

    ParseObject post = null;

    try {
        post = query.getFirst();
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } finally {
        if (post != null) {
            post.increment("Grunts");
            post.saveInBackground();
        }
    }

    Intent i = new Intent(GruntrMain.this, RecordGrunt.class);
    Bundle idData = new Bundle();
    idData.putString("id", id);
    i.putExtras(idData);
    startActivity(i);

}

}

Here's CustomAdapter.java, where the query is made and the list is populated.

package com.snowflakes.gruntr;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.parse.ParseFile;
import com.parse.ParseImageView;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.ParseQueryAdapter;
import com.parse.ParseRelation;
import com.parse.ParseUser;

public class CustomAdapter extends ParseQueryAdapter<ParseObject> {


public CustomAdapter(Context context) {
    // Use the QueryFactory to construct a PQA that will only show
    // Todos marked as high-pri
    super(context, new ParseQueryAdapter.QueryFactory<ParseObject>() {
        public ParseQuery create() {

            ParseUser user = ParseUser.getCurrentUser();

            ParseRelation<ParseUser> relation = user.getRelation("friends");
            ParseQuery friendsQuery = relation.getQuery();

            ParseQuery<ParseObject> query = new ParseQuery<ParseObject>("Post");
            query.whereMatchesQuery("CreatedBy", friendsQuery);
            query.addDescendingOrder("createdAt");
            return query;
        }
    });
}

// Customize the layout by overriding getItemView
@Override
public View getItemView(ParseObject object, View v, ViewGroup parent) {
    if (v == null) {
        v = View.inflate(getContext(), R.layout.individual_post, null);
    }

    super.getItemView(object, v, parent);

    // Add and download the image
    ParseImageView todoImage = (ParseImageView) v.findViewById(R.id.icon);
    ParseFile imageFile = object.getParseFile("Image");
    if (imageFile != null) {
        todoImage.setParseFile(imageFile);
        todoImage.loadInBackground();
    }

    // Add the title view
    TextView titleTextView = (TextView) v.findViewById(R.id.text1);
    titleTextView.setText(object.get("Name").toString());

    // Add a reminder of how long this item has been outstanding
    TextView timestampView = (TextView) v.findViewById(R.id.timestamp);
    timestampView.setText(object.getCreatedAt().toString());

    TextView objectID = (TextView) v.findViewById(R.id.tvPostIDCheese);
    objectID.setText(object.getObjectId());

    TextView gruntCount = (TextView) v.findViewById(R.id.showGrunts);
    gruntCount.setText("Show Grunts (" + object.getNumber("Grunts") + ")");


    return v;
}



}

Solution

  • The view argument passed to your onClick is just the view that was clicked, not the surrounding layout. Unless tvPostIDCheese is a child view of your button it's not accessible in that fashion.

    Instead try storing your object ID within your clickable's view using gruntCount.setTag(object.getObjectId()) during your adapter's getItemView. Then in displayGruntList retrieve it with (String)view.getTag().

    See setTag and getTag for further reference.