Previously, in project targeted KitKat, I have a fairly simple ArrayAdapter
being used in Spinner
without any trouble.
CountryArrayAdapter.java
public class CountryArrayAdapter extends ArrayAdapter<Country> {
private static class ViewHolder0 {
public TextView textView0;
}
private static class ViewHolder1 {
public CheckedTextView checkedTextView0;
}
private static List<Country> getValidCountries() {
List<Country> countries = new ArrayList<Country>(Arrays.asList(Country.values()));
return Collections.unmodifiableList(countries);
}
public CountryArrayAdapter(Context context) {
super(context, R.layout.country_spinner_item, validCountries);
this.setDropDownViewResource(R.layout.country_spinner_dropdown_item);
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
View rowView = convertView;
if (rowView == null) {
LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowView = inflater.inflate(R.layout.country_spinner_dropdown_item, null);
ViewHolder1 viewHolder1 = new ViewHolder1();
viewHolder1.checkedTextView0 = (CheckedTextView)rowView.findViewById(R.id.checked_text_view_0);
rowView.setTag(viewHolder1);
}
final ViewHolder1 holder = (ViewHolder1)rowView.getTag();
final CheckedTextView checkedTextView0 = holder.checkedTextView0;
Country country = validCountries.get(position);
checkedTextView0.setText(country.humanString);
checkedTextView0.setCompoundDrawablesWithIntrinsicBounds(country.icon, 0, 0, 0);
return rowView;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView = convertView;
if (rowView == null) {
LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowView = inflater.inflate(R.layout.country_spinner_item, null);
ViewHolder0 viewHolder0 = new ViewHolder0();
viewHolder0.textView0 = (TextView)rowView.findViewById(R.id.text_view_0);
rowView.setTag(viewHolder0);
}
final ViewHolder0 holder = (ViewHolder0)rowView.getTag();
final TextView textView0 = holder.textView0;
Country country = validCountries.get(position);
textView0.setText(country.humanString);
textView0.setCompoundDrawablesWithIntrinsicBounds(country.icon, 0, 0, 0);
return rowView;
}
private static final List<Country> validCountries = getValidCountries();
}
After migrating to project targeted Marshmallow, we would get the following when clicking on the spinner.
java.lang.NullPointerException
at android.widget.TextView.checkForRelayout(TextView.java:6556)
at android.widget.TextView.setText(TextView.java:3813)
at android.widget.TextView.setText(TextView.java:3671)
at android.widget.TextView.setText(TextView.java:3646)
at com.example.yccheok.bug.CountryArrayAdapter.getDropDownView(CountryArrayAdapter.java:55)
NPE exception is being thrown at
checkedTextView0.setText(country.humanString);
This is how the ArrayAdapter
being used.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Spinner countrySpinner = (Spinner)this.findViewById(R.id.country_spinner);
final CountryArrayAdapter countryArrayAdapter = new CountryArrayAdapter(this);
countrySpinner.setAdapter(countryArrayAdapter);
}
}
The 2 XML files, which is being used in getDropDownView
and getView
are fairly simple. I don't see how they can go wrong.
country_spinner_dropdown_item.xml
<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/checked_text_view_0"
style="?android:attr/spinnerDropDownItemStyle"
android:singleLine="true"
android:layout_width="match_parent"
android:layout_height="48dp"
android:minHeight="48dp"
android:drawablePadding="10dp" />
country_spinner_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text_view_0"
style="?android:attr/spinnerItemStyle"
android:singleLine="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="10dp"
android:gravity="center_vertical" />
A minimal project to demonstrate the problem can be downloaded from : https://www.dropbox.com/s/k5jvodoyh6ux0ka/bug.zip?dl=0
Any idea what's goes wrong on my ArrayAdapter
?
A "hack" solution
I can easily prevent crash, by modify the code in getDropDownView
,
from
viewHolder1.checkedTextView0 = (CheckedTextView)rowView.findViewById(R.id.checked_text_view_0);
rowView.setTag(viewHolder1);
to
viewHolder1.checkedTextView0 = (CheckedTextView)rowView.findViewById(R.id.checked_text_view_0);
viewHolder1.checkedTextView0.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.WRAP_CONTENT, AbsListView.LayoutParams.WRAP_CONTENT));
rowView.setTag(viewHolder1);
I get this idea from NullPointerException at TextView.checkForRelayout() while setText()
But it still doesn't explain why it doesn't happen on KitKat target. Also, the proposed solution looks like a "hack" to me.