onBindViewHolder not called after RecyclerView Adapter notifyDataSetChanged() - android

I'm getting some data from Firebase database and I'm trying to populate RecyclerView adapter with it. After Adapter's notifyDataSetChanged() is called, screen blinks and nothing happens, I couldn't even catch a breakpoint inside onBindViewHolder.
Here is my code:
POJO class:
public class Result2 implements Serializable {
private int score;
private String userName;
public Result2(int score, String userName) {
this.score = score;
this.userName = userName;
}
public int getScore() {
return score;
}
public String getUserName() {
return userName;
}
}
This is my activitys layout called activity_results.xml that contains RecyclerView
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:weightSum="2">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="USERNAME"
android:textColor="#000"
android:textSize="16sp"/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="SCORE"
android:textColor="#000"
android:textSize="16sp"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000"
/>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingTop="10dp"
android:scrollbars="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
Here is my Adapters ViewHolder layout called score_view_holder.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:weightSum="2">
<TextView
android:id="#+id/username"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:textSize="14sp"/>
<TextView
android:id="#+id/score"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:textSize="14sp"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:background="#4545"/>
So it will contain two horizontal TextViews and a View as a line below..
Here is my Adapter:
public class ScoreAdapter extends RecyclerView.Adapter<ScoreAdapter.ScoreViewHolder> {
private List<Result2> results = new ArrayList<>();
public void setResults(List<Result2> results) {
this.results.clear();
this.results.addAll(results);
notifyDataSetChanged();
}
#Override
public ScoreAdapter.ScoreViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.score_view_holder, parent, false);
return new ScoreAdapter.ScoreViewHolder(view);
}
#Override
public void onBindViewHolder(ScoreAdapter.ScoreViewHolder holder, int position) {
Result2 result = getResult(position);
if (result != null) {
holder.setUsername(result.getUserName() != null ? result.getUserName() : "-");
holder.setScore(String.valueOf(result.getScore()));
}
}
private Result2 getResult(int position) {
return !results.isEmpty() ? results.get(position) : null;
}
#Override
public int getItemCount() {
return results.size();
}
public class ScoreViewHolder extends RecyclerView.ViewHolder {
private TextView username;
private TextView score;
public ScoreViewHolder(View itemView) {
super(itemView);
username = itemView.findViewById(R.id.username);
score = itemView.findViewById(R.id.score);
}
public void setUsername(String username) {
this.username.setText(username);
}
public void setScore(String score) {
this.score.setText(score);
}
}
}
It should get List of Result2 objects and just set text in those two TextViews (username and score)
And finally my Activity where I'm trying to notify adapter from:
public class Results extends AppCompatActivity {
private DatabaseReference mDatabase;
private ScoreAdapter scoreAdapter;
private RecyclerView recyclerView;
private List<Result2> results = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_results);
recyclerView = findViewById(R.id.recycler_view);
scoreAdapter = new ScoreAdapter();
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(scoreAdapter);
mDatabase = FirebaseDatabase.getInstance().getReference();
loadResults();
}
private void loadResults() {
mDatabase.child("Users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot childSnapshot : dataSnapshot.getChildren()) {
Result2 result = childSnapshot.getValue(Result2.class);
if (result != null) {
results.add(result);
}
}
Toast.makeText(Results.this, String.valueOf(results.size()), Toast.LENGTH_SHORT).show();
scoreAdapter.setResults(results);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
}
);
}
}
So after for loop is done, Toast shows correct number of results, adapter setResults method is called, there i receive correct number of results, here is a picture:
But after notifyDataSetChanged() is called, screen just blinks and everything is blank... methods inside onBindViewHolder can not be reached after putting breakpoints on them.. Any idea what's wrong here?

There are several problems in your code. The first one is that you are calling the setResults() method and pass your results list inside your ScoreAdapter class and not to the constrcutor. In order to solve this, change your setResults() method to become a constructor like this:
public ScoreAdapter(List<Result2> results) {
this.results.clear();
this.results.addAll(results);
notifyDataSetChanged();
}
The adapter must also be instantiated and set to your RecyclerView inside the callback. So to solve this, simply move the following lines of code:
ScoreAdapter scoreAdapter = new ScoreAdapter(result);
recyclerView.setAdapter(scoreAdapter);
Right after the following line:
Toast.makeText(Results.this, String.valueOf(results.size()), Toast.LENGTH_SHORT).show();
Don't also forget to comment this line of code: scoreAdapter.setResults(results);.
See now, to instantiate the adapter you need to pass the result list to the constructor.
Also please also don't forget to add the public no-argument constructor to your Result2 class. Please see here more details.

