3

I have a listview that displays data from firebase.The listview works prefectly.But now i would like to implement an onclick listener in my recycler adapter that then displays more details in a new activity. I have searched everywhere for a solution but cant figure it out.I know i am supposed to be using firebase UI.

This is the fragment thats displaying the data from firebase to listview using recycler adaper:

 public class CategoryFragment extends Fragment {
    private static final String TAG = "CategoryFragment";


    private RecyclerView recyclerView;
    private RecyclerView.Adapter adapter;
    // private Adapter adapter;
    private List<ListItem> listItems;
    private FirebaseDatabase mFirebaseDatabase;
    private DatabaseReference myRef;

    private AdapterView.OnItemSelectedListener listener;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
 View rootView =inflater.inflate(R.layout.recycler_view,container,false);
        listItems = new ArrayList<>();
         adapter = new CategoryRecycler(listItems,getContext());
        FirebaseDatabase.getInstance().setPersistenceEnabled(true);
        mFirebaseDatabase = FirebaseDatabase.getInstance();
        getFirebaseData();
        progressBar = rootView.findViewById(R.id.progressBar);
        recyclerView = rootView.findViewById(R.id.my_recycler_view);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        int tilePadding = getResources().getDimensionPixelSize(R.dimen.tile_padding);
        recyclerView.setPadding(tilePadding, tilePadding, tilePadding, tilePadding);
        recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 2));
        return recyclerView;

        //return rootView

    }

    private void getFirebaseData() {

        myRef = mFirebaseDatabase.getReference("Users");
        myRef.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                ListItem data = dataSnapshot.getValue(ListItem.class);
                listItems.add(data);
                recyclerView.setAdapter(adapter);


            }

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

            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

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

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {


            }
        });
    }
}

And heres my attempt to implement onclick inside recycler adapter :

public ViewHolder(LayoutInflater inflater, ViewGroup parent) {
    super(inflater.inflate(R.layout.category_item_tile, parent, false));
    username = itemView.findViewById(R.id.list_title);
    imageUrl = itemView.findViewById(R.id.list_avatar);
    itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            Context context = v.getContext();
          Intent pDetail = new Intent(context, ProfileDetail.class);
            pDetail.putExtra(ProfileDetail.EXTRA_POSITION, getAdapterPosition());
           context.startActivity(pDetail);
        }
    });
}

And this is my detailactivity:

public class ProfileDetail extends AppCompatActivity {
    private static final String TAG = "EXTRA_POSITION";
    public static final String EXTRA_POSITION = "profile_detail";
    private List<ListItem> listItems;
    private FirebaseDatabase mFirebaseDatabase;
    private DatabaseReference myRef;
    private TextView tvName, tvSummary, tvStatus, tvDetail;
    private ImageView ivImage;
    private ImageView ivImageCard;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.categorydetails);
        listItems = new ArrayList<>();
        ivImage = findViewById(R.id.userAvatar);
        ivImageCard = findViewById(R.id.header_img);
        tvName = findViewById(R.id.userProfileName);
        tvSummary = findViewById(R.id.ProfileSummary);
        tvStatus = findViewById(R.id.userStatus);
        tvDetail = findViewById(R.id.ProfileDetail);

    }

}

This is the full code for the Adapter:

    public class CategoryRecycler extends RecyclerView.Adapter<CategoryRecycler.ViewHolder> {
    private List<ListItem> listItems;
    private Context context;
    public CategoryRecycler(List<ListItem>listItems,Context context) {
        this.listItems = listItems;
        this.context =context;
    }
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new ViewHolder(LayoutInflater.from(parent.getContext()), parent);
    }
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        ListItem listItem = listItems.get(position);
        holder.username.setText(listItem.getUsername());
        //holder.summary.setText(listItem.getSummary());
        Picasso.with(context).load(listItem.getImageurl()).into(holder.imageUrl);
    }
    @Override
    public int getItemCount() {
        return listItems.size();
    }

public class ViewHolder extends RecyclerView.ViewHolder {
    public TextView username;
    public TextView summary;
    public ImageView imageUrl;


    public ViewHolder(LayoutInflater inflater, ViewGroup parent) {
        super(inflater.inflate(R.layout.category_item_tile, parent, false));
        username = itemView.findViewById(R.id.list_title);
        imageUrl = itemView.findViewById(R.id.list_avatar);
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Context context = v.getContext();
              Intent pDetail = new Intent(context, ProfileDetail.class);
                pDetail.putExtra(ProfileDetail.EXTRA_POSITION, getAdapterPosition());
               context.startActivity(pDetail);
            }
        });
    }
}


}

