My database is saving as follows:
On another activity, I will use the "temas" values to display as tags with a horizontal RecyclerView. My problem now is that I can only retrieve the element 0 of all other elements. (In this case, the "Feminismo"). I am trying to use an incrementor inside the onBindViewHolder so it can change from 0 to 1 to 2 and on and on, but it doesn't seem to be working.
This is my ArticleActivity:
public class ArticlesActivity extends AppCompatActivity {
private TextView txtTitle, txtAutor, txtDate;
private RecyclerView articleTags;
private String articleKey;
private DatabaseReference articleRef, articleTagRef;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_articles);
articleKey = getIntent().getStringExtra("articleKey");
articleRef = FirebaseDatabase.getInstance().getReference().child("Artigos").child(articleKey);
articleTagRef = articleRef.child("temas");
initializeFields();
articleTags = findViewById(R.id.articlesActRecyclerView);
articleTags.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
}
#Override
protected void onStart() {
super.onStart();
FirebaseRecyclerOptions options = new FirebaseRecyclerOptions.Builder<String>()
.setQuery(articleTagRef, String.class)
.build();
FirebaseRecyclerAdapter<String, ArticleViewHolder> adapter = new FirebaseRecyclerAdapter<String, ArticleViewHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull final ArticleViewHolder holder, final int position, #NonNull String model) {
Toast.makeText(ArticlesActivity.this, Integer.toString(position), Toast.LENGTH_SHORT).show();
articleTagRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
if (dataSnapshot.hasChildren()) {
holder.tags.setText(dataSnapshot.child(Integer.toString(position)).getValue().toString());
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
#NonNull
#Override
public ArticleViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_article_tags, viewGroup, false);
ArticleViewHolder viewHolder = new ArticleViewHolder(view);
return viewHolder;
}
};
articleTags.setAdapter(adapter);
adapter.startListening();
}
public class ArticleViewHolder extends RecyclerView.ViewHolder {
private TextView tags;
public ArticleViewHolder(#NonNull View itemView) {
super(itemView);
tags = itemView.findViewById(R.id.recyclerArticleTag);
}
}
private void initializeFields() {
txtTitle = findViewById(R.id.txtArticleActTitle);
txtAutor = findViewById(R.id.txtArticleActAutor);
txtDate = findViewById(R.id.txtArticleActDate);
}
This is the Article xml (pretty standard one):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="6dp"
tools:context=".ArticlesActivity">
<TextView
android:id="#+id/txtArticleActTitle"
android:textSize="26sp"
android:textStyle="bold"
android:text="Article Title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/txtArticleActAutor"
android:textSize="24sp"
android:textStyle="bold"
android:layout_below="#id/txtArticleActTitle"
android:text="Article Autor"
android:layout_marginTop="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/txtArticleActDate"
android:text="Article Date"
android:layout_below="#id/txtArticleActAutor"
android:textSize="20sp"
android:layout_marginTop="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v7.widget.RecyclerView
android:id="#+id/articlesActRecyclerView"
android:layout_below="#id/txtArticleActDate"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
Can anyone suggest how this can be done?
You can loop over the child nodes of a snapshot with DataSnapshot.getChildren():
articleRef = FirebaseDatabase.getInstance().getReference().child("Artigos").child(articleKey);
articleTagRef = articleRef.child("temas");
articleTagRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot childSnapshot: dataSnapshot.getChildren()) {
System.out.println(childSnapshot.getKey()); // prints 0, 1, etc
System.out.println(childSnapshot.getValue(String.class)); // prints "Feminismo", "Racismo", etc
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
I've made some changes to the Recycler methods, the new one:
#Override
protected void onStart() {
super.onStart();
FirebaseRecyclerOptions options = new FirebaseRecyclerOptions.Builder<Tags>()
.setQuery(articleTagRef, Tags.class)
.build();
FirebaseRecyclerAdapter<Tags, ArticleViewHolder> adapter = new FirebaseRecyclerAdapter<Tags, ArticleViewHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull final ArticleViewHolder holder, final int position, #NonNull Tags model) {
String tagKey = getRef(position).getKey();
articleTagRef.child(tagKey).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
if (dataSnapshot.hasChild("tagName")) {
String tagName = dataSnapshot.child("tagName").getValue().toString();
holder.tags.setTextSize(20);
holder.tags.setPadding(8,8,8,8);
holder.tags.setText(tagName);
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
#NonNull
#Override
public ArticleViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_article_tags, viewGroup, false);
ArticleViewHolder viewHolder = new ArticleViewHolder(view);
return viewHolder;
}
};
articleTags.setAdapter(adapter);
adapter.startListening();
}
I've created too a Tags class:
public class Tags {
private String tagName;
public Tags() {
}
public Tags(String tagName) {
this.tagName = tagName;
}
public String getTagName() {
return tagName;
}
public void setTagName(String tagName) {
this.tagName = tagName;
}
}
Changed too the recycler_article_tags.xml to wrap_content on the RelativeLayout and on the TextView:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:background="#drawable/buttons"
android:id="#+id/recyclerArticleTag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="6dp" />
</RelativeLayout>
And finally, the database is now like this:
Related
I'm trying to set up a RecyclerView on my HomeFragment and I'm at the point where I'm setting up the adapter. How do I set the image in my onBindViewHolder function? Do I need to change the class inside the Glide.with() in my onBindViewHolder function? Also, for the DatabaseReference, how do I get the actual uid for the reference? Thank you!
One of my Database References
category.xml
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/category_view_background">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/categoryImageView"
android:layout_width="420dp"
android:layout_height="676dp"
android:background="#drawable/image_rounded_top_corners"
android:scaleType="fitXY"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/profile_photo_"
android:contentDescription="#string/category_image" />
<TextView
android:id="#+id/categoryNameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/category_name"
android:textAlignment="center"
android:textColor="#color/black"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/categoryImageView" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
category.java
public class category {
private String categoryImage;
private URL categoryImageUrl;
private String category;
private String uid;
public category() {}
public String getCategoryImage() {
return categoryImage;
}
public void setCategoryImage(String categoryImage) {
this.categoryImage = categoryImage;
}
public URL getCategoryImageUrl() { return categoryImageUrl; }
public void setCategoryImageUrl(URL categoryImageUrl) { this.categoryImageUrl = categoryImageUrl; }
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String setCategory() {
return "";
}
}
categoryAdapter.java
public class categoryAdapter extends FirebaseRecyclerAdapter<category, categoryAdapter.categoryViewholder>{
public categoryAdapter(#NonNull FirebaseRecyclerOptions<category> options) {
super(options);
}
#Override protected void onBindViewHolder(#NonNull categoryAdapter.categoryViewholder holder, int position, #NonNull category model) {
holder.category.setText(model.setCategory());
DatabaseReference url_db = FirebaseDatabase.getInstance().getReference().child("job-category").child(model.getUid()).child("categoryImage");
url_db.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
String image = snapshot.child("categoryImage").getValue(String.class);
Glide.with(this, category.class).load(image).into(holder.categoryImage);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
#NonNull #Override public categoryViewholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.category, parent, false);
return new categoryViewholder(view);
}
static class categoryViewholder extends RecyclerView.ViewHolder {
TextView category;
Image categoryImage;
public categoryViewholder(#NonNull View itemView) {
super(itemView);
category = itemView.findViewById(R.id.categoryNameTextView);
categoryImage = itemView.findViewById(R.id.categoryImageView);
}
}
}
In your category class you should use have url of image and the type of categoryImage should be String.
then to load and show image in your onBindViewHolder use Glide
I`ve followed a tutorial to create a recyclerview for my app
I doesnt work, in that it only occasionally shows anything in the view
There doesnt appear to be any rime or reason at all...
The layout that is recycled
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parent_layout"
android:layout_width="match_parent"
android:layout_height="80dp">
<TextView
android:id="#+id/text_value"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="Test"
android:textColor="#000000"
android:textSize="18sp" />
</LinearLayout>
The xml with the recyclerview in it
<?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="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/recycler_view_accounts"/>
</LinearLayout>
The RecyclerViewAdapter class
public class RecyclerViewAdapterAccounts extends RecyclerView.Adapter<RecyclerViewAdapterAccounts.ViewHolder> {
private ArrayList<String> mlist;
private Context mContext;
public RecyclerViewAdapterAccounts(ArrayList<String> list, Context context) {
mlist = list;
mContext = context;
}
#NonNull
#Override
public RecyclerViewAdapterAccounts.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_listitem, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull RecyclerViewAdapterAccounts.ViewHolder holder, final int position) {
holder.accountName.setText(mlist.get(position));
holder.parent_layout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext, mlist.get(position), Toast.LENGTH_LONG).show();
}
});
}
#Override
public int getItemCount() {
return mlist.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView accountName;
LinearLayout parent_layout;
public ViewHolder(#NonNull View itemView) {
super(itemView);
accountName = itemView.findViewById(R.id.text_value);
parent_layout = itemView.findViewById(R.id.parent_layout);
}
}
}
The relevant onCreate activity
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage_accounts);
initAccounts();
RecyclerView recyclerView = findViewById(R.id.recycler_view_accounts);
RecyclerViewAdapterAccounts adapter = new RecyclerViewAdapterAccounts(accounts, this);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter.notifyDataSetChanged();
}
Although not shown, I can test that initAccounts() is filling the array as it should
I can also see that onCreateViewHolder and onBindViewHolder is not being called
Any suggestions???
edit:
heres the initAccounts
private void initAccounts() {
DatabaseReference userRef =
FirebaseDatabase.getInstance().getReference().child("users");
userRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
Iterator<DataSnapshot> children = snapshot.getChildren().iterator();
while (children.hasNext()) {
DataSnapshot child = children.next();
String[] parts = child.getValue().toString().split(",");
String role = parts[0].replace('{', ' ');
String msg = child.getKey() + ": " + role;
accounts.add(msg);
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
The problem here is you are adding object to accounts but not notifying the Adapter about the change . Make the following changes to your code .
For setting adapter inside onCreate() use Only .
private RecyclerViewAdapterAccounts adapter;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage_accounts);
initAccounts();
RecyclerView recyclerView = findViewById(R.id.recycler_view_accounts);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new RecyclerViewAdapterAccounts(accounts, this);
recyclerView.setAdapter(adapter);
}
Now create a method in RecyclerViewAdapterAccounts to add elements in adapter ;
public void addAll(ArratList<String> list){
mlist.clear();// To clear list if thats the case
mlist.addAll(list);
notifyDataSetChanged();
}
Now you can add all elements in one go to adapter .
public void onDataChange(#NonNull DataSnapshot snapshot) {
ArrayList<String> dataList=new ArrayList<>();
Iterator<DataSnapshot> children = snapshot.getChildren().iterator();
while (children.hasNext()) {
DataSnapshot child = children.next();
String[] parts = child.getValue().toString().split(",");
String role = parts[0].replace('{', ' ');
String msg = child.getKey() + ": " + role;
dataList.add(msg);
}
adapter.addAll(dataList);
}
I have been trying to get some padding between the Firebase RecyclerView items.I tried using ItemDecoration, but it works only with the first item of the RecyclerView.
Would be a great help if you could point out my mistake.
This is what I get
What I am trying to get is a layout where there is a padding between each recyclerview item so that I could see the shadow of each item.
Here's my Java code where I use the Firebase RecyclerView to load items.
public class CommunityFragment extends Fragment {
private static final String KEY_RECYCLER_STATE = "";
private RecyclerView mConvList;
private DatabaseReference mConvDatabase;
private DatabaseReference mUsersDatabase;
private DatabaseReference mMessageDatabase;
private Bundle mBundleRecyclerViewState;
private Parcelable mListState;
private static final String TAG = "ChatsFragment";
public CommunityFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View mMainView = inflater.inflate(R.layout.fragment_chats, container, false);
mConvList = (RecyclerView) mMainView.findViewById(R.id.conv_list);
FirebaseAuth mAuth = FirebaseAuth.getInstance();
String mCurrent_user_id = Objects.requireNonNull(mAuth.getCurrentUser()).getUid();
mConvDatabase = FirebaseDatabase.getInstance().getReference().child("Chat").child(mCurrent_user_id);
mConvDatabase.keepSynced(true);
mUsersDatabase = FirebaseDatabase.getInstance().getReference().child("Users");
mUsersDatabase.keepSynced(true);
mMessageDatabase = FirebaseDatabase.getInstance().getReference().child("messages").child(mCurrent_user_id);
mMessageDatabase.keepSynced(true);
mConvList = mMainView.findViewById(R.id.conv_list);
mConvList.setHasFixedSize(true);
mConvList.setLayoutManager(new LinearLayoutManager(getContext()));
// Inflate the layout for this fragment
return mMainView;
}
#Override
public void onStart() {
super.onStart();
Query conversationQuery = mConvDatabase.orderByChild("timestamp");
FirebaseRecyclerOptions<Conv> friendsFirebaseRecyclerOptions = new FirebaseRecyclerOptions.Builder<Conv>()
.setQuery(conversationQuery, Conv.class)
.build();
FirebaseRecyclerAdapter<Conv, ConvViewHolder> firebaseConvAdapter = new FirebaseRecyclerAdapter<Conv, ConvViewHolder>(friendsFirebaseRecyclerOptions) {
#Override
protected void onBindViewHolder(#NonNull final ConvViewHolder holder, int position, #NonNull final Conv model) {
final String list_user_id = getRef(position).getKey();
assert list_user_id != null;
Query lastMessageQuery = mMessageDatabase.child(list_user_id).limitToLast(1);
lastMessageQuery.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
String data = Objects.requireNonNull(dataSnapshot.child("message").getValue()).toString();
String seen_ = Objects.requireNonNull(dataSnapshot.child("seen").getValue()).toString();
holder.setMessage(data, Boolean.parseBoolean(seen_));
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
mUsersDatabase.child(list_user_id).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
final String userName = dataSnapshot.child("Name").getValue().toString();
holder.setName(userName);
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent chatIntent = new Intent(getContext(), ChatActivity.class);
chatIntent.putExtra("user_id", list_user_id);
chatIntent.putExtra("user_name",userName);
startActivity(chatIntent);
}
});
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
#NonNull
#Override
public ConvViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.single_q_layout, viewGroup, false);
return new ConvViewHolder(view);
}
};
mConvList.addItemDecoration(new PaddingItemDecoration(200));
firebaseConvAdapter.startListening();
mConvList.setAdapter(firebaseConvAdapter);
}
public void onPause(){
super.onPause();
mBundleRecyclerViewState = new Bundle();
mListState = mConvList.getLayoutManager().onSaveInstanceState();
mBundleRecyclerViewState.putParcelable(KEY_RECYCLER_STATE, mListState);
}
public void onResume(){
super.onResume();
if (mBundleRecyclerViewState != null) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mListState = mBundleRecyclerViewState.getParcelable(KEY_RECYCLER_STATE);
mConvList.getLayoutManager().onRestoreInstanceState(mListState);
}
}, 50);
}
mConvList.setLayoutManager(new LinearLayoutManager(getContext()));
}
public class ConvViewHolder extends RecyclerView.ViewHolder {
View mView;
public ConvViewHolder(#NonNull View itemView) {
super(itemView);
mView = itemView;
}
public void setName(String name){
TextView userNameView = (TextView) mView.findViewById(R.id.question_title);
userNameView.setText(name);
}
public void setMessage(String message, boolean isSeen) {
TextView userStatusView = (TextView) mView.findViewById(R.id.question_catergory);
//Log.i("textData", "State is " + isSeen);
if(!isSeen){
userStatusView.setText("New Message");
userStatusView.setTypeface(userStatusView.getTypeface(), Typeface.BOLD);
}else{
userStatusView.setText("No New Messages");
userStatusView.setTypeface(userStatusView.getTypeface(), Typeface.NORMAL);
}
}
}
}
This is my PaddingItemDecoration class.
public class PaddingItemDecoration extends RecyclerView.ItemDecoration {
private final int size;
public PaddingItemDecoration(int size) {
this.size = size;
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
// Apply offset only to first item
if (parent.getChildAdapterPosition(view) == 0) {
outRect.top += size;
outRect.bottom += size;
}
}
And this is the XML file of my Adapter I use to load my RecyclerView items.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/relativeLayout3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/message_text_background"
android:elevation="4dp"
android:padding="10dp">
<TextView
android:id="#+id/question_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:padding="10dp"
android:text="Display Name"
android:textColor="#020202"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/question_catergory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:text="catergory"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/question_title" />
<TextView
android:id="#+id/counter_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="counter"
app:layout_constraintEnd_toStartOf="#+id/answers_given"
app:layout_constraintTop_toBottomOf="#+id/question_title" />
<TextView
android:id="#+id/answers_given"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="answers"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/question_title" />
</androidx.constraintlayout.widget.ConstraintLayout>
Instead of using this PaddingItemDecoration, just add margin around your XML file of adapter.
As I can see you already added padding to the parent constraint layout (try margin), so just remove your decoration class and just try simply printing list to screen, you should be good to go.
Below image is an example of ListItem layout for adapter from my app, yours also should look like this one.
Try to add margin 10dp
If you aren't able to do so, try implementing the way shown in this article.
I am trying to implement multiple view or so called heterogeneous recyclerview using firebase realtime database. I found a example of heterogeneous recyclerview implementation on github.
I followed this to implement on firebase. But my code didn't work.
fragment_search.java (one of the fragment in my tabbed view) This is my fragment file which contains the main recyclerview which will hold multiple views
public class fragment_search extends Fragment {
RecyclerView recyclerViewSearch;
private static SingleSearchBannerModel singleSearchBannerModel;
private static SingleSearchType1Model singleSearchType1Model;
private static DatabaseReference databaseReference;
private List<Object> objects = new ArrayList<>();
private static SearchMainAdapter searchMainAdapter;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_search,null);
//Recycler view initialized and Properties Set.
recyclerViewSearch = view.findViewById(R.id.recyclerview_search);
recyclerViewSearch.setHasFixedSize(true);
recyclerViewSearch.setLayoutManager(new LinearLayoutManager(getContext()));
databaseReference = FirebaseDatabase.getInstance().getReference();
return view;
}
private List<Object> getObjects(){
objects.addAll(getBannerData());
objects.addAll(getType1Data());
return objects;
}
public static List<SingleSearchBannerModel> getBannerData(){
final List<SingleSearchBannerModel> singleSearchBannerModelList = new ArrayList<>();
databaseReference.child("Featured").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
singleSearchBannerModel = dataSnapshot.getValue(SingleSearchBannerModel.class);
singleSearchBannerModelList.add(singleSearchBannerModel);
searchMainAdapter.notifyDataSetChanged();
System.out.println("this is image in getBanner == "+singleSearchBannerModel.getImage());
}
#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) {
}
});
return singleSearchBannerModelList;
}
public static List<SingleSearchType1Model> getType1Data(){
final List<SingleSearchType1Model> singleSearchType1ModelList = new ArrayList<>();
databaseReference.child("Featured").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
singleSearchType1Model = dataSnapshot.getValue(SingleSearchType1Model.class);
singleSearchType1ModelList.add(singleSearchType1Model);
searchMainAdapter.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) {
}
});
return singleSearchType1ModelList;
}
#Override
public void onStart() {
super.onStart();
searchMainAdapter = new SearchMainAdapter(getContext(),getObjects());
System.out.println("this is getObject == "+getObjects());
System.out.println("this is getBanner == "+getBannerData());
System.out.println("this is getType1 == "+getType1Data());
recyclerViewSearch.setAdapter(searchMainAdapter);
}
}
fragment_home.xml this is the xml file of fragment which contains one recyclerview (My main recyclerview).
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview_search"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
Model files
*I have two model files. Basically getter and setters *
SingleSearchBannerModel.java My firsts model file.
public class SingleSearchBannerModel {
String Image;
public SingleSearchBannerModel() {
}
public SingleSearchBannerModel(String image) {
Image = image;
}
public String getImage() {
return Image;
}
public void setImage(String image) {
Image = image;
}
}
SingleSearchType1Model.java This is my second model file.
public class SingleSearchType1Model {
String Image;
public SingleSearchType1Model() {
}
public SingleSearchType1Model(String image) {
Image = image;
}
public String getImage() {
return Image;
}
public void setImage(String image) {
Image = image;
}
}
RecyclerView Adapters
Adapters are the ones used to set the value to the recyclerView. I have three Adapters as I'm trying to insert two views in one recyclerview. One is the MainAdapter which combines the other two Adapters. The MainAdapter is set to the the recyclerview of fragment_search.java (my main fragment)
SearchBannerAdapter.java This adapter is to generate the required layout file and populate the inflated layout file.
public class SearchBannerAdapter extends RecyclerView.Adapter<SearchBannerAdapter.TaskViewHolder>{
List<SingleSearchBannerModel> data;
public SearchBannerAdapter(List<SingleSearchBannerModel> data) {
this.data = data;
}
#NonNull
#Override
public TaskViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.search_single_banners,parent,false);
return new TaskViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull TaskViewHolder holder, int position) {
SingleSearchBannerModel singleSearchBannerModel = data.get(position);
Picasso.get().load(singleSearchBannerModel.getImage()).fit().into(holder.imageView);
}
#Override
public int getItemCount() {
return data.size();
}
public static class TaskViewHolder extends RecyclerView.ViewHolder{
View mView;
ImageView imageView;
public TaskViewHolder(View itemView) {
super(itemView);
mView = itemView;
imageView = mView.findViewById(R.id.search_banner_imgView);
}
}
}
search_single_banners.xml The xml file which is inflated in SearchBannerAdapter.java
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.v7.widget.CardView
android:layout_width="290dp"
android:layout_height="160dp"
android:id="#+id/longCard"
android:layout_marginTop="50dp"
android:layout_marginEnd="20dp"
app:cardCornerRadius="20sp"
app:cardElevation="#dimen/ten"
app:cardPreventCornerOverlap="false"
android:layout_centerHorizontal="true">
<ImageView
android:id="#+id/search_banner_imgView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/test1"/>
</android.support.v7.widget.CardView>
SearchType1Adapter.java This is the second adapter which inflates search_type_1.xml
public class SearchType1Adapter extends RecyclerView.Adapter<SearchType1Adapter.TaskViewHolder>{
List<SingleSearchType1Model> data;
public SearchType1Adapter(List<SingleSearchType1Model> data) {
this.data = data;
}
#NonNull
#Override
public TaskViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.search_type_1,parent,false);
return new TaskViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull TaskViewHolder holder, int position) {
SingleSearchType1Model singleSearchType1Model = data.get(position);
Picasso.get().load(singleSearchType1Model.getImage()).fit().into(holder.imageView);
}
#Override
public int getItemCount() {
return data.size();
}
public static class TaskViewHolder extends RecyclerView.ViewHolder{
View mView;
ImageView imageView;
public TaskViewHolder(View itemView) {
super(itemView);
mView = itemView;
imageView = mView.findViewById(R.id.search_single_type_1_imgView);
}
}
}
search_type_1.xml The file that is inflated in SearchType1Adapter.java
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.v7.widget.CardView
android:layout_width="290dp"
android:layout_height="160dp"
android:id="#+id/longCard"
android:layout_marginTop="50dp"
android:layout_marginEnd="20dp"
app:cardCornerRadius="20sp"
app:cardElevation="#dimen/ten"
app:cardPreventCornerOverlap="false"
android:layout_centerHorizontal="true">
<ImageView
android:id="#+id/search_single_type_1_imgView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/test1"/>
</android.support.v7.widget.CardView>
SearchMainAdapter.java This is the main adapter which is used in fragment_search to populate the main recyclerview.
public class SearchMainAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private List<Object> items;
private final int BANNER = 1;
private final int TYPE1 = 2;
public SearchMainAdapter(Context context, List<Object> items) {
this.context = context;
this.items = items;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view;
RecyclerView.ViewHolder holder;
switch (viewType){
case BANNER:
view = inflater.inflate(R.layout.search_banners,parent,false);
holder = new BannerViewHolder(view);
break;
case TYPE1:
view = inflater.inflate(R.layout.search_type_1,parent,false);
holder = new Type1ViewHolder(view);
break;
default:
view = inflater.inflate(R.layout.search_banners,parent,false);
holder = new Type1ViewHolder(view);
break;
}
return holder;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
if(holder.getItemViewType() == BANNER)
BannerView((BannerViewHolder) holder);
else if (holder.getItemViewType() == TYPE1)
Type1View((Type1ViewHolder) holder);
}
private void BannerView(BannerViewHolder holder){
SearchBannerAdapter searchBannerAdapter = new SearchBannerAdapter(getBannerData());
holder.recyclerView.setLayoutManager(new LinearLayoutManager(context,LinearLayoutManager.HORIZONTAL,false));
holder.recyclerView.setAdapter(searchBannerAdapter);
}
private void Type1View(Type1ViewHolder holder){
SearchType1Adapter searchType1Adapter = new SearchType1Adapter(getType1Data());
holder.recyclerView.setLayoutManager(new LinearLayoutManager(context,LinearLayoutManager.HORIZONTAL,false));
holder.recyclerView.setAdapter(searchType1Adapter);
}
#Override
public int getItemCount() {
return items.size();
}
#Override
public int getItemViewType(int position) {
if (items.get(position) instanceof SingleSearchBannerModel)
return BANNER;
if (items.get(position) instanceof SingleSearchType1Model)
return TYPE1;
return -1;
}
public static class BannerViewHolder extends RecyclerView.ViewHolder{
RecyclerView recyclerView;
public BannerViewHolder(View itemView) {
super(itemView);
recyclerView = itemView.findViewById(R.id.recyclerview_banners);
}
}
public static class Type1ViewHolder extends RecyclerView.ViewHolder{
RecyclerView recyclerView;
public Type1ViewHolder(View itemView) {
super(itemView);
recyclerView = itemView.findViewById(R.id.recyclerview_search_type_1);
}
}
}
search_banners.xml and search_type_1.xml have the same code
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview_banners"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
The above code is not working. I want to know where i went wrong. My main objective is to make an app something like google play store or hotstar (In terms of UI). So I assume they are using single recyclerview and multiple view within it.
Thanks for help.
I know this answer is a bit late but it might still help someone in future. Here is how I achived the goal
public class HeteroActivity extends AppCompatActivity {
private ArrayList<Object> objects = new ArrayList<>();
private static final String TAG = HeteroActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hetero);
RecyclerView recyclerView = findViewById(R.id.recycler_View);
MainAdapter adapter = new MainAdapter(this, getObject());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
private ArrayList<Object> getObject() {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Data");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
objects.add(getVerticalData(dataSnapshot).get(0));
objects.add(getHorizontalData(dataSnapshot).get(0));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
return objects;
}
public static ArrayList<SingleVertical> getVerticalData(DataSnapshot dataSnapshot) {
ArrayList<SingleVertical> singleVerticals = new ArrayList<>();
for (DataSnapshot snapshot : dataSnapshot.getChildren()){
singleVerticals.add(new SingleVertical(
snapshot.child("price").getValue(String.class),
snapshot.child("location").getValue(String.class),
snapshot.child("image").getValue(String.class)
));
Log.e(TAG,"Text loaded");
}
return singleVerticals;
}
public static ArrayList<SingleHorizontal> getHorizontalData(DataSnapshot dataSnapshot) {
ArrayList<SingleHorizontal> singleHorizontals = new ArrayList<>();
for (DataSnapshot snapshot : dataSnapshot.getChildren()){
singleHorizontals.add(new SingleHorizontal(
snapshot.child("price").getValue(String.class),
snapshot.child("location").getValue(String.class),
snapshot.child("image").getValue(String.class)
));
Log.e(TAG,"Horizontal data loaded");
}
return singleHorizontals;
}
}
Then in your main Adapter
do this:
private void verticalView(final VerticalViewHolder holder) {
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference().child("Data");
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
VerticalAdapter adapter1 = new VerticalAdapter(getVerticalData(dataSnapshot), context);
holder.recyclerView.setLayoutManager(new LinearLayoutManager(context));
holder.recyclerView.setAdapter(adapter1);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void horizontalView(final HorizontalViewHolder holder) {
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference().child("Data");
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
HorizontalAdapter adapter = new HorizontalAdapter(getHorizontalData(dataSnapshot),context);
holder.recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
holder.recyclerView.setAdapter(adapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
so what should be changed in adapter which populates new cards when a child is added in firebase,im sending data through an object of modal class.Now its only makes 1 card on which newly added data is updated on.No new cards are being populated.and i dont want to get the whole data again and again,i just wanted to get data only one time and it should be retreived when a child is added on because the admin has to approve the patients data(and then it should be removed thereafter).
myadapter
public class Adapter extends RecyclerView.Adapter<Adapter.Viewholder> {
public UserInformation value = new UserInformation();
public Adapter(UserInformation value) {
this.value = value;
}
#Override
public Viewholder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.item_user_layout, parent, false);
return new Viewholder(view);
}
#Override
public void onBindViewHolder(Viewholder holder, int position) {
holder.edt_blood_group.setText(value.getBlood_group());
holder.edt_contact_no.setText(value.getContact_no());
holder.btn_approve.setText("approve");
}
#Override
public int getItemCount() {
return 3; //i dont know what to write here as i have no array of data
}
public class Viewholder extends RecyclerView.ViewHolder {
EditText edt_blood_group;
EditText edt_contact_no;
Button btn_approve;
public Viewholder(View itemView) {
super(itemView);
edt_blood_group = (EditText) itemView.findViewById(R.id.edt_blood_group);
edt_contact_no = (EditText) itemView.findViewById(R.id.edt_contact_no);
btn_approve = (Button) itemView.findViewById(R.id.btn_approve);
}
}
}
my MainActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final RecyclerView rcc = (RecyclerView) findViewById(R.id.userList);
rcc.setLayoutManager(new LinearLayoutManager(this));
//edtobj= (EditText) findViewById(R.id.edt_id);
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance();
DatabaseReference reference = firebaseDatabase.getReference("Patients");
reference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
UserInformation newPost = dataSnapshot.getValue(UserInformation.class);
rcc.setAdapter(new Adapter(newPost));
}
item_layout
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="#+id/edt_blood_group"
android:layout_width="160dp"
android:layout_height="wrap_content" />
<EditText
android:id="#+id/edt_contact_no"
android:layout_width="160dp"
android:layout_height="wrap_content" />
<Button
android:id="#+id/btn_approve"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="approve" />
</LinearLayout>
Do this in your main activity..!
Adapter adapter = null;
ArrayList<GetPatients> list = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_data_list);
recyclerView = findViewById(R.id.recycler_view);
adapter = new Adapter(list);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(adapter);
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance();
DatabaseReference reference = firebaseDatabase.getReference("Patients");
reference.addChildEventListener(this);
}
#Override
public void onChildAdded(DataSnapshot dataSnapshot,String s) {
GetPatients value = dataSnapshot.getValue(GetPatients.class);
list.add(new GetPatients(value.getPatientName(),value.getBloodGroup(),value.getContactNo()));
adapter.notifyDataSetChanged();
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot,String s) {
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
}