Related

RecyclerView does not show list of objects

I tried the solutions on the other similar posts that I found here but they did not help me. I hope someone can help.
I get data from Unsplash's API and the ArrayList contains 10 items once I get a response, that's why I think It has to be something with the way the view is inflated.
This is code:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "unsplash";
private final String CLIENT_ID = "...myclientID...";
// testing keyword used on the API to search for pictures
private final String KEYWORD = "stackOverflow";
private final String urlBase = "https://api.unsplash.com/";
private Retrofit retrofit;
private RecyclerView recyclerView;
private RecyclerViewAdapter recyclerViewAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
retrofit = new Retrofit.Builder()
.baseUrl(urlBase)
.addConverterFactory(GsonConverterFactory.create())
.build();
recyclerView = findViewById(R.id.recyclerview);
recyclerView.setHasFixedSize(true);
GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(layoutManager);
recyclerViewAdapter = new RecyclerViewAdapter();
recyclerView.setAdapter(recyclerViewAdapter);
getData();
}
private void getData() {
PictureService service = retrofit.create(PictureService.class);
Call<PictureResponse> pictureResponseCall = service.getPictureList(KEYWORD, CLIENT_ID);
pictureResponseCall.enqueue(new Callback<PictureResponse>() {
#Override
public void onResponse(Call<PictureResponse> call, Response<PictureResponse> response) {
if (response.isSuccessful()){
PictureResponse pictureResponse = response.body();
ArrayList<UnsplashPic> pictureList = pictureResponse.getResults();
recyclerViewAdapter.addPictures(pictureList);
}else{
Log.e (TAG, " onResponse " + response.errorBody());
}
}
#Override
public void onFailure(Call<PictureResponse> call, Throwable t) {
Log.e (TAG, " onFailure " + t.getMessage());
}
});
}
}
My adapter class:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private ArrayList<UnsplashPic> pictureList;
public RecyclerViewAdapter (){
pictureList = new ArrayList<>();
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_view_holder, viewGroup, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int i) {
UnsplashPic pic = pictureList.get(i);
viewHolder.artistName.setText(pic.user.getUsername());
viewHolder.unsplash.setText("Unsplash");
}
#Override
public int getItemCount() {
return pictureList.size();
}
public void addPictures(ArrayList<UnsplashPic> pictureList) {
pictureList.addAll(pictureList);
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private TextView artistName;
private TextView unsplash;
public ViewHolder (View itemView){
super(itemView);
imageView = itemView.findViewById(R.id.imageview);
artistName = itemView.findViewById(R.id.artist_name);
unsplash = itemView.findViewById(R.id.unsplash_name);
}
}
}
item_view_holder.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="wrap_content"
android:orientation="vertical">
<ImageView
android:id="#+id/imageview"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:background="#ababab"
android:contentDescription="unsplash picture" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Photo by "/>
<TextView
android:id="#+id/artist_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="on "/>
<TextView
android:id="#+id/unsplash_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true" />
</LinearLayout>
activity_main.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Search for pictures"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Search"/>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/recyclerview">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
Here:
public void addPictures(ArrayList<UnsplashPic> pictureList) {
pictureList.addAll(pictureList);
notifyDataSetChanged();
}
you are actually adding the content of the pictureList you send as parameter of the function to that same list, your list from adapter is not updated.
You should do like this instead:
public void addPictures(ArrayList<UnsplashPic> pictureList) {
this.pictureList.addAll(pictureList);
notifyDataSetChanged();
}

Unable to populate RecyclerView adapter with Firebase data