onBindViewHolder holder calls:

holder.username.setText(listItem.getUsername());
Picasso.with(context).load(listItem.getImageurl()).into(holder.imageUrl);

context is required for my picasso image management.

//declaring context

private Context context;

//using context

this.context =context;

But if you know any other way without using context on image,i would appreciate the suggestions.

RiM
  • 45
  • 7
  • You can use the title in your adapter position to get the data from firebase related to the info you want to display in the next activity. – HB. Mar 18 '18 at 18:26
  • could you show your database schema? – Levi Moreira Mar 18 '18 at 19:18
  • Levi,i am using firebase and dont have enough points to upload an image here.But the root node is "Users"...so its something like this:Users:[{id,username,summary]} – RiM Mar 18 '18 at 22:28
  • H.Brooks..i tried,nothing displayed – RiM Mar 18 '18 at 22:35

1 Answers1

2

The best way to implement the onClick action in your adapter is to create an interface in your adapter and link it to your fragment by making it implement such interface, and then in the fragment you launch the new activity. I've refactored your adapter as such:

public class SpecialAdapter extends RecyclerView.Adapter<SpecialAdapter.SpecialViewHolder> {

    List<ListItem> items;
    OnItemClickListener callback;

    public SpecialAdapter(List<ListItem> items, OnItemClickListener callback) {
        this.items = items;
        this.callback = callback;
    }

    @NonNull
    @Override
    public SpecialViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.category_item_tile, parent, false);
        return new SpecialViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull SpecialViewHolder holder, int position) {
        ListItem listItem = items.get(position);
        holder.username.setText(listItem.getUsername());
        holder.imageUrl.setVisibility(View.VISIBLE);
    }

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

    class SpecialViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        public TextView username;
        public ImageView imageUrl;

        public SpecialViewHolder(View itemView) {
            super(itemView);

            username = itemView.findViewById(R.id.list_title);
            imageUrl = itemView.findViewById(R.id.list_avatar);

            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            callback.onItemClick(getAdapterPosition());
        }
    }

    public interface OnItemClickListener {
        void onItemClick(int pos);
    }
}

I've refactored your fragment:

public class CategoryFragment extends Fragment implements SpecialAdapter.OnItemClickListener {
    private static final String TAG = "CategoryFragment";


    private RecyclerView recyclerView;
    private SpecialAdapter adapter;
    // private Adapter adapter;
    private List<ListItem> listItems;
    private FirebaseDatabase mFirebaseDatabase;
    private DatabaseReference myRef;

    private AdapterView.OnItemSelectedListener listener;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView =inflater.inflate(R.layout.recycler_view,container,false);
        listItems = new ArrayList<>();
        adapter = new SpecialAdapter(listItems, this);
        FirebaseDatabase.getInstance().setPersistenceEnabled(true);
        mFirebaseDatabase = FirebaseDatabase.getInstance();
        getFirebaseData();
        progressBar = rootView.findViewById(R.id.progressBar);
        recyclerView = rootView.findViewById(R.id.my_recycler_view);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        int tilePadding = getResources().getDimensionPixelSize(R.dimen.tile_padding);
        recyclerView.setPadding(tilePadding, tilePadding, tilePadding, tilePadding);
        recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 2));
        recyclerView.setAdapter(adapter);
        return recyclerView;

        //return rootView

    }

    private void getFirebaseData() {

        myRef = mFirebaseDatabase.getReference("Users");
        myRef.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                ListItem data = dataSnapshot.getValue(ListItem.class);
                listItems.add(data);
                adapter.notifyDataSetChanged();

            }

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

            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

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

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {


            }
        });
    }

    @Override
    public void onItemClick(int pos) {

        Intent pDetail = new Intent(this.getContext(), ProfileDetail.class);

        ListItem item = listItems.get(pos);
        pDetail.putExtra(ProfileDetail.EXTRA_POSITION, item.getUserid());
        startActivity(pDetail);
    }
}

I suppose your ListItem looks like this:

public class ListItem {

    private String details;
    private String imageurl;
    private String status;
    private String summary;
    private String userid;
    private String username;

    public String getDetails() {
        return details;
    }

    public void setDetails(String details) {
        this.details = details;
    }

    public String getImageurl() {
        return imageurl;
    }

