0

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

}
Community
  • 1
  • 1
sje
  • 117
  • 1
  • 9

2 Answers2

1

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();
        }
    });
}
0xDEADC0DE
  • 2,453
  • 1
  • 17
  • 22
0

Try extending your Adapter from BaseAdapter.

This

public class PassagesViewAdapter extends ArrayAdapter<Passage>{

to this

public class PassagesViewAdapter extends BaseAdapter {

Also your setOnItemClickListener needs to be in the onCreate function in your activity, not in the adapter.

James
  • 3,580
  • 3
  • 25
  • 36