I'm trying to fetch the data from my Firebase database but from some reason it doesn't work, so I'll try to provide as much info as posible ..
This is the database snapshot:
This is a POJO class:
public class Result2 implements Serializable {
private int score;
private String userName;
public Result2(int score, String userName) {
this.score = score;
this.userName = userName;
}
public int getScore() {
return score;
}
public String getUserName() {
return userName;
}
}
This is my activitys layout called activity_results.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:weightSum="2"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="50dp">
<TextView
android:textColor="#000"
android:textSize="16sp"
android:gravity="center"
android:text="USERNAME"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<TextView
android:textColor="#000"
android:textSize="16sp"
android:text="SCORE"
android:gravity="center"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
<View
android:background="#000"
android:layout_width="match_parent"
android:layout_height="1dp"
/>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="10dp"
android:clipToPadding="false"
android:scrollbars="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
As you can see, I want to have 2 TextViews that will act like tabs and a RecyclerView underneath that will show the data
Here is my Adapters ViewHolder layout called score_view_holder.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:weightSum="2">
<TextView
android:id="#+id/username"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:textSize="14sp" />
<TextView
android:id="#+id/score"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:textSize="14sp" />
</LinearLayout>
<View
android:background="#4545"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"/>
So it will contain two horizontal TextViews and a View as a line below..
Here is my Adapter:
public class ScoreAdapter extends RecyclerView.Adapter<ScoreAdapter.ScoreViewHolder> {
private List<Result2> results = new ArrayList<>();
public void setResults(List<Result2> results) {
this.results.clear();
this.results.addAll(results);
notifyDataSetChanged();
}
#Override
public ScoreAdapter.ScoreViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.score_view_holder, parent, false);
return new ScoreAdapter.ScoreViewHolder(view);
}
#Override
public void onBindViewHolder(ScoreAdapter.ScoreViewHolder holder, int position) {
Result2 result = getResult(position);
if (result != null) {
holder.setUsername(result.getUserName() != null ? result.getUserName() : "-");
holder.setScore(String.valueOf(result.getScore()));
}
}
private Result2 getResult(int position) {
return !results.isEmpty() ? results.get(position) : null;
}
#Override
public int getItemCount() {
return results.size();
}
public class ScoreViewHolder extends RecyclerView.ViewHolder {
private TextView username;
private TextView score;
public ScoreViewHolder(View itemView) {
super(itemView);
username = itemView.findViewById(R.id.username);
score = itemView.findViewById(R.id.score);
}
public void setUsername(String username) {
this.username.setText(username);
}
public void setScore(String score) {
this.score.setText(score);
}
}
}
It should get List of Result2 objects and just set text in those two TextViews (username and score)
And finally my Activity where all the magic doesn't happen :)
public class Results extends AppCompatActivity {
private DatabaseReference mDatabase;
private ScoreAdapter scoreAdapter;
private RecyclerView recyclerView;
private List<Result2> results = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_results);
recyclerView = findViewById(R.id.recycler_view);
scoreAdapter = new ScoreAdapter();
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(scoreAdapter);
mDatabase = FirebaseDatabase.getInstance().getReference();
loadResults();
}
private void loadResults() {
mDatabase.child("Users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot childSnapshot : dataSnapshot.getChildren()) {
Result2 result = childSnapshot.getValue(Result2.class);
if(result != null) {
results.add(result);
}
}
Toast.makeText(Results.this, String.valueOf(results.size()), Toast.LENGTH_SHORT).show();
scoreAdapter.setResults(results);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
}
);
}
}
Funniest thing, Toast shows correct number of Results, but when scoreAdapter.setResults(results); is called, screen blinks and goes back to first screen... Does it have something to do with a POJO class, or onDataChange method or main thread?
I tried with debugging and also funny thing here.. This is what is caught on breakpoint inside ScoreAdapter setResult method:
I tried setting breakpoints inside onBindViewHolder and getResults() but none of them gets called :o
if(result != null) {
results.add(result);
}
See here you are checking result != null. But result is a reference variable of type Results. So it will not be null, it will contains some value.
So, try removing that if statement and do only -
results.add(result);

Data from JSON does not appear on the RecyclerView layout

