简体   繁体   中英

RecyclerView inside Fragment not showing properly

I am currently working on a WhatsApp-like Android application and I recently ran into a problem which gave me a headache. I am trying to create a RecyclerView inside a Fragment belonging to a TabLayout. Basically, I have 3 tabs, each containing a Fragment. The first Tab is for the user's conversations, the second one is for the groups and the third is for the contacts. Each tab should contain a RecyclerView which displays data from the Firebase Firestore according to the current user's ID provided by the Firebase Authentication. The problem I think is coming from the fact that inside the Fragment besides the RecyclerView I also have a FloatingActionButton. This button is used in each Fragment for performing actions like sending a new message or creating a new group or adding a new contact. When clicked, let's say inside the groups tab, the FAB displays an AlertDialog asking the user to complete the group name and then select "Create" and the group is created and added into the database. Now, the problem is that when the user logs into the app and accesses the groups tab, his groups are not displayed, but if the user clicks on the FAB, the AlertDialog appears and if the user clicks the EditText in order to pick a name for a new group, suddenly the user's groups are displayed inside the Fragment. I need some help with this, I don't have much experience with Android and I think I am messing up the layouts, but I can't really figure it out by myself.

Here is my code:

MainActivity.java

    package com.example.snakemessenger;

    import android.content.Intent;
    import android.os.Bundle;

    import androidx.annotation.NonNull;

    import android.view.MenuItem;
    import com.google.android.material.tabs.TabLayout;
    import com.google.firebase.auth.FirebaseAuth;
    import com.google.firebase.auth.FirebaseUser;
    import com.google.firebase.firestore.FirebaseFirestore;
    import com.google.firebase.storage.FirebaseStorage;
    import com.google.firebase.storage.StorageReference;

    import androidx.appcompat.app.AppCompatActivity;
    import androidx.appcompat.widget.Toolbar;
    import androidx.viewpager.widget.ViewPager;

    import android.view.Menu;
    import android.widget.Toast;

    public class MainActivity extends AppCompatActivity {
        public static final String TAG = "SnakeMessenger";
        private FirebaseAuth mAuth;
        private FirebaseFirestore db;
        private StorageReference storageReference;

        private ViewPager mViewPager;
        private TabLayout mTabLayout;
        private TabsAccessorAdapter mTabsAccessorAdapter;

        private FirebaseUser currentUser;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Toolbar mToolbar = findViewById(R.id.main_page_toolbar);
            setSupportActionBar(mToolbar);
            getSupportActionBar().setTitle("Snake Messenger");

            mAuth = FirebaseAuth.getInstance();
            db = FirebaseFirestore.getInstance();
            storageReference = FirebaseStorage.getInstance().getReference();

            currentUser = mAuth.getCurrentUser();

            if (currentUser == null) {
                sendUserToLoginActivity();
            }

            mViewPager = findViewById(R.id.main_tabs_pager);
            mTabsAccessorAdapter = new TabsAccessorAdapter(getSupportFragmentManager());
            mViewPager.setAdapter(mTabsAccessorAdapter);

            mTabLayout = findViewById(R.id.main_tabs);
            mTabLayout.setupWithViewPager(mViewPager);
        }

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            super.onCreateOptionsMenu(menu);

            getMenuInflater().inflate(R.menu.options_menu, menu);

            return true;
        }

        @Override
        public boolean onOptionsItemSelected(@NonNull MenuItem item) {
            super.onOptionsItemSelected(item);

            if (item.getItemId() == R.id.main_find_friends_option) {
                // TODO
            } else if (item.getItemId() == R.id.main_settings_option) {
                sendUserToSettingsActivity();
            } else if (item.getItemId() == R.id.main_sign_out_option) {
                mAuth.signOut();
                sendUserToLoginActivity();
                Toast.makeText(MainActivity.this, "Signed out", Toast.LENGTH_SHORT).show();
            }

            return true;
        }

        private void sendUserToSettingsActivity() {
            Intent settingsIntent = new Intent(MainActivity.this, SettingsActivity.class);
            startActivity(settingsIntent);
        }

        private void sendUserToLoginActivity() {
            Intent loginIntent = new Intent(MainActivity.this, SignInActivity.class);
            loginIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(loginIntent);
            finish();
        }
    }

