gridviewtypedarray

Custom ImageAdapter for GridView NullPointerException in getView(...)


I have a class called AddZoneDialog which extends DialogFragment. In this class I'm trying to populate a GridView with images I have stored in an xml file called arrays.xml.

I've used TypedArray to retrieved the entries in the xml file. I pass that array to the custom ImageAdapter (called ZoneIconAdapter) to fill the GridView. The problem is, I keep getting NullPointerExceptions on the line

holder.iv.setImageResource(imgs.getResourceId(position, -1));

To test the usage of the TypedArray using getResourceId(...), I placed an ImageView in the DialogFragment itself outside of the GridView and populated it with an image manually using:

(ImageView) iv = (ImageView) view.findViewById(R.id.ivIcon);

I then used the same syntax I used in the ZoneIconAdapter to add an image to it:

iv.setImageResource(imgs.getResourceId(0, -1));

and the correct image displayed. The image adapter is in a package called com.my.project.model and so I did import com.my.project.R In the adapter, it still gives the Null error if I try to hard code the resource:

holder.iv.setImageResource(R.drawable.bed1);

My code is as follows: Class - ZoneIconAdapter:

public class ZoneIconAdapter extends BaseAdapter {
private static final String LOGTAG = "HOMECONTROL";

Context context;
TypedArray imgs;

public ZoneIconAdapter(Context c, TypedArray a){
    this.context = c;
    imgs = a;
}

@Override
public int getCount() {
    //return imgIds.length;
    return imgs.length();
}

@Override
public Object getItem(int index) {
    return imgs.getResourceId(index, -1);
}

@Override
public long getItemId(int index) {
    return index;
}

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

    if(convertView == null){

        LayoutInflater inflater = ((Activity) context).getLayoutInflater();
        convertView = inflater.inflate(R.layout.zone_icon_list, parent, false);

        holder = new ViewHolder();
        holder.iv = (ImageView) convertView.findViewById(R.id.ivIcon);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();

    }

    Log.w(LOGTAG, "(ZoneIconAdapter) pos: "+ position + " drawable : " +          imgs.getDrawable(position) + " res: " + 
            imgs.getResourceId(position, -1));
    holder.iv.setImageResource(imgs.getResourceId(position, -1));  // ***NULL ERROR ***

    return convertView;
}

private class ViewHolder {
    ImageView iv;
}

}

Class - AddZoneDialog.java

 public class AddZoneDialog extends DialogFragment {
private static final String LOGTAG = "HOMECONTROL";

public static final String EXTRA_ZONE_NAME = "homecontrol.zonename";
public static final String EXTRA_NAME_LIST = "homecontrol.namelist";
public static final String EXTRA_IMG_LIST = "homecontrol.imglist";

private ZoneList zoneList;  
private EditText etZoneName;
private GridView gvIcons;
TypedArray ids;


public AddZoneDialog newInstance(ZoneList zones){
    Bundle args = new Bundle();
    args.putSerializable(EXTRA_NAME_LIST, zones);

    AddZoneDialog newDialog = new AddZoneDialog();
    newDialog.setArguments(args);

    return newDialog;
}

@Override
public Dialog onCreateDialog(Bundle savedState) {
    View v = getActivity().getLayoutInflater()
            .inflate(R.layout.frag_add_zone_dialog, null);

    zoneList =  (ZoneList) getArguments().getSerializable(EXTRA_NAME_LIST);
    gvIcons = (GridView) v.findViewById(R.id.gvZonesIcons);
    etZoneName = (EditText) v.findViewById(R.id.etZonesPopupName);
    etZoneName.requestFocus();

    ImageView iv = (ImageView) v.findViewById(R.id.ivIcon);

    ZoneIconAdapter iconAdapter = new ZoneIconAdapter(getActivity(), ids);
    gvIcons.setAdapter(iconAdapter);

    ids = getActivity().getResources().obtainTypedArray(R.array.zone_icons);
    iconAdapter.notifyDataSetChanged();
    Log.w(LOGTAG, "SIZE OF IDS: " + ids.length());

    iv.setImageResource(ids.getResourceId(0, -1));  // *****THIS WORKS*****

    return new AlertDialog.Builder(getActivity())
        .setView(v)
        .setTitle(R.string.zones_add_dialog_title)
        .setPositiveButton(R.string.ok, new AlertDialog.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                sendResult(Activity.RESULT_OK);
            }
        })
        .setNegativeButton(R.string.cancel, null)
        .create();
}



@Override
public void onActivityCreated(Bundle bundle) {
    getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);

    super.onActivityCreated(bundle);
}

public void sendResult(int resultCode){
    String newName = etZoneName.getText().toString();
    if ((newName.length() < 1) || (newName == null) || (newName == "")){
        Toast.makeText(getActivity(), "You must enter a name.", Toast.LENGTH_SHORT).show();
        return;
    }
    else{
        if (nameOnList(newName)){
            Toast.makeText(getActivity(), "That name is already in use.  Please enter another name.",
                    Toast.LENGTH_LONG).show();
            return;
        }
    }

    if(getTargetFragment() == null)
        return;

    Intent i = new Intent();
    i.putExtra(EXTRA_ZONE_NAME, newName);

    getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, i);
}

boolean nameOnList(String name){
    for (int i = 0; i < zoneList.size(); i++){
        if (zoneList.get(i).getName().equals(name))
            return true;
    }
    return false;
}

@Override
public void onStop() {
    ids.recycle();
    super.onStop();
}
}

XML - zone_icon_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<!-- <com.example.homecontrol.views.DynamicImageView  -->
<ImageView
    android:name="@+id/ivIcon"
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:scaleType="centerCrop" />

</LinearLayout>

XML - frag_add_zone_dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp" >

<TextView 
    android:id="@+id/tvZonesEnterName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/zones_enter_name"/>
<EditText
    android:id="@+id/etZonesPopupName"
    android:layout_width="fill_parent"
    android:layout_height="match_parent"
    android:inputType="text"
    android:hint="@string/zones_name_hint"
    android:singleLine="true" />
<TextView 
    android:id="@+id/tvZonesSelectIcon"
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:text="@string/zones_select_icon" />
 <ImageView
    android:id="@+id/ivIcon"
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:scaleType="centerCrop" />

<GridView
    android:id="@+id/gvZonesIcons"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:numColumns="auto_fit"
    android:stretchMode="columnWidth"
    android:gravity="center"
    android:layout_gravity="center_horizontal" />

</LinearLayout>

One last thing: to test the GridView and ZoneIconAdapter, I replaced the ImageView in zone_icon_list.xml with a TextView and in the Adapter class I changed the ViewHolder to have a TextView instead of the ImageView and used:

...
holder.tv = (TextView) convertView.findViewById(R.id.tvTest);
...
holder.tv.setText("image " + position);

to see if the GridView would populate with just a TextView, and it worked just fine. I've been working on this all day and have gone through about all of the stackoverflow entries I could find and I am burnt out. I feel it has to be something stupid I'm overlooking, but just not sure.


Solution

  • Well, this one is a bit embarrassing. In the custom grid adapter view, I put the code:

    <ImageView
        android:name="@+id/..."
    

    instead of

    <ImageView
        android:id="@+id/..."
    

    So I was getting a null value for the view with the ID I was requesting.