    public void setImageurl(String imageurl) {
        this.imageurl = imageurl;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getSummary() {
        return summary;
    }

    public void setSummary(String summary) {
        this.summary = summary;
    }

    public String getUserid() {
        return userid;
    }

    public void setUserid(String userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

Finally in your detail activity you can simply get this id and read the database:

public class ProfileDetail extends AppCompatActivity {
    private static final String TAG = "EXTRA_POSITION";
    public static final String EXTRA_POSITION = "profile_detail";
    private List<ListItem> listItems;
    private FirebaseDatabase mFirebaseDatabase;
    private DatabaseReference myRef;
    private TextView tvName, tvSummary, tvStatus, tvDetail;
    private ImageView ivImage;
    private ImageView ivImageCard;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.categorydetails);
        listItems = new ArrayList<>();
        ivImage = findViewById(R.id.userAvatar);
        ivImageCard = findViewById(R.id.header_img);
        tvName = findViewById(R.id.userProfileName);
        tvSummary = findViewById(R.id.ProfileSummary);
        tvStatus = findViewById(R.id.userStatus);
        tvDetail = findViewById(R.id.ProfileDetail);

        String userid = getIntent().getExtras().getString(ProfileDetail.EXTRA_POSITION);
        mFirebaseDatabase = FirebaseDatabase.getInstance();
        myRef = mFirebaseDatabase.getReference("Users");
        myRef.child(userid).addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                ListItem item = (ListItem) dataSnapshot.getValue(ListItem.class);
                if(item!=null){
                   tvName.setText(item.getUsername());
                }

            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {

            }
        });


    }

}

If you don't want to make a request in the details activity you could make ListItem (parcelable) serializable and put it straight as an intent extra. See here. Passing data through intent using Serializable

RiM
  • 45
  • 7
Levi Moreira
  • 11,917
  • 4
  • 32
  • 46
  • I am trying to implement your approach.But the data is no longer displaying.Probably an issue in SpecialAdapter.Then onclick item,i am getting the error:Null firebase object reference – RiM Mar 18 '18 at 20:11
  • To display the items properly you need to complete the onBindViewHolder method, did you do that? – Levi Moreira Mar 18 '18 at 20:16
  • Also add mFirebaseDatabase = FirebaseDatabase.getInstance(); to your detail activity onCreate(). – Levi Moreira Mar 18 '18 at 20:28
  • your SpecialAdapter is not working.When i set the holder.username and imageurl,i get the error:cannot find imageurl and username. – RiM Mar 18 '18 at 22:21
  • Seems the variables arent being picked up properly – RiM Mar 18 '18 at 22:22
  • ProfileDetail error:Attempt to invoke virtual method 'com.google.firebase.database.DatabaseReference com.google.firebase.database.FirebaseDatabase.getReference(java.lang.String)' on a null object refere – RiM Mar 18 '18 at 22:46
  • sorry you were having problems, I've fixed the problems with both the adapter and the ProfileDetail activity – Levi Moreira Mar 19 '18 at 00:07
  • And even after multiple attempts..i am still getting a null id pointer exception.:"Can't pass null for argument 'pathString' in child()" – RiM Mar 19 '18 at 10:45
  • Humm, I might have misunderstood your schema, could you post a picture of it? – Levi Moreira Mar 19 '18 at 10:51
  • I have added a link to an image displaying the firebase data schema.The data is inserted into firebase by the user via:profileactivity.Then saved to firebase. – RiM Mar 19 '18 at 11:05
  • I am also using picasso for images in my adapter as follows:Picasso.with(context).load(listItem.getImageurl()).into(holder.imageUrl);,i will add this to my question. – RiM Mar 19 '18 at 11:08
  • Ok, the problem was that the ID passed to the DetailsActivity was null, no i see you put the id in your schema so you can access it through getUserid(). I've edited the onClick function and the onDataChanged in the main fragment – Levi Moreira Mar 19 '18 at 11:11
  • btw, are you seeing the data in the list fragment? – Levi Moreira Mar 19 '18 at 11:14
  • no errors or warning this time.But detail activity is not displaying any data. – RiM Mar 19 '18 at 11:26
  • In the detail activity I didn't fill the data anywhere on purpose, just received it on the onDataChange() callback, you can see there is a "item" object in there, it must contain the specific data of one user. I've edited it so you can see how to do fill one of the fields and do the rest :) – Levi Moreira Mar 19 '18 at 11:30
  • Works perfectly.Thank you for your help,i really appreciate it. – RiM Mar 19 '18 at 11:38
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/167105/discussion-between-rim-and-levi-albuquerque). – RiM Mar 19 '18 at 16:49