TabsAccessorAdapter.java


    package com.example.snakemessenger;

    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.fragment.app.Fragment;
    import androidx.fragment.app.FragmentManager;
    import androidx.fragment.app.FragmentPagerAdapter;

    public class TabsAccessorAdapter extends FragmentPagerAdapter {
        public TabsAccessorAdapter(@NonNull FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            switch (position) {
                case 0:
                    return new ChatsFragment();
                case 1:
                    return new GroupsFragment();
                case 2:
                    return new FriendsFragment();
                default:
                    return null;
            }
        }

        @Override
        public int getCount() {
            return 3;
        }

        @Nullable
        @Override
        public CharSequence getPageTitle(int position) {
            switch (position) {
                case 0:
                    return "Chats";
                case 1:
                    return "Groups";
                case 2:
                    return "Friends";
                default:
                    return null;
            }
        }
    }

GroupsFragment.java


    package com.example.snakemessenger;

    import android.content.DialogInterface;
    import android.os.Bundle;
    import android.text.TextUtils;
    import android.util.Log;
    import android.view.Gravity;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.EditText;
    import android.widget.LinearLayout;
    import android.widget.Toast;

    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.appcompat.app.AlertDialog;
    import androidx.fragment.app.Fragment;
    import androidx.recyclerview.widget.LinearLayoutManager;
    import androidx.recyclerview.widget.RecyclerView;

    import com.google.android.gms.tasks.OnCompleteListener;
    import com.google.android.gms.tasks.OnFailureListener;
    import com.google.android.gms.tasks.OnSuccessListener;
    import com.google.android.gms.tasks.Task;
    import com.google.android.material.floatingactionbutton.FloatingActionButton;
    import com.google.firebase.auth.FirebaseAuth;
    import com.google.firebase.auth.FirebaseUser;
    import com.google.firebase.firestore.FirebaseFirestore;
    import com.google.firebase.firestore.QueryDocumentSnapshot;
    import com.google.firebase.firestore.QuerySnapshot;

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;

    import de.hdodenhof.circleimageview.CircleImageView;


    /**
     * A simple {@link Fragment} subclass.
     */
    public class GroupsFragment extends Fragment {
        private View groupFragmentView;
        private RecyclerView mGroupsRecyclerView;
        private List<Group> groups;
        private FloatingActionButton mCreateGroup;
        private FirebaseAuth mAuth;
        private FirebaseUser currentUser;
        private FirebaseFirestore db;

        public GroupsFragment() {
            // Required empty public constructor
        }

        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            mAuth = FirebaseAuth.getInstance();
            currentUser = mAuth.getCurrentUser();
            db = FirebaseFirestore.getInstance();

            groups = new ArrayList<>();
            db.collection("users")
                    .document(currentUser.getUid())
                    .collection("groups")
                    .get()
                    .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                        @Override
                        public void onComplete(@NonNull Task<QuerySnapshot> task) {
                            if (task.isSuccessful()) {
                                for (QueryDocumentSnapshot document : task.getResult()) {
                                    groups.add(document.toObject(Group.class));
                                }

                                Toast.makeText(getActivity(), "Groups received successfully", Toast.LENGTH_SHORT).show();
                            } else {
                                Log.d(MainActivity.TAG, "Error getting document: ", task.getException());
                                Toast.makeText(getContext(), "Failed getting groups", Toast.LENGTH_SHORT).show();
                            }
                        }
                    });
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            // Inflate the layout for this fragment

            groupFragmentView = inflater.inflate(R.layout.fragment_groups, container, false);
            mCreateGroup = groupFragmentView.findViewById(R.id.create_group_btn);
            mCreateGroup.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    requestNewGroup();
                }
            });

            initView();
            setLayoutManager();
            setAdapter();

            return groupFragmentView;
        }

        private void initView() {
            mGroupsRecyclerView = groupFragmentView.findViewById(R.id.groups_recycler_view);
        }

        private void setLayoutManager() {
            RecyclerView.LayoutManager layoutManager =
                    new LinearLayoutManager(getActivity());
            mGroupsRecyclerView.setLayoutManager(layoutManager);
        }

        private void setAdapter() {
            GroupsAdapter groupsAdapter = new GroupsAdapter(getContext(), groups);
            mGroupsRecyclerView.setAdapter(groupsAdapter);
        }

        private void requestNewGroup() {
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AlertDialog);
            builder.setTitle("Enter group name");

            final LinearLayout layout = new LinearLayout(groupFragmentView.getContext());
            layout.setOrientation(LinearLayout.VERTICAL);

            final CircleImageView mGroupImage = new CircleImageView(groupFragmentView.getContext());
            mGroupImage.setId(R.id.set_group_image);
            mGroupImage.setImageResource(R.drawable.group_image);

            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(200, 200);
            layout.addView(mGroupImage);

            final EditText mGroupName = new EditText(groupFragmentView.getContext());
            mGroupName.setId(R.id.set_group_name);
            mGroupName.setGravity(Gravity.CENTER);
            mGroupName.setHint("Group name");
            layout.addView(mGroupName);

            builder.setView(layout);

            builder.setPositiveButton("Create", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    EditText mGroupName = layout.findViewById(R.id.set_group_name);
                    String groupName = mGroupName.getText().toString();

                    if (TextUtils.isEmpty(groupName)) {
                        Toast.makeText(getActivity(), "Please write the name of the group", Toast.LENGTH_SHORT).show();
                    } else {
                        createNewGroup(groupName);
                    }
                }
            });

            builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    dialogInterface.cancel();
                }
            });

            AlertDialog mCreateGroupDialog = builder.create();
            mCreateGroupDialog.show();
            mCreateGroupDialog.getWindow().setLayout(900, 1200);
        }

        private void createNewGroup(String groupName) {
            Map<String, Object> groupData = new HashMap<String, Object>();
            groupData.put("name", groupName);
            groupData.put("adminID", currentUser.getUid());
            groupData.put("users", Arrays.asList(currentUser.getUid()));
            db.collection("users")
                    .document(currentUser.getUid())
                    .collection("groups")
                    .document(groupName)
                    .set(groupData)
                    .addOnSuccessListener(new OnSuccessListener<Void>() {
                        @Override
                        public void onSuccess(Void aVoid) {
                            Toast.makeText(getContext(), "Group created successfully", Toast.LENGTH_SHORT).show();
                            sendUserToGroupConversation();
                        }
                    })
                    .addOnFailureListener(new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            Log.w(MainActivity.TAG, "Error adding document", e);
                            Toast.makeText(getActivity(), "There was an error processing the request.",
                                    Toast.LENGTH_SHORT).show();
                        }
                    });
        }

        private void sendUserToGroupConversation() {
            // TODO
        }
    }

Group.java


    package com.example.snakemessenger;
    import java.util.List;

    class Group {
        private String name;
        private String adminID;
        private List<String> users;

        public Group() {

        }

        public Group(String name, String adminID, List<String> users) {
            this.name = name;
            this.adminID = adminID;
            this.users = users;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getAdminID() {
            return adminID;
        }

        public void setAdminID(String adminID) {
            this.adminID = adminID;
        }

        public List<String> getUsers() {
            return users;
        }

        public void setUsers(List<String> users) {
            this.users = users;
        }
    }

GroupsAdapter.java


    package com.example.snakemessenger;

    import android.content.Context;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;

    import androidx.annotation.NonNull;
    import androidx.recyclerview.widget.RecyclerView;

    import java.util.List;

    class GroupsAdapter extends RecyclerView.Adapter<GroupsViewHolder> {
        private Context mContext;
        private List<Group> mGroups;

        public GroupsAdapter(Context mContext, List<Group> mGroups) {
            this.mContext = mContext;
            this.mGroups = mGroups;
        }

        @NonNull
        @Override
        public GroupsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View itemView = LayoutInflater.from(mContext)
                    .inflate(R.layout.group_item, parent, false);

            return new GroupsViewHolder(itemView);
        }

        @Override
        public void onBindViewHolder(@NonNull GroupsViewHolder holder, int position) {
            Group currentGroup = mGroups.get(position);

            holder.getTextViewName().setText(currentGroup.getName());
            holder.getCircleImageViewGroup().setImageResource(R.drawable.group_image);
        }

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

GroupsViewHolder.java


    package com.example.snakemessenger;

    import android.view.View;
    import android.widget.TextView;

    import androidx.annotation.NonNull;
    import androidx.recyclerview.widget.RecyclerView;

    import de.hdodenhof.circleimageview.CircleImageView;

    class GroupsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private TextView mGroupName;
        private CircleImageView mGroupImage;

        public GroupsViewHolder(@NonNull View itemView) {
            super(itemView);

            mGroupName = itemView.findViewById(R.id.group_name_item);
            mGroupImage = itemView.findViewById(R.id.group_image_item);
            itemView.setOnClickListener(this);
        }

        public TextView getTextViewName() {
            return mGroupName;
        }

        public CircleImageView getCircleImageViewGroup() {
            return mGroupImage;
        }

        @Override
        public void onClick(View view) {
            // TODO
        }
    }

And here are the XML files:

activity_main.xml


    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:orientation="vertical">

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appBarLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <include
                android:id="@+id/main_page_toolbar"
                layout="@layout/app_bar_layout">
            </include>

            <com.google.android.material.tabs.TabLayout
                android:id="@+id/main_tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:tabIndicatorColor="#FFFFFF"
                app:tabMode="fixed"
                app:tabGravity="fill">
            </com.google.android.material.tabs.TabLayout>
        </com.google.android.material.appbar.AppBarLayout>

        <androidx.viewpager.widget.ViewPager
            android:id="@+id/main_tabs_pager"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>

app_bar_layout.xml


    <?xml version="1.0" encoding="utf-8"?>
    <androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/main_app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimary"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    </androidx.appcompat.widget.Toolbar>

fragment_groups.xml


    <?xml version="1.0" encoding="utf-8"?>
    <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        tools:context=".GroupsFragment">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/groups_recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/create_group_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/fab_margin"
            app:fabSize="normal"
            android:backgroundTint="@color/colorPrimary"
            android:elevation="6dp"
            android:src="@drawable/ic_group_add_black_24dp"
            />
    </androidx.coordinatorlayout.widget.CoordinatorLayout>

group_item.xml


    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="5dp">

        <de.hdodenhof.circleimageview.CircleImageView
            android:layout_gravity="start"
            android:id="@+id/group_image_item"
            android:layout_width="56dp"
            android:layout_height="56dp"
            android:src="@drawable/group_image"/>

        <LinearLayout
            android:layout_marginStart="20dp"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/group_name_item"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:textSize="25sp"
                android:textStyle="bold"
                android:text="Contact Name"/>
        </LinearLayout>
    </LinearLayout>

I am sorry for the long post but I don't know how could I explain the problem in less words. I provided you all the code because I am not sure where the problem could be. As I said, the list is displayed only when the user clicks on the FAB in order to create a new group, after clicking on the EditText in order to pick a name. Please help. Thank you!

On your GroupFragment.java class

please layout provide orientation to recylerview

change this

RecyclerView.LayoutManager layoutManager =
                new LinearLayoutManager(getActivity());
        mGroupsRecyclerView.setLayoutManager(layoutManager);

into this

RecyclerView.LayoutManager layoutManager =
                new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL,false);
        mGroupsRecyclerView.setLayoutManager(layoutManager);

or you change this through XML

<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />

Lets start by doing this:

do you see this method:

setAdapter();

move it from this method:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
      ..........
      ..........

      setAdapter();

put it inside the get request to the database:

        groups = new ArrayList<>();
        db.collection("users")
                .document(currentUser.getUid())
                .collection("groups")
                .get()
                .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                    @Override
                    public void onComplete(@NonNull Task<QuerySnapshot> task) {
                        if (task.isSuccessful()) {
                            for (QueryDocumentSnapshot document : task.getResult()) {
                                groups.add(document.toObject(Group.class));


                            }
                            //here
                             setAdapter();

                            Toast.makeText(getActivity(), "Groups received successfully", Toast.LENGTH_SHORT).show();
                        } else {
                            Log.d(MainActivity.TAG, "Error getting document: ", task.getException());
                            Toast.makeText(getContext(), "Failed getting groups", Toast.LENGTH_SHORT).show();
                        }
                    }
                });

And then move the get request and everything with it from onCreate to onCreateView exactly where setAdapter() was.

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