sje sje - 3 months ago 13
Android Question

Null Pointer Exception when invoking setOnItemClickListener

New to Android and Java and learning by doing but I seem to be missing something fundamental on detecting item clicks in a ListView.

I have an activity that is a couple of buttons with a ListView below them. Each ListView item has an icon that can be clicked on, that will eventually go to another activity, and a timestamp (currently just indicating the click worked via Toast). Click detection on the icon is working but I am trying to implement selection of items in the ListView and that is where I am running into trouble.

I have reviewed numerous examples such as this and I think I am doing it as prescribed but I am obviously missing something, as I am receiving a runtime exception "java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ListView.setOnItemClickListener(android.widget.AdapterView$OnItemClickListener)' on a null object reference:"

Here is the layout for the view:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.foo.magma.ViewPassagesActivity">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/linearLayout">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Update"
android:onClick="update"
android:id="@+id/updateButton"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_weight="1"/>

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Delete"
android:onClick="delete"
android:id="@+id/deleteButton"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_weight="1"/>
</LinearLayout>

<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/passagesListView"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_below="@+id/linearLayout" />
</RelativeLayout>


Here is the layout for an item in the ListView. As mentioned, it an icon on the left with a timestamp:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >

<ImageView
android:id="@+id/icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="10dp"
android:layout_marginTop="4dp"
android:src="@drawable/curve" >
</ImageView>

<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Timestamp: "
android:textStyle="bold"
android:textSize="20dp"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/icon"
android:layout_toEndOf="@+id/icon">
android:layout_toRightOf="@+id/icon"
</TextView>

<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="layouttesttime"
android:textSize="20dp"
android:layout_below="@+id/date"
android:layout_alignRight="@+id/date"
android:layout_alignEnd="@+id/date"
android:layout_alignLeft="@+id/date"
android:layout_alignStart="@+id/date">
</TextView>

<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="layouttestdate"
android:textSize="20dp"
android:layout_alignTop="@+id/icon"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_toRightOf="@+id/label"
android:layout_toEndOf="@+id/label">
</TextView>
</RelativeLayout>


Here is he onCreate() from my activity, which sets my adapter:

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

ListView passagesListView = (ListView) findViewById(R.id.passagesListView);
assert passagesListView != null;

buildPassageList();

PassagesViewAdapter adapter = new PassagesViewAdapter(this, R.layout.passages_row_layout, passages);
passagesListView.setAdapter(adapter);


}


And here is where I am having the issue. The OnClickListener for the icon works. My intent is to detect selection of a ListView item so that it can be deleted. However, when I add the invocation of setOnItemClickListener(), I receive a runtime exception.

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;


public class PassagesViewAdapter extends ArrayAdapter<Passage>{

private int resource;
private List<Passage> passages;

public PassagesViewAdapter(Context context, int resource, List<Passage> passages) {
super(context, resource, passages);
this.resource = resource;
this.passages = passages;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

View view = convertView;

if (view == null) {
LayoutInflater li = LayoutInflater.from(getContext());
view = li.inflate(resource, null);
}

Passage passage = this.passages.get(position);

TextView time = (TextView) view.findViewById(R.id.time);
TextView date = (TextView) view.findViewById(R.id.date);
ImageView icon = (ImageView) view.findViewById(R.id.icon);
icon.setTag(new Integer(position));
final String positionStr = icon.getTag().toString();

// This works great!
icon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getContext(), "You clicked on " + positionStr, Toast.LENGTH_SHORT).show();
}
});

ListView passagesListView = (ListView) view.findViewById(R.id.passagesListView);
assert passagesListView != null;

// ** This call results in a runtime error for null pointer reference.
passagesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapter, View view, int position,long arg3) {
Toast.makeText(getContext(), "Foo", Toast.LENGTH_SHORT).show();
}
});

time.setText(passage.getPassageTime());
date.setText(passage.getPassageDate());

return view;
}

}

Answer

That because the View you have in your adapter represents the View you see in the list, not the layout of your ViewPassagesActivity. You do not have a ListView in your ListView items so it cannot be found and findViewById returns null. If you move the part where you set the OnItemClickListener to the onCreate of your ViewPassagesActivity, everything should be working fine

Remove this part from your adapter

// ** This call results in a runtime error for null pointer reference.
passagesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> adapter, View view, int position,long arg3) {
        Toast.makeText(getContext(), "Foo", Toast.LENGTH_SHORT).show();
    }
});

and add it to your Activity's onCreate like this

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

    ListView passagesListView = (ListView) findViewById(R.id.passagesListView);
    assert passagesListView != null;

    buildPassageList();

    PassagesViewAdapter adapter = new PassagesViewAdapter(this, R.layout.passages_row_layout, passages);
    passagesListView.setAdapter(adapter);

    // ** This call results in a runtime error for null pointer reference.
    passagesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapter, View view, int position,long arg3) {
            Toast.makeText(getContext(), "Foo", Toast.LENGTH_SHORT).show();
        }
    });
}