I have created RecyclerView and showing data from JSON.
Issue I'm facing is, while Toast data is showing correctly, but in RecyclerView same data is not appear.
Here is code:
public class MainActivity extends AppCompatActivity {
private static final int NUM_LIST_ITEMS = 100;
private static final String TOKEN =
"71cf2d3dec294394e267fbb0bf28916f4198f8d6";
private CuloAdapter culoAdapter;
List<Hotel> lh = new ArrayList<>();
RecyclerView recyclerView;
#Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.rv_tiketapi);
LinearLayoutManager layout = new LinearLayoutManager(this);
layout.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layout);
CuloAdapter ar = new CuloAdapter(lh);
recyclerView.setAdapter(ar);
loadHotelLocation();
}
private void loadHotelLocation() {
final search apiService = ApiService.getService(search.class);
retrofit2.Call<SingleResult<Hotel>> call = apiService.findHotel(TOKEN,
"json");
call.enqueue(new Callback<SingleResult<Hotel>>() {
#Override
public void onResponse(retrofit2.Call<SingleResult<Hotel>> call,
Response<SingleResult<Hotel>> response) {
if (response.body().getDiagnostic().isSuccess()) {
//SingleResult.ResultList list =
response.body().getResults();
List<Hotel> mHotels = (List<Hotel>)
response.body().getResults().getResult();
lh.addAll(mHotels);
Toast.makeText(getApplicationContext(), "OK" +lh,
Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(retrofit2.Call<SingleResult<Hotel>> call,
Throwable t) {
}
});
}
RecyclerView Adapter :
public class CuloAdapter extends
RecyclerView.Adapter<CuloAdapter.ViewHolder> {
public CuloAdapter(List<Hotel> lh) {
this.items = lh;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView txtTitle;
public TextView txtSubTitle;
public ImageView imgIcon;
public ViewHolder(final View container) {
super(container);
txtTitle = (TextView) container.findViewById(R.id.airportName);
txtSubTitle = (TextView) container.findViewById(R.id.airportCode);
}
}
private List<Hotel> items;
public CuloAdapter(final Activity activity, List<Hotel> items) {
this.items = items;
}
#Override
public int getItemCount() {
return items.size();
}
#Override
public void onBindViewHolder(CuloAdapter.ViewHolder holder, int position) {
Hotel item = items.get(position);
holder.txtTitle.setText(item.getLabel());
holder.txtSubTitle.setText(item.getId());
}
#Override
public CuloAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int
viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_flight, parent, false);
return new ViewHolder(v);
}
}
MainActivity XML :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.admin.exampletiketapi.MainActivity">
<EditText
android:id="#+id/et_find"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="center" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_tiketapi"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
Item List XML :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/airportsLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp">
<TextView
android:id="#+id/airportName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:text="Soekarno Hatta" />
<TextView
android:id="#+id/airportCode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:text="20" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="5dp"
android:background="#DDD"
android:visibility="visible" />
</LinearLayout>
In your code you are trying to set adapter before loading data.
CuloAdapter ar = new CuloAdapter(lh);
recyclerView.setAdapter(ar);
loadHotelLocation();
What i found is You are getting data in loadHotelLocation(), but trying to set adpter before that,
Call notifyDatasetChanged after lh.addAll(mHotels). And check for null's else you are heading for crash in case search results are zero/null

Custom adapter used in onStart() but app crashes when adding an item to ListView

public class ExerciseList extends ArrayAdapter<Exercise> {
private Activity context;
List<Exercise> exercises;
public ExerciseList(Activity context, List<Exercise> exercises) {
super(context, R.layout.layout_exercise_list, exercises);
this.context = context;
this.exercises = exercises;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = context.getLayoutInflater();
View listViewItem = inflater.inflate(R.layout.layout_exercise_list, null, true);
TextView nameTextView = listViewItem.findViewById(R.id.nameTextView);
TextView setsTextView = listViewItem.findViewById(R.id.setsTextView);
TextView repsTextView = listViewItem.findViewById(R.id.repsTextView);
TextView restTextView = listViewItem.findViewById(R.id.restTextView);
Exercise exercise = exercises.get(position);
nameTextView.setText(exercise.getName());
setsTextView.setText(String.valueOf(exercise.getSets()));
repsTextView.setText(String.valueOf(exercise.getReps()));
restTextView.setText(String.valueOf(exercise.getRestTime()));
return listViewItem;
}
}
public class WorkoutActivity extends AppCompatActivity {
EditText exerciseNameET;
EditText setsET;
EditText repsET;
EditText restTimeET;
Button addButton;
ArrayList<Exercise> exercises;
ListView exerciseList;
DatabaseReference dbExercises;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_workout);
dbExercises = FirebaseDatabase.getInstance().getReference("exercises");
exerciseNameET = findViewById(R.id.exerciseNameET);
setsET = findViewById(R.id.setsET);
repsET = findViewById(R.id.repsET);
restTimeET = findViewById(R.id.restTimeET);
addButton = findViewById(R.id.addButton);
exerciseList = findViewById(R.id.exerciseList);
exercises = new ArrayList<>();
addButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
addExercise();
}
});
}
#Override
protected void onStart() {
super.onStart();
dbExercises.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
exercises.clear();
for(DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
Exercise exercise = postSnapshot.getValue(Exercise.class);
exercises.add(exercise);
}
ExerciseList exerciseAdapter = new ExerciseList(WorkoutActivity.this, exercises);
exerciseList.setAdapter(exerciseAdapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private void addExercise() {
String name = exerciseNameET.getText().toString();
int sets = Integer.parseInt(setsET.getText().toString());
int reps = Integer.parseInt(repsET.getText().toString());
int restTime = Integer.parseInt(restTimeET.getText().toString());
if((!TextUtils.isEmpty(name)) || (!TextUtils.isEmpty(setsET.getText().toString())) || (!TextUtils.isEmpty(repsET.getText().toString())) || (!TextUtils.isEmpty(restTimeET.getText().toString()))) {
String id = dbExercises.push().getKey();
Exercise exercise = new Exercise(id,name,sets,reps,restTime);
dbExercises.child(id).setValue(exercise);
exerciseNameET.setText("");
setsET.setText("");
repsET.setText("");
restTimeET.setText("");
} else {
Toast.makeText(this, "Invalid fields. Please complete the missing fields.", Toast.LENGTH_LONG).show();
}
}
}
I have created a custom adapter to be used for a ListView of one of my activites. This custom adapter is created in the onStart() method. I also have a separate layout file for each item in this ListView. The app uses Firebase and it successfully saves information to the database, but the app crashes when trying to display this same information on the ListView. I have followed many tutorials and my professor's lab solutions but I still do not know why my app is crashing. The XML files for the main activity is included below:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.josh2.mygains.WorkoutActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="#+id/exerciseNameET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Exercise name"
android:inputType="textPersonName" />
<EditText
android:id="#+id/setsET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Number of sets"
android:inputType="textPersonName" />
<EditText
android:id="#+id/repsET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Number of reps"
android:inputType="textPersonName" />
<EditText
android:id="#+id/restTimeET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Length of rest time"
android:inputType="textPersonName" />
<Button
android:id="#+id/addButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/exerciseList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</ScrollView>
</LinearLayout>
The XML file for the items of the ListView is found below:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/nameTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp" />
<TextView
android:id="#+id/setsTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="15sp" />
<TextView
android:id="#+id/repsTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="15sp" />
<TextView
android:id="#+id/restTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="15sp" />
your adapter code is minefield for possible null pointer exceptions. String.valueOf(null) returns NullPointerException, so you should carefully handle any exception here.
if(exercise!=null && exercise.getSets()!=null){
setsTextView.setText(String.valueOf(exercise.getSets()));
}
apply same procedure for rest 2 setters as well.
In addition, the way you have handled your data update in the custom adapter is not standard. You should initiate your adapter in onCreate as you don't have to initialize the adapter every time the list is updated.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_workout);
dbExercises = FirebaseDatabase.getInstance().getReference("exercises");
exerciseNameET = findViewById(R.id.exerciseNameET);
setsET = findViewById(R.id.setsET);
repsET = findViewById(R.id.repsET);
restTimeET = findViewById(R.id.restTimeET);
addButton = findViewById(R.id.addButton);
exerciseList = findViewById(R.id.exerciseList);
exercises = new ArrayList<>();
ExerciseList exerciseAdapter = new ExerciseList(WorkoutActivity.this,
exercises);
exerciseList.setAdapter(exerciseAdapter);
addButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
addExercise();
}
});
}
and keep an updateExerciseList method on your custom adapter so that you can update the list in your adapter every time data is updated
public void updateExerciseList(List<Exercise> exercises) {
this.exercises = exercises;
}
simply update your data list after database is updated
#Override
protected void onStart() {
super.onStart();
dbExercises.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
exercises.clear();
for(DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
Exercise exercise = postSnapshot.getValue(Exercise.class);
exercises.add(exercise);
}
exerciseAdapter.updateExerciseList(exercises);
exerciseAdapter.setNotifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}

Item not showing inside RecyclerView

I check everything in my code,I ask a question and do some of the modification,and also reading all the question about "Item not showing in RecyclerView",but the layout still not appear in my recyclerView.
This is my previous question,I attach all my code here,have 3 answer tell me,I should use LinearLayout instead of RelativeLayout as the parent of RecyclerView,so I modified the code as below
Fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/commentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="#+id/titlebar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="#+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/feed_item_margin"
android:layout_marginRight="#dimen/feed_item_margin"
android:layout_marginTop="#dimen/feed_item_margin"
android:layout_weight="1"
android:text="Be the first to like this" />
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/comment_recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:animateLayoutChanges="false"
android:scrollbars="vertical">
</android.support.v7.widget.RecyclerView>
<LinearLayout
android:id="#+id/commentInsert"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white"
android:orientation="horizontal">
<EditText
android:id="#+id/commentField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#null"
android:ems="10"
android:hint="Add a comment" />
<Button
android:id="#+id/sendButton"
android:layout_width="77dp"
android:layout_height="wrap_content"
android:text="Send" />
</LinearLayout>
</LinearLayout>
Here is the item which should be appear in the RecyclerView.comment_item.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="wrap_content"
android:layout_weight="1">
<ImageView
android:id="#+id/commentProfilePic"
android:layout_weight="0.05"
android:layout_width="#dimen/comment_item_profile_pic"
android:layout_height="#dimen/comment_item_profile_pic"
android:layout_marginLeft="#dimen/feed_item_margin"
android:layout_marginRight="#dimen/feed_item_margin"
android:layout_marginTop="#dimen/feed_item_margin"
android:scaleType="fitCenter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/feed_item_margin"
android:layout_marginRight="#dimen/feed_item_margin"
android:layout_marginTop="#dimen/feed_item_margin"
android:orientation="vertical"
android:layout_weight="1"
android:paddingLeft="#dimen/comment_item_profile_info_padd">
<TextView
android:id="#+id/commentUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="#dimen/comment_item_status_pad_left_right"
android:paddingRight="#dimen/comment_item_status_pad_left_right"
android:textStyle="bold" />
<TextView
android:id="#+id/commentBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="#dimen/comment_item_status_pad_left_right"
android:paddingRight="#dimen/comment_item_status_pad_left_right" />
<TextView
android:id="#+id/commentTimestamp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="#dimen/comment_item_status_pad_left_right"
android:paddingRight="#dimen/comment_item_status_pad_left_right"
android:paddingTop="#dimen/comment_item_timestamp_pad_top" />
</LinearLayout>
I read this issue tell that need to add this line of code adapter.notifyDataSetChanged();,I done that,this is how I notifyDataSetChanged() when I parse the JSON.
private void parseJsonFeed(JSONObject response) {
try {
JSONArray commentArray = response.getJSONArray("comment");
//get all the item in Json
for (int i = 0; i < commentArray.length(); i++) {
JSONObject commentObj = (JSONObject) commentArray.get(i);
commentId = commentObj.getInt("comment_id");
commenterUsername= commentObj.getString("commenter_username");
commentBody = commentObj.getString("comment_body");
commenterProfileImage = commentObj.getString("commenter_profile_image");
commentCreatedAt = commentObj.getString("comment_created_at");
//set all item to the Array list
setItemToCommentArrayList(commentId,commenterUsername,commenterProfileImage,commentBody,commentCreatedAt);
}
// notify data changes to list adapter
commentAdapter.notifyDataSetChanged();
}catch (JSONException e){
System.out.println("end of content");
}
}
I checked,whether is something wrong when I set my adapter to the RecycleView.So i read this answer,and here is how I set my adapter
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View dialogView = inflater.inflate(R.layout.fragment_comment, container,false);
commentRecyclerView =(RecyclerView)dialogView.findViewById(R.id.comment_recycler_view);
commentRecyclerView.setNestedScrollingEnabled(false);
//bind the recycler view with the adapter
commentAdapter = new CommentAdapter(getActivity(),commentItems);
final LinearLayoutManager myLayoutManager = new LinearLayoutManager(getActivity());
commentRecyclerView.setLayoutManager(myLayoutManager);
commentRecyclerView.setAdapter(commentAdapter);
Here is my CommentAdpater , I still didnt see any different with my another recycleview adapter.
public class CommentAdapter extends RecyclerView.Adapter<CommentAdapter.MyViewHolder>{
private Context cContext;
private List<CommentItem> commentItems;
class MyViewHolder extends RecyclerView.ViewHolder{
TextView commentBody,commentUsername,commentTimeStamp;
ImageView commentProfilePic;
//find all the view here
MyViewHolder(final View view) {
super(view);
commentProfilePic = (ImageView)view.findViewById(R.id.commentProfilePic);
commentUsername = (TextView)view.findViewById(R.id.commentUsername);
commentBody = (TextView)view.findViewById(R.id.commentBody);
commentTimeStamp = (TextView)view.findViewById(R.id.commentTimestamp);
}
}
public CommentAdapter(Context cContext, List<CommentItem> commentItems) {
this.cContext = cContext;
this.commentItems = commentItems;
}
#Override
public long getItemId(int position) {
return position;
}
//this one for make the adview inside this
#Override
public int getItemViewType(int position) {
return position;
}
//bind the comment item here
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View commentItemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.comment_item, parent, false);
return new MyViewHolder(commentItemView);
}
//do all the action here
#Override
public void onBindViewHolder(CommentAdapter.MyViewHolder holder, int position) {
final CommentItem commentItem = commentItems.get(position);
//commenter username
holder.commentUsername.setText(commentItem.getUsername());
//commenter profile image
Glide
.with(cContext)
.load(commentItem.getCommentProfilePic())
.fitCenter()
.into(holder.commentProfilePic);
//comment body
holder.commentBody.setText(commentItem.getCommentBody());
//comment timestamp
holder.commentTimeStamp.setText(commentItem.getCommentTimeStamp());
}
#Override
public int getItemCount() {
return commentItems.size();
}
}
I make sure my JSON is received in my volley Request.I log it out,is received in Volley onResponse .My JSON is look like this
"comment":[{"comment_created_at":"2017-03-21 13:03:40","comment_id":8,"comment_body":"abc","commenter_username":"abc","profile_image_path":"http:\/\/abc\/def\/v1\/ef\/defaultmale.jpg"
And this question I ask just tell me no need to worry about cause I using Glide to load my image,Glide will handle that
This is all the solution that tried,and still havent get it done,so what I still missing out here,in order the comment_xmlappear to the recyclerView inside Fragment.xml? Any guide that lead me to the right direction is well-appreciated.Thanks
UPDATE
Here is my setItemToCommentArrayList()
private void setItemToCommentArrayList(int commentId, String commenterUsername, String commenterProfileImage, String commentBody, String commentCreatedAt) {
CommentItem item = new CommentItem();
item.setCommentId(commentId);
item.setUsername(commenterUsername);
item.setCommentProfilePic(commenterProfileImage);
item.setCommentBody(commentBody);
item.setCommentTimeStamp(commentCreatedAt);
//save it to the comment array list
commentItems.add(item);
}
Here is my data model of Comment Item
public class CommentItem {
private String username,commentBody,commentTimeStamp,commentProfilePic;
private int commentId;
public CommentItem(){
}
public CommentItem(int commentId, String username,String commentBody,String commentTimeStamp,String commentProfilePic){
super();
this.commentId = commentId;
this.username = username;
this.commentBody = commentBody;
this.commentProfilePic= commentTimeStamp;
this.commentTimeStamp= commentProfilePic;
}
public int getCommentId() {
return commentId;
}
public void setCommentId(int commentId) {
this.commentId = commentId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getCommentBody() {
return commentBody;
}
public void setCommentBody(String commentBody) {
this.commentBody = commentBody;
}
public String getCommentTimeStamp() {
return commentTimeStamp;
}
public void setCommentTimeStamp(String commentTimeStamp) {
this.commentTimeStamp = commentTimeStamp;
}
public String getCommentProfilePic() {
return commentProfilePic;
}
public void setCommentProfilePic(String commentProfilePic) {
this.commentProfilePic = commentProfilePic;
}
}
First of all try to print out your dataSet size into getItemCount method of recyclerview -
#Override
public int getItemCount() {
Log.e("Size of data:", "" +commentItems.size())
return commentItems.size();
}
If it returns greater than zero - there may be a problem with your row_item of recyclerview. Double check your comment_item.xml file. Possibly try to remove weights.
Are items not showing in the cards or is the recycler view itself not showing?
If the recycler view itself is not showing, maybe the problem is inside the getItemCount the size being sent is 0.
do a check there and return 1 if size is zero.
#Override
public int getItemCount() {
if(commentItems.size() == 0)
return 1;
else
return commentItems.size();
}

Categories

Resources