【问题标题】:RecyclerView isn't showing data until refreshedRecyclerView 在刷新之前不显示数据
【发布时间】:2017-03-09 22:08:21
【问题描述】:

我正在使用 Firebase 填充我的 RecyclerView,但问题是每次我打开应用程序时,我都需要向下滑动以刷新,然后项目列表才可用。

这是我的代码

public class MainActivity extends AppCompatActivity {


private static final int RC_PHOTO_PICKER =  2;

DatabaseReference db;
FirebaseHelper helper;
MyAdapter adapter;
RecyclerView rv;
EditText nameEditTxt,grpTxt,descTxt,linkTxt;
FirebaseAuth.AuthStateListener authListener;
SwipeRefreshLayout swipeRefresh;
Uri downloadUrl;
String Admin_code;
FirebaseStorage mfirebaseStorage;
private StorageReference mEventPhotoReference;
FloatingActionButton fab;
SharedPreferences sharedPreferences;
ProgressBar spinner;


static boolean calledAlready=false;
public MyAdapter adapter1;


@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    spinner = (ProgressBar)findViewById(R.id.progressBar);
    spinner.setVisibility(View.GONE);


    mfirebaseStorage=FirebaseStorage.getInstance();

    if(!calledAlready){
        FirebaseDatabase.getInstance().setPersistenceEnabled(true);
        calledAlready=true;
    }

    swipeRefresh=(SwipeRefreshLayout)findViewById(R.id.swiperefresh);

    //SETUP RECYCLER
    rv = (RecyclerView) findViewById(R.id.rv);
    rv.setLayoutManager(new LinearLayoutManager(this));



    //INITIALIZE FIREBASE DB
    db= FirebaseDatabase.getInstance().getReference();
    mEventPhotoReference=mfirebaseStorage.getReference().child("Event Photos");
    helper=new FirebaseHelper(db);



    //ADAPTER
    adapter=new MyAdapter(this,helper.retrieve());
    rv.setAdapter(adapter);
    adapter.notifyDataSetChanged();




    swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            rv.setAdapter(adapter);
            swipeRefresh.setRefreshing(false);
        }
    });

    sharedPreferences= PreferenceManager.getDefaultSharedPreferences(this);
    Admin_code=sharedPreferences.getString(getString(R.string.Admin_code),getString(R.string.Admin_default_value));

    Log.e("MainActivity","" + Admin_code);

    fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            displayInputDialog();
        }
    });

    fab.setVisibility(View.GONE);
    showBtn();

}

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
protected void onResume() {
    showBtn();
    super.onResume();

}



@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void showBtn(){
    Admin_code=sharedPreferences.getString(getString(R.string.Admin_code),getString(R.string.Admin_default_value));
    if(Objects.equals(Admin_code, "28011996")){

        fab.setVisibility(View.VISIBLE);

    }
    else
        fab.setVisibility(View.GONE);


}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu_main, menu);
    return true;
}


@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == R.id.action_logout) {
        FirebaseAuth.getInstance().signOut();
        startActivity(new Intent(MainActivity.this, LoginActivity.class));
        return true;
    }
    if(id==R.id.settings){
        Intent settingsIntent=new Intent(this,Settings.class);
        startActivity(settingsIntent);
        return true;
    }

    return super.onOptionsItemSelected(item);
}


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if ((requestCode& 0xffff) == RC_PHOTO_PICKER && resultCode == RESULT_OK) {
        Uri selectedImageUri = data.getData();


        StorageReference photoRef=mEventPhotoReference.child(selectedImageUri.getLastPathSegment());


        photoRef.putFile(selectedImageUri)
                .addOnSuccessListener(this, new OnSuccessListener<UploadTask.TaskSnapshot>() {
                    public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                        // When the image has successfully uploaded, we get its download URL
                        downloadUrl = taskSnapshot.getDownloadUrl();
                        Toast.makeText(MainActivity.this,"Photo selected successfully",Toast.LENGTH_LONG).show();
                    }

                }).addOnFailureListener(this, new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Toast.makeText(MainActivity.this,"there was a problem uploading photo",Toast.LENGTH_LONG).show();
            }
        });

    }


}

