【问题标题】:RecyclerView displaying the last item in the adapter several times. Need all adapter items to show in RecyclerViewRecyclerView 多次显示适配器中的最后一项。需要在 RecyclerView 中显示所有适配器项目
【发布时间】:2015-08-24 21:15:42
【问题描述】:

所以我试图将 Parse 数据库查询中的数据加载到 recyclerView 中。该查询将我的所有数据完美地加载到我的 ArrayList 中,然后我将其添加到我的适配器中。

不幸的是,我的 recyclerView 只显示适配器数据的最后添加。但是,如果适配器中有 15 个对象,那么最后一次添加将在屏幕上重复 15 次。

我认为问题在于数据 ArrayList 内部的值由于某种原因在查询之外不存在,即使我在类中声明了它。

这是我的 MainActivity 代码:

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 代码:

    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);
}
}

【问题讨论】:

  • 尝试替换'adapter = new DataAdapter(getApplicationContext(), data);' by '适配器 = 新 DataAdapter(this, data);'或'适配器 = 新 DataAdapter(上下文,数据);'用'上下文=这个;'在声明或内部 onCreate
  • 刚试过这个 - 没用 :(
  • 您应该发布 DataAdapter 代码以获取更多信息。此外,将Log.d("ADAPTERDATA", data.toString()); 放入done(...)。
  • 如果我把 Log.d("ADAPTERDATA", data.toString());里面完成(..)然后它工作正常。整个问题是我需要 data.toString() 在 done 循环之外可用。另外,我发布了 DataAdapter 代码
  • 我认为done 在另一个线程中,对吧?那么,如果在done 之外,如何确保数据不为空?

标签: java android parse-platform nullpointerexception android-recyclerview


【解决方案1】:

我也遇到了同样的问题,并在谷歌上搜索了一下。 绝对的解决方案是在主活动的 for 循环中创建 Data 类型的 current 对象

说明:当您在循环外声明 current 对象时,您正在创建一个对象并使用具有相同值的同一对象 n 次

编码愉快!!

【讨论】:

    【解决方案2】:

    我认为这是因为您在适配器初始化之前调用了 adapter.notifyDataSetChanged() 。由于它仅在回调中初始化,我认为您可能会在回调完成之前调用 notifyDataSetChanged() (我认为您提到广播每秒发生一次)。我不能 100% 确定,因为我看不到您的其余代码。

    【讨论】:

    • 我还有什么可以补充的吗?因为如果我将 Log.d("arrayList", data) 放入我的查询中,它将显示在 Log.d("ADAPTERDATA", data.toString) 之前,那么它之前不是发生过吗?问题是我需要能够使用查询之外的查询结果访问数据。如果我可以在任何地方访问填充的 ArrayList,我应该是金子。
    • 调试它,在回调和 onReceive 上设置一个断点,看看哪个先命中。还要在 adapter.notifyDataSetChanged() 上创建一个断点;并检查适配器是否已初始化。您可以做的另一件事是在执行查询之前使用空数据初始化适配器,并在回调中添加数据,然后调用 adapter.notifyDataSetChanged();
    • 因此,可能对这个问题有所了解的一个问题是,完成循环之外的 Log.d("ADAPTERDATA", data.toString()) 返回空,我不知道为什么.出于这个原因,我认为这与被调用的内容无关,更多的是我无法在循环之外访问适配器的内容。另外,如果我将 Log.d("ADAPTERDATA", data.toString()) 放在 done 循环中,那么它就不是 null...
    • 所以,在您从 Parse 取回 ParseObjects 之前,不会调用 done()。 (你明白,直到数据从 Parse 回来后才会运行回调,对吧?)因此,当你做 Log 时,适配器数据将为空。而且我认为您正在接收广播以在回调完成之前更新适配器(甚至在适配器被实例化之前)。因此,就像我上面所说的,您必须在执行 query.findInBackground() 之前初始化适配器。在回调中,只需将 ParseObjects 添加到适配器并调用 notifyDatasetChanged()
    • 好的,所以我补充说——认为它会起作用,但没有。还有其他建议吗? Log.d("ADAPTERDATA") 仍然显示一个空适配器。我还编辑了上面的代码以反映您的建议。
    【解决方案3】:

    那么,让我直说吧。您在 onCreate() 中执行以下操作:

    1. 做一些设置
    2. 创建适配器。
    3. 启动在另一个线程中运行的进程以填充数据。
    4. 几乎立即尝试使用该数据记录消息。

    因此,您希望在执行 3 中的代码和基本上执行 4 中代码的下一组指令之间,Android 将在另一个 CPU 上安排该进程,运行至完成,将该数据编组到由UI 线程,所有的时间,以便当它执行 4 的代码时,有实际的数据?你有一个特殊的手机,它的 UI 线程运行在一个慢 100 倍的内核上?

    没有。线程同步问题没有灵丹妙药。要么重新设计代码,以便正确同步跨线程访问数据,要么使用线程同步原语来强制执行所需的行为。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-17
      • 2017-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-07
      相关资源
      最近更新 更多