8

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:

List of results inside setResults()

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?

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
joe
  • 1,341
  • 4
  • 21
  • 32
  • In adapter always use else with every if statement. Specially when working with views – Amroun Aug 08 '18 at 08:49
  • Please don't include pictures of your screen. It's almost impossible to read. If you absolutely do have to include an image you should use a screenshot. – André Kool Aug 08 '18 at 09:07
  • Also it seems your POJO class is missing a [default constructor](https://firebase.google.com/docs/database/android/read-and-write#basic_write) – André Kool Aug 08 '18 at 09:08
  • Sorry, I'm asking for a friend and that's what she sent me.. Also, it's not working with default constructor too – joe Aug 08 '18 at 11:18

2 Answers2

0

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.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
0

I have the same issue where I call notifyDataSetChanged but the onBindViewHolder isn't called. Only when I touch and try to scroll, onBindViewHolder is called.

I solved mine by add a mini scroll after notifying the adapter:

binding.rvwContent.scrollBy(0,1) // this is scroll down
Vaz
  • 678
  • 6
  • 16
  • Hey, welcome to StackOverflow. Here are a couple of suggestions on formatting answers: you can use backticks `\`` around code snippets, method names, etc. to have them stand out and be written monospaced, `like this`. Also, try to be precise with capitalization when referring to method names. I can tell what you mean when you write "onbindviewholder", but it can lead others to make mistakes if they aren't as familiar with the API yet, and it's harder to read. Thanks for your contribution and I hope you keep it up. – Vaz Jun 21 '23 at 00:45