简体   繁体   中英

RecyclerView displaying the last item in the adapter several times. Need all adapter items to show in RecyclerView

So I am trying to load data from a Parse database query into a recyclerView. The query is loading all my data into my ArrayList perfectly, which I then add to my adapter.

Unfortunately, my recyclerView only displays the last addition to the adapter's data. However, if there are 15 objects in the adapter, then this last addition will be repeated 15 times on screen.

I believe the issue is that the values inside of the data ArrayList, for some reason, do not exist outside of the query, even though I declared it in the class.

Here is my MainActivity Code:

public class MainActivity extends  ActionBarActivity implements DataAdapter.ClickListener {
private RecyclerView recyclerView;
static DataAdapter adapter;

public TextView tvParseUser;
private Context context;
String currentUser;

Data current = new Data();

List<Data> data = new ArrayList<>();


public static String BROADCAST_ACTION =
        "broadcast_action_packagename";
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    IntentFilter filter = new IntentFilter();
    filter.addAction(BROADCAST_ACTION);
    filter.addCategory(Intent.CATEGORY_DEFAULT);
    registerReceiver(br, filter);

//testing Sree's suggestion
        adapter = new CommitmentDataAdapter(getApplicationContext(), data);
        Log.d("DATABEFOREADD", data.toString()); //data here is empty

    startService(new Intent(this, Broadcast_Service.class));
    tvParseUser = (TextView) findViewById(R.id.ParseUserName);
    setContentView(R.layout.activity_main);

    ParseQuery<ParseObject> query = ParseQuery.getQuery("ColumnTitle");
    query.whereEqualTo("author", ParseUser.getCurrentUser());
    recyclerView = (RecyclerView) findViewById(R.id.recyclerView); 
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    query.findInBackground(new FindCallback<ParseObject>() {

        @Override
        public void done(List<ParseObject> list, ParseException e) { //returns actual Parse Object
            if (e == null)
            {
                for (ParseObject getData : list)
                {

                    current.1= getData.getString("1");
                    current.2= getData.getString("2");
                    current.3= getData.getString("3");
                    current.4= getData.getString("4");
                    current.5= getData.getString("5");

                    current.combined= current.1 + current.2 + current.3 + current.4;
                    //checked Log.d, query is working fine. ArrayList should have an object of all the data for use

                    data.add(current);
                    Log.d("data", data.toString());
                    Log.d("CurrentCombined", current.combined);
                    adapter.notifyDataSetChanged(); //Sree's suggestion




                    //data correctly contains list of objects that I need

                }

                Log.d("dataInfo", String.valueOf(data));
                Log.d("Current", current.toString());
            }

            else
            {

            }

            recyclerView.setAdapter(adapter); //set recyclerView to this adapter

        }

    });
    Log.d("ADAPTERDATA", data.toString()); //NOTE THIS RETURNS AN EMPTY ARRAY. WHY?!
}

public BroadcastReceiver br = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        updateGUI(intent);
        adapter.notifyDataSetChanged();

    }
};

DataAdapter code:

    package com.example.android.moneyspeaks;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Chronometer;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import org.slf4j.helpers.Util;
import org.w3c.dom.Text;

import java.sql.Time;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;


public class DataAdapter extends RecyclerView.Adapter<DataAdapter.MyViewHolder> {
    List<Data> data = Collections.emptyList();
    private LayoutInflater inflater;
    private Context context;
    private ClickListener clickListener;
    long millisCountDownTime;
    Intent intent;
    PendingIntent pendingIntent;
    Thread thread;
    Handler handler;
    Long now;
    Long later;
    Integer i = 0;
    final Handler myHandler = new Handler();

    public DataAdapter(Context context, List<Data> data) 
    {
        inflater = LayoutInflater.from(context);
        this.data = data;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        View view = inflater.inflate(R.layout.recycler_view_layout, parent, false); 
        MyViewHolder holder = new MyViewHolder(view); 

        return holder;
    }

    public void setClickListener(ClickListener clickListener) {
        this.clickListener = clickListener;

    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, int position) {
        Data current = data.get(position);
        holder.combined.setText(current.combined);
        holder.codeTimer.setText(current.goalTimer);
        holder.submitCode.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(v.getContext(), "HELL YA", Toast.LENGTH_LONG).show();

            }
        });

        if (current.classify2.equals("day(s)")) {
            millisCountDownTime = 86400000;
        } else if (current.classify2.equals("week(s)")) {
            millisCountDownTime = 604800000;
        } else if (current.classify2.equals("hour(s)")) {
            millisCountDownTime = 3600000;
        } else if (current.classify2.equals("minute(s)")) {
            millisCountDownTime = 6000;
        }
        final Runnable myRunnable = new Runnable() {
            @Override
            public void run() {
                holder.codeTimer.setText(String.valueOf(i));

            }
        };
    }
@Override
public int getItemCount() {
    return data.size();
}

class MyViewHolder extends RecyclerView.ViewHolder {
    TextView combined;
    TextView enterCode;
    TextView codeTimer;
    EditText codeInputField;
    Button submitCode;

    public MyViewHolder(View itemView) {
        super(itemView);
        combined= (TextView) itemView.findViewById(R.id.combinedSentence);
        codeTimer = (TextView) itemView.findViewById(R.id.codeTimer);
        submitCode = (Button) itemView.findViewById(R.id.submitTheCode);

        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (v.getId() == R.id.submitTheCode) {
                    Intent i = new Intent(v.getContext(), Different.class);
                    v.getContext().startActivity(i);

                }

            }
        });
    }
}

public interface ClickListener {
    public void itemClicked(View view, int position);
}
}

I too faced the same issue and broke my head googling.. The absolute solution is to create your current object of type Data inside the for loop in the main activity

Explanation :You are creating a single object and using the same object with the same value n times when you declare your current object outside the loop

Happy coding!!

I think its because you call adapter.notifyDataSetChanged() before the adapter is initialized. Since it is only initialized on the callback, I think there might be a chance that you are calling notifyDataSetChanged() before the callback is done (I think you mentioned the broadcast happens every second). I can't be a 100% sure because I can't see the rest of your code though.

So, let me get this straight. You do the following in onCreate():

  1. Do some setup
  2. Create adapter.
  3. Kickoff a process that runs in another thread to fill data.
  4. Attempt to log a message using that data almost immediately.

So, you expect that between executing the code in 3 and essentially the immediate next set of instructions for the code in 4, Android will have scheduled that process on another CPU, run to completion, marshall that data to the reference held by the UI thread, all in time so that when it executes the code for 4, there is actual data? Do you have a special phone where the UI thread runs on a core that's 100X slower?

No. There is no magic bullet for thread synchronization issues. Either you redesign your code so that access to data across threads is correctly synchronized or you use thread synchronization primitives that enforce the behavior you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM