android - RecyclerView 在刷新之前不显示数据

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

我正在使用 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;
    }

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

最佳答案

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

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

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

rv.setAdapter(adapter);

此行将适配器设置为 recyclerView,它有一个空数组列表作为数据,因此它不会在 UI 上显示任何内容。

从 firebase 收到数据后,ArrayList<Spacecraft> spacecrafts用新数据更新。但是适配器没有收到有关此新数据的通知。理想情况下,应该在新数据放入数组列表后调用 adapter.notifyDataSetChanged()。

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

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

  • 创建接口(interface) OnFirebaseDataChanged 并添加方法 void dataChanged();

  • MainAcitivty 应实现此接口(interface),该方法的实现应调用 adapter.notifyDataSetChanged()

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

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

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

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

关于android - RecyclerView 在刷新之前不显示数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42487771/

相关文章:

java - 提供单例依赖与对象初始化

java - Android 应用程序 - 后退按钮

javascript - 从选择的 angularJs 和 Firebase 中获取值(value)

android - Firebase异常 : [ DEVELOPMENT_MODE_MISMATCH: Non-development mode Verification Proof given in development mode request

javascript - 如何按键获取Firebase数据库(Angular)中的值(value)?

android - Android可以在单个线程内事件 "switch"吗

java - 通过 ActionBarActivity 支持 v7 操作栏

ios - 将新用户插入 firebase

AngularFire2 观察者

android - 如何在android后台服务中监听firebase事件?