Search code examples
javaandroidandroid-listviewparameterized-types

Android's List View. Parameterized type in AdapterView.OnItemClickListener


I have a ListView with a hypothetical SquirrelAdapter. Something like this:

public class SquirrelAdapter extends ArrayAdapter<Squirrel> {

    private LayoutInflater layoutInflater;
    private Context context;
    private int resource;

    public SquirrelAdapter (Context context, int resource, List<Squirrel> squirrels) {
        super(context, resource, squirrels);
        layoutInflater = LayoutInflater.from(context);
        this.context = context;
        this.resource = resource;
    }

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

        Squirrel squirrel = getItem( position );

        convertView = layoutInflater.inflate(resource, null);
        TextView squirrelNicknameTextView = (TextView) convertView.findViewById(R.id.squirrelNickname);
        squirrelNicknameTextView.setText( squirrel.getCuteNickname() );

        return convertView;
    }
}

So in an activity I can do this:

Squirrel[] squirrels = // Lot of Squirrels

SquirrelAdapter squirrelAdapter = new SquirrelAdapter(
    getApplicationContext(),
    R.layout.squirrel_layout,
    Arrays.asList( squirrels )
);
squirrelListView.setAdapter( squirrelAdapter );

And so I can append an onItemClickListener:

squirrelListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

        // Bad people cast a squirrel :|
        Squirrel squirrel = (Squirrel) parent.getItemAtPosition(position);

        Log.i("WWF", "Tapped squirrel " + squirrel.getCuteNickname());
    }
});

It works really well. But now the really boring question:

How can I use Java's parameterized type in AdapterView<T extends Adapter>.setOnItemClickListener? I'm thinking something like below, but it gives errors:

squirrelListView.setOnItemClickListener(new AdapterView<SquirrelAdapter>.OnItemClickListener() {

    @Override
    public void onItemClick(AdapterView<SquirrelAdapter> parent, View view, int position, long id) {

        // Squirrels approved!
        Squirrel squirrel = parent.getItemAtPosition(position);

        Log.i("WWF", "Tapped squirrel " + squirrel.getCuteNickname());
    }
});

Why can't I have syntax such as "AdapterView<ArrayAdapter<T>>.OnItemClickListener"?

Why doesn't something like this exist: <T> getItemPosition(int position)? Can it be implemented?

Thanks helping my squirrels not to be casted.

(More seriously I want to learn more about this Java feature...)


Solution

  • As you might have seen if you try new AdapterView<SquirrelAdapter>.OnItemClickListener() compiler would say,

    The member type AdapterView<SquirrelAdapter>.OnItemClickListener cannot be qualified with a parameterized type, since it is static.

    What you are doing inside listView.setOnItemClickListener is to create an Anonymous class, for an interface OnItemClickListener inside abstract class AdapterView. Now, to get reference of interface inside an abstract class, you need to access only statically, such as AdapterView.OnItemClickListener, and hence the compiler error as above.

    Even if you try to create a concrete class that implements OnItemClickListener you have to access statically only, such as class MyImpl implements MyAbstract.MyInterface

    If you have noticed signature of AdapterView class

    abstract class AdapterView<T extends Adapter>
    

    that indicates, AdapterView can have Adapter or any subtype of it as its parameter. But to get hold of elements(data) Adapter is connected to, AdapterView uses

    Object getItem(int position);
    

    As you could see inside:

    public Object getItemAtPosition(int position) {
        T adapter = getAdapter();
        return (adapter == null || position < 0) ? null : adapter.getItem(position);
    }
    

    Casting of your Squirrel inside onItemClick is needed. See if it helps!