auser auser - 7 months ago 100
Java Question

RecyclerViewAdapter OnBindViewHolder has null IDs

I'm writing a Weather forecast app and I wanted to try to implement a RecyclerView so that I can have essentially a side-scrolling ListView. However, my onBindViewHolder() throws NPEs when I set the text due to view IDs being null, even though the ID is not null when the ViewHolder is created. I would appreciate any help in finding the discrepancy. Thank you!

My code is as follows:

MainActivity:

public class MainActivity extends AppCompatActivity {
EditText searchText;
Button searchButton;
Context context;
int count = 1; // default to one - potentially change later
RecyclerView recyclerView;


WeatherAdapter adapter;
ArrayList weatherList;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;

recyclerView = (RecyclerView)findViewById(R.id.recyclerViewID);
LinearLayoutManager manager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(manager);
adapter = new WeatherAdapter(context, weatherList);
recyclerView.setAdapter(adapter);

searchText = (EditText) findViewById(R.id.citySearchID);
searchButton = (Button) findViewById(R.id.searchButtonID);

searchButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) { startSearch(view); }
});
}

public void startSearch(View view) {
JSONWeatherTask task = new JSONWeatherTask();
String s = searchText.getText().toString();
if (s.isEmpty()){
Toast.makeText(this, R.string.searchError, Toast.LENGTH_SHORT).show();
return;
}
try{ task.execute(s.replaceAll("\\s+", ""));
} catch (Exception e) {
Toast.makeText(this, R.string.searchError, Toast.LENGTH_SHORT).show();
}
}
private class JSONWeatherTask extends AsyncTask<String, Void, ArrayList<Weather>> {
@Override
protected ArrayList<Weather> doInBackground(String... strings) {
weatherList = new ArrayList<>();
JSONObject jObj = ((new WeatherHttpClient()).getWeatherData(strings[0], count));
try { weatherList = JSONWeatherParser.getWeather(jObj, count);
} catch (JSONException e) { e.printStackTrace(); }
return weatherList;
}

@Override
protected void onPostExecute(ArrayList<Weather> weatherList) {
super.onPostExecute(weatherList);
LinearLayoutManager manager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(manager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
adapter = new WeatherAdapter(context, weatherList);
recyclerView.setAdapter(adapter);
}
}
}


Adapter:

public class WeatherAdapter extends RecyclerView.Adapter<ViewHolder> {
private final Context context;
private final ArrayList<Weather> weatherList;

public WeatherAdapter(Context context, ArrayList<Weather> weatherList){
this.context = context;
this.weatherList = weatherList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
LayoutInflater infl = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = infl.inflate(R.layout.weather_row, null);
ViewHolder holder = new ViewHolder(v);
return holder;
}

@Override
public void onBindViewHolder(ViewHolder holder, int pos){
Weather weather = weatherList.get(pos);
String[] Days = {"SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY"};
long unixSeconds = weather.currentCondition.getDate();
Date date = new Date(unixSeconds * 1000L);
SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd");
String formattedDate = dateFormat.format(date);
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(unixSeconds * 1000L);

holder.dateText.setText(formattedDate);
holder.dayText.setText(Days[cal.get(Calendar.DAY_OF_WEEK)-1]);

// set other TextViews in the same way
}


@Override
public int getItemCount() {
try{ return weatherList.size();
} catch (NullPointerException e) { return 0; }
}
}


ViewHolder:

public class ViewHolder extends RecyclerView.ViewHolder{
TextView dateText;
TextView dayText;
// Other TextViews in the same vein

public ViewHolder (View view){
super(view);
TextView dateText = (TextView)view.findViewById(R.id.dateTextID);
TextView dayText = (TextView)view.findViewById(R.id.dayTextID);
// Other TextViews in the same vein
}

}


CustomRow xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/dateTextID"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/dayTextID"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- Etc with more TextViews -->
</LinearLayout>


MainActivity xml:

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/citySearchID"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:inputType="text"
android:hint="@string/cityHint"/>
<Button
android:id="@+id/searchButtonID"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_weight="1"
android:text="@string/search"/>
</LinearLayout>

<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerViewID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>


Logcat:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
at com.ak.niceweather.WeatherAdapter.onBindViewHolder(WeatherAdapter.java:48)
at com.ak.niceweather.WeatherAdapter.onBindViewHolder(WeatherAdapter.java:17)
at android.support.v7.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:5217)
at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:5250)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4487)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4363)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1961)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1370)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1333)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:562)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2900)
at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3071)
at android.view.View.layout(View.java:15596)
at android.view.ViewGroup.layout(ViewGroup.java:4966)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
at android.view.View.layout(View.java:15596)
at android.view.ViewGroup.layout(ViewGroup.java:4966)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
at android.view.View.layout(View.java:15596)
at android.view.ViewGroup.layout(ViewGroup.java:4966)
at android.support.v7.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:435)
at android.view.View.layout(View.java:15596)
at android.view.ViewGroup.layout(ViewGroup.java:4966)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
at android.view.View.layout(View.java:15596)
at android.view.ViewGroup.layout(ViewGroup.java:4966)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
at android.view.View.layout(View.java:15596)
at android.view.ViewGroup.layout(ViewGroup.java:4966)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
at android.view.View.layout(View.java:15596)
at android.view.ViewGroup.layout(ViewGroup.java:4966)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2072)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1829)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1054)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5779)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
at android.view.Choreographer.doCallbacks(Choreographer.java:580)
at android.view.Choreographer.doFrame(Choreographer.java:550)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

Answer

Your problem lies in your ViewHolder.java class. In the constructor you are defining and initializing new variables and not your private ones. So replace

TextView dateText = (TextView)view.findViewById(R.id.dateTextID);
TextView dayText = (TextView)view.findViewById(R.id.dayTextID);

With

this.dateText = (TextView)view.findViewById(R.id.dateTextID);
this.dayText = (TextView)view.findViewById(R.id.dayTextID);

Or simply with:

dateText = (TextView)view.findViewById(R.id.dateTextID);
dayText = (TextView)view.findViewById(R.id.dayTextID);