I have a ListView and a BaseAdapter. In the Adapter I show 7 different types of views.
This is image is showing when I run the project all the ListView and the view inside are loading correctly. But when I scroll the list view down I get an error.
All shows well when I run the project, but the problem goes when I scroll down the ListView, and throw me the error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.panelapps.mapen, PID: 25969
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
at com.panelapps.uiformcreator.UIFormAdapter.getView(UIFormAdapter.java:234)
at android.widget.AbsListView.obtainView(AbsListView.java:2413)
at android.widget.ListView.makeAndAddView(ListView.java:1975)
at android.widget.ListView.fillDown(ListView.java:709)
at android.widget.ListView.fillGap(ListView.java:673)
at android.widget.AbsListView.trackMotionScroll(AbsListView.java:5475)
at android.widget.ListView.trackMotionScroll(ListView.java:1894)
at android.widget.AbsListView.scrollIfNeeded(AbsListView.java:3708)
at android.widget.AbsListView.onTouchMove(AbsListView.java:4157)
at android.widget.AbsListView.onTouchEvent(AbsListView.java:3964)
at android.view.View.dispatchTouchEvent(View.java:9957)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2705)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2386)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2400)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2400)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2400)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2400)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2400)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2711)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2400)
at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:416)
at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1834)
at android.app.Activity.dispatchTouchEvent(Activity.java:3154)
at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:68)
at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:378)
at android.view.View.dispatchPointerEvent(View.java:10177)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4650)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4518)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3969)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4022)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3988)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3996)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3969)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4022)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3988)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4117)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3996)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4174)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3969)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4022)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3988)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3996)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3969)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6459)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6433)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6394)
E/AndroidRuntime: at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6593)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:176)
at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:6541)
at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:6616)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:871)
at android.view.Choreographer.doCallbacks(Choreographer.java:683)
at android.view.Choreographer.doFrame(Choreographer.java:613)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:857)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6316)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:872)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:762)
My BaseAdapter code is:
public class UIFormAdapter extends BaseAdapter {
CallbackFormAdapter callback;
public interface CallbackFormAdapter {
void getData();
}
Context context;
ArrayList<Form> UIForm = new ArrayList<>();
private LayoutInflater inflater;
//functino
Function function = new Function();
public UIFormAdapter(Context context, ArrayList<Section> form, CallbackFormAdapter callback) {
this.context = context;
this.callback = callback;
//do the form
for (int i = 0; i<form.size(); i++){
//add the separator
UIForm.add(new Form("", form.get(i).getText(), 0));
//go over the forms inside
for (int j = 0; j<form.get(i).getForms().size(); j++){
UIForm.add(form.get(i).getForms().get(j));
}
}
}
@Override
public int getCount() {
return UIForm.size();
}
@Override
public Object getItem(int position) {
return UIForm.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemViewType(int position) {
return UIForm.get(position).getType();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//holder
Holder viewHolder = null;
//get the data
Form data = UIForm.get(position);
//if inflater inicialize
if (inflater == null)
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null){
//get the tipe
int type = getItemViewType(position);
//switch type
switch (type){
case 0:
convertView = inflater.inflate(R.layout.separator, parent, false);
//set the holder
viewHolder = new Holder(convertView, type);
viewHolder.txtSection.setText(data.getText());
break;
case Types.EditText:
convertView = inflater.inflate(R.layout.text_edit, parent, false);
//set the holder
viewHolder = new Holder(convertView, type);
viewHolder.etEditText.setHint(data.getText());
break;
case Types.Single_Spinner:
convertView = inflater.inflate(R.layout.single_spinner, parent, false);
//set the holder
viewHolder = new Holder(convertView, type);
viewHolder.txtSingleSpinner.setText(data.getText());
break;
case Types.Spinner:
convertView = inflater.inflate(R.layout.spinner, parent, false);
//set the holder
viewHolder = new Holder(convertView, type);
viewHolder.spinner.setAdapter(data.getAdapter());
viewHolder.spinner.setBackgroundColor(Color.LTGRAY);
break;
case Types.Date:
convertView = inflater.inflate(R.layout.button, parent, false);
//set the holder
viewHolder = new Holder(convertView, type);
viewHolder.button.setText(data.getText());
viewHolder.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Calendar mcurrentDate = Calendar.getInstance();
final int[] mYear = {mcurrentDate.get(Calendar.YEAR)};
final int[] mMonth = {mcurrentDate.get(Calendar.MONTH)};
final int[] mDay = {mcurrentDate.get(Calendar.DAY_OF_MONTH)};
DatePickerDialog mDatePicker = new DatePickerDialog(context, new DatePickerDialog.OnDateSetListener() {
public void onDateSet(DatePicker datepicker, int selectedyear, int selectedmonth, int selectedday) {
Calendar myCalendar = Calendar.getInstance();
myCalendar.set(Calendar.YEAR, selectedyear);
myCalendar.set(Calendar.MONTH, selectedmonth);
myCalendar.set(Calendar.DAY_OF_MONTH, selectedday);
//date
String myDate = function.dateToString(myCalendar.getTime(), "dd/MM/yyyy");
//viewHolder.button.setText(myDate);
mDay[0] = selectedday;
mMonth[0] = selectedmonth;
mYear[0] = selectedyear;
}
}, mYear[0], mMonth[0], mDay[0]);
//mDatePicker.setTitle("Select date");
mDatePicker.show();
}
});
break;
case Types.Button:
convertView = inflater.inflate(R.layout.button, parent, false);
//set the holder
viewHolder = new Holder(convertView, type);
viewHolder.button.setText(data.getText());
viewHolder.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
callback.getData();
}
});
break;
}
//set the holder
convertView.setTag(viewHolder);
}else{ //else customview null
//get the viewHolder
viewHolder = (Holder)convertView.getTag();
//get the tipe
int type = UIForm.get(position).getType();
switch (type){
case 0:
viewHolder.txtSection.setText(data.getText());
break;
case Types.EditText:
viewHolder.etEditText.setHint(data.getText());
break;
case Types.Single_Spinner:
viewHolder.txtSingleSpinner.setText(data.getText());
break;
case Types.Spinner:
viewHolder.spinner.setAdapter(data.getAdapter());
viewHolder.spinner.setBackgroundColor(Color.LTGRAY);
break;
case Types.Date:
viewHolder.button.setText(data.getText());
viewHolder.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Calendar mcurrentDate = Calendar.getInstance();
final int[] mYear = {mcurrentDate.get(Calendar.YEAR)};
final int[] mMonth = {mcurrentDate.get(Calendar.MONTH)};
final int[] mDay = {mcurrentDate.get(Calendar.DAY_OF_MONTH)};
DatePickerDialog mDatePicker = new DatePickerDialog(context, new DatePickerDialog.OnDateSetListener() {
public void onDateSet(DatePicker datepicker, int selectedyear, int selectedmonth, int selectedday) {
Calendar myCalendar = Calendar.getInstance();
myCalendar.set(Calendar.YEAR, selectedyear);
myCalendar.set(Calendar.MONTH, selectedmonth);
myCalendar.set(Calendar.DAY_OF_MONTH, selectedday);
//date
String myDate = function.dateToString(myCalendar.getTime(), "dd/MM/yyyy");
//viewHolder.button.setText(myDate);
mDay[0] = selectedday;
mMonth[0] = selectedmonth;
mYear[0] = selectedyear;
}
}, mYear[0], mMonth[0], mDay[0]);
//mDatePicker.setTitle("Select date");
mDatePicker.show();
}
});
break;
case Types.Button:
viewHolder.button.setText(data.getText());
viewHolder.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
callback.getData();
}
});
break;
}
}
//return the view
return convertView;
}
//CREATE THE HOLDERS
private class Holder{
EditText etEditText;
TextView txtSection;
MaterialSpinner spinner;
TextView txtSingleSpinner;
Button button;
public Holder(View v, int type) {
switch (type){
case Types.EditText:
this.etEditText = v.findViewById(R.id.etEditText);
break;
case Types.Single_Spinner:
this.txtSingleSpinner = v.findViewById(R.id.txtSingleSpinner);
break;
case 0:
this.txtSection = v.findViewById(R.id.txtSeparator);
break;
case Types.Spinner:
this.spinner = v.findViewById(R.id.spinner);
break;
case Types.Button:
this.button = v.findViewById(R.id.button);
break;
case Types.Date:
this.button = v.findViewById(R.id.button);
break;
}
}
}
}
The error is showing me is in the next line (viewHolder.button.setText ...):
case Types.Button:
viewHolder.button.setText(data.getText());
And this button is the next one to show in the ListView.
You need to override getViewTypeCount
to indicate the number of different types of views, otherwise when it tries to reuse a view it may be of a different type (so convertView
isn't null, but the ViewHolder
is of the wrong type). It worked for the first screen of views since convertView
would be null for all of them.
@Override
public int getViewTypeCount() {
return 6; // question says 7 view types but I counted 6
}
See here for some more details.