//DISPLAY INPUT DIALOG
private void displayInputDialog()
{
    Dialog d=new Dialog(this);
    d.setTitle("Save To Firebase");
    d.setContentView(R.layout.input_dialog);

    nameEditTxt= (EditText) d.findViewById(R.id.nameEditText);
    grpTxt= (EditText) d.findViewById(R.id.propellantEditText);
    descTxt= (EditText) d.findViewById(R.id.descEditText);
    Button saveBtn= (Button) d.findViewById(R.id.saveBtn);
    Button photoBtn=(Button)d.findViewById(R.id.photoBtn);
    linkTxt = (EditText) d.findViewById(R.id.linkEditText);



    photoBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent=new Intent(Intent.ACTION_GET_CONTENT);
            intent.setType("image/jpeg");
            intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
            startActivityForResult(Intent.createChooser(intent, "Complete action using"), RC_PHOTO_PICKER);

        }
    });


    //SAVE
    saveBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            //GET DATA
            String name=nameEditTxt.getText().toString();
            String propellant=grpTxt.getText().toString();
            String desc=descTxt.getText().toString();
            String link=linkTxt.getText().toString();
            Long tsLong = System.currentTimeMillis()/1000;
            String ts = tsLong.toString();


            //SET DATA
            Spacecraft s=new Spacecraft();


                s.setName(name);
                s.setPropellant(propellant);
                s.setDescription(desc);
                s.setLink(link);
                s.setImageUrl(downloadUrl.toString());
                s.setTimestamp(ts);

            //SIMPLE VALIDATION
            if(name != null && name.length()>0)
            {
                //THEN SAVE
                if(helper.save(s))
                {
                    //IF SAVED CLEAR EDITXT
                    nameEditTxt.setText("");
                    grpTxt.setText("");
                    descTxt.setText("");
                    linkTxt.setText("");
                    downloadUrl=null;



                    adapter=new MyAdapter(MainActivity.this,helper.retrieve());
                    rv.setAdapter(adapter);
                    adapter.notifyDataSetChanged();

                }
            }else
            {
                Toast.makeText(MainActivity.this, "Name Must Not Be Empty", Toast.LENGTH_SHORT).show();
            }

        }
    });

    d.show();
}

搜索了一会,发现应该用notifyDataSetChanged();,但是不行。

这是我获取数据的辅助类:

public class FirebaseHelper {

    DatabaseReference db;
    Boolean saved=null;
    ArrayList<Spacecraft> spacecrafts=new ArrayList<>();

    public FirebaseHelper(DatabaseReference db) {
        this.db = db;
    }

    //WRITE IF NOT NULL
    public Boolean save(Spacecraft spacecraft)
    {
        if(spacecraft==null)
        {
            saved=false;
        }else
        {
            try
            {
                db.child("Spacecraft").push().setValue(spacecraft);
                saved=true;


            }catch (DatabaseException e)
            {
                e.printStackTrace();
                saved=false;
            }
        }

        return saved;
    }

    //IMPLEMENT FETCH DATA AND FILL ARRAYLIST
    private void fetchData(DataSnapshot dataSnapshot)
    {
        spacecrafts.clear();

        for (DataSnapshot ds : dataSnapshot.getChildren())
        {
            Spacecraft spacecraft=ds.getValue(Spacecraft.class);
            spacecrafts.add(spacecraft);

        }

    }

    //READ THEN RETURN ARRAYLIST
    public ArrayList<Spacecraft> retrieve() {
        db.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                fetchData(dataSnapshot);


            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {
                fetchData(dataSnapshot);

            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {
                fetchData(dataSnapshot);

            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
        return spacecrafts;
    }

如果有人可以在这里帮助我,那就太好了。这是我项目中的最后一个问题。

【问题讨论】:

  • retrieve 方法返回空ArrayList。您需要先获取数据,然后使用该数据更新RecyclerView。使用调试器了解发生了什么。
  • 这是一个异步响应。
  • @Divers 你能详细说明一下我对android还很陌生,谢谢
  • 尝试使用调试器 - 你会明白的。请记住,它是异步的。
  • 这个答案解释了数据的异步加载:stackoverflow.com/a/41409942/4815718

标签: android firebase firebase-realtime-database android-recyclerview


【解决方案1】:

问题是由于适配器不知道异步获取的数据。让我们尝试一步一步地查看您的代码:

adapter = new MyAdapter(this, helper.retrieve());

helper.retrieve() 将发起一个异步请求以从 firebase 获取数据并立即返回一个空的 ArrayList(因为来自 firebase 的数据尚未到达应用程序)。

rv.setAdapter(adapter);

这一行将适配器设置为recyclerView,它有一个空的arraylist作为数据,所以它不会在UI上显示任何东西。

从 firebase 收到数据后,ArrayList&lt;Spacecraft&gt; spacecrafts 会更新为新数据。但是适配器不会收到有关此新数据的通知。理想情况下,应在将新数据放入数组列表后调用 adapter.notifyDataSetChanged()。

因此,当您在初始调用后滑动刷新时,适配器会设置为回收器视图,从而导致对 notifyDataSetChanged() 的内部调用,进而将新数据加载到 UI。

最简单的解决方案是在从 Firebase 收到新数据时通知适配器。下面是它的伪代码-

  • 创建一个接口 OnFirebaseDataChanged 并向其添加方法void dataChanged();

  • MainAcitivty 应该实现这个接口并且那个方法的实现应该调用adapter.notifyDataSetChanged()

  • helper = new FirebaseHelper(db); 应改为helper = new FirebaseHelper(db, this);

  • FirebaseHelper 的构造函数应具有 OnFirebaseDataChanged 类型的第二个参数,并将其保存在名为 dataChangeListener 的字段中。

  • 在 FirebaseHelper dataChangeListener.dataChanged() 中的 fetchData 方法末尾添加一行;

理想的解决方案是以不同方式构建代码,但该主题超出了本问题的范围。

【讨论】:

  • 感谢您的解决方案,它有效(标记为答案)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-15
  • 1970-01-01
  • 2021-05-08
相关资源
最近更新 更多