How to show exactly ten rows in RecyclerView - android

I would like to always display exactly ten rows in a RecyclerView. RecyclerView does not have a configurable setting for this. One approach is to calculate the size of each row. I would like the table to take up half the screen and always show 0..10 items within that area. If more than 10 items the user would scrooll to see the items. Right now the default behavior of RecyclerView seems to be to keep pushing everything down as it grows.

#Override
public int getItemCount() {
if(list.size()>10){
return 10;
}else{
return list.size();
}
}
In your recycler view adapter class return get item count to 10 if size of array or list is greater than 10

Your guess is right it's working with a dynamic computation of row height, here is an example:
Simple class standing for data you want to display:
public class CustomItem {
private int counter;
public CustomItem(int counter) {
this.counter = counter;
}
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
}
And simple layout to display CustomItem in a list:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/darker_gray">
<TextView
android:id="#+id/txvTest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="Test"/>
</RelativeLayout>
RecyclerAdapter for this class, you will see that we set row height programmatically in onBindViewHolder method:
public class CustomItemRecyclerAdapter extends RecyclerView.Adapter<CustomItemRecyclerAdapter.SimpleItemViewHolder> {
private static int NB_OF_ITEM_TO_DISPLAY = 10;
private List<CustomItem> customItems;
private Context context;
private int rowHeight;
public class SimpleItemViewHolder extends RecyclerView.ViewHolder {
TextView txvTest;
RelativeLayout container;
public SimpleItemViewHolder(View itemView) {
super(itemView);
txvTest = (TextView) itemView.findViewById(R.id.txvTest);
container = (RelativeLayout) itemView;
}
}
public CustomItemRecyclerAdapter(Context context, List<CustomItem> customItems) {
this.context = context;
this.customItems = customItems;
}
public void setItemHeight(int parentHeightInPx){
// Height of a row is just parent height divided by number of row to display
this.rowHeight = parentHeightInPx / CustomItemRecyclerAdapter.NB_OF_ITEM_TO_DISPLAY;
// Notify adapter because items will need to be redraw to use newly set height
this.notifyDataSetChanged();
}
#Override
public SimpleItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(context).inflate(R.layout.custom_item, parent, false);
return new SimpleItemViewHolder(itemView);
}
#Override
public void onBindViewHolder(SimpleItemViewHolder holder, int position) {
CustomItem item = customItems.get(position);
holder.txvTest.setText("Item " + item.getCounter());
holder.txvTest.setBackgroundColor(item.getCounter());
// Row size magic is here!
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, rowHeight);
holder.container.setLayoutParams(params);
}
#Override
public int getItemCount() {
return customItems.size();
}
}
Activity layout, with both a TextView used as placeholder for half the screen and the RecyclerView:
<?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">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#color/colorAccent"
android:text="TEST"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/a_main_rcv_items"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimary"
android:layout_weight="1"
android:scrollbars="vertical"/>
</LinearLayout>
And Activity code with comments to understand how it works ;):
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private CustomItemRecyclerAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Link to layout recycler view
recyclerView = (RecyclerView) findViewById(R.id.a_main_rcv_items);
// Create a simple linear layout manager and set it to recyclerview
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
// Create an instance of our custom Adapter
adapter = new CustomItemRecyclerAdapter(MainActivity.this, generateItems(20));
// Set this adapter
recyclerView.setAdapter(adapter);
// Listen for recycler view layout change event to get its height and then compute and set new rows height
recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
// Set item height using height of recyclerView
adapter.setItemHeight(view.getHeight());
// Need to simulate a scroll to force a redraw of recycler rows with new height... I have not find better for now...
recyclerView.scrollBy(0,1);
}
});
}
private List<CustomItem> generateItems(int nb) {
ArrayList<CustomItem> items = new ArrayList<>(nb);
for (int i = 1; i < nb + 1; i++) {
items.add(new CustomItem(i));
}
return items;
}
}

Related

How to add child item to last item from RecyclerView?

Actually i'm trying to make an app that take restaurant orders and i'm having some issues after adding child item to my order.
I have a recyclerView where i have different type's of food when i press on one of them i add it to recyclerView that work's something like a notebook, then i have an button that open an AlertDialog in which there is another recyclerView with variant's.
Example: i press on PIZZA from food recyclerView it's add it to the notebook after i press on variant's and i can add "WITH PEPERONI" or "LARGE" or both.
I have followed this guide ( here github project from the guide) for making the recyclerView with child item.
Now the issue is how can i add the child item to the last parent item?
like i'm adding parent item using this:
dummyParentDataItem = new ItemPTERM();
dummyParentDataItem.setButton_name(filteredList.get(position).getDeskS());
dummyParentDataItem.setQuant( Integer.parseInt(filteredList.get(position).getQuant()));
dummyChildDataItems = new ArrayList<>();
dummyParentDataItem.setChildDataItems(dummyChildDataItems);
itemCassas.add(dummyParentDataItem);
And this when i add a child from variant's recyclerView
dummyChildDataItem = new DummyChildDataItem();
dummyChildDataItem.setChildName(tipo.toString());
dummyChildDataItems.add(dummyChildDataItem);
dummyParentDataItem.setChildDataItems(dummyChildDataItems);
itemCassas.add(dummyParentDataItem);
but obviously the app crash because that don't know which is the last paren't item to which add the child.
When i try to put both parent and child when i'm adding food from recyclerView with:
dummyParentDataItem = new ItemPTERM();
dummyParentDataItem.setButton_name(filteredList.get(position).getDeskS());
dummyParentDataItem.setQuant( Integer.parseInt(filteredList.get(position).getQuant()));
dummyChildDataItems = new ArrayList<>();
dummyChildDataItem = new DummyChildDataItem();
dummyChildDataItem.setChildName("WITH PEPERONI");
dummyChildDataItems.add(dummyChildDataItem);
dummyParentDataItem.setChildDataItems(dummyChildDataItems);
itemCassas.add(dummyParentDataItem);
that obviously works but i have to add the child in a separate method to the last parent.
How can i do it? any suggestion?
Here is also my adapter code:
public class AdapterPTERM extends RecyclerView.Adapter<AdapterPTERM.ExampleViewHolder> {
private ArrayList<ItemPTERM> mExampleList;
#NonNull
#Override
public ExampleViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_pterm,parent,false);
return new ExampleViewHolder(v);
}
AdapterPTERM(ArrayList<ItemPTERM> exampleList){
mExampleList = exampleList;
}
#Override
public void onBindViewHolder(#NonNull ExampleViewHolder holder, int position) {
ItemPTERM item = mExampleList.get(position);
holder.desc.setText(item.getBtnName());
holder.qta.setText(String.valueOf(item.getQuant()));
int noOfChildTextViews = holder.linearLayout_childItems.getChildCount();
int noOfChild = item.getChildDataItems().size();
if (noOfChild < noOfChildTextViews) {
for (int index = noOfChild; index < noOfChildTextViews; index++) {
TextView currentTextView = (TextView) holder.linearLayout_childItems.getChildAt(index);
currentTextView.setVisibility(View.GONE);
}
}
for (int textViewIndex = 0; textViewIndex < noOfChild; textViewIndex++) {
TextView currentTextView = (TextView) holder.linearLayout_childItems.getChildAt(textViewIndex);
currentTextView.setText(item.getChildDataItems().get(textViewIndex).getChildName());
}
if(position % 2 == 0 ){
holder.itemView.setBackgroundColor(Color.parseColor("#C0C0C0"));
}else if(position % 2 == 1){
holder.itemView.setBackgroundColor(Color.parseColor("#D3D3D3"));
}
}
#Override
public int getItemCount() {
return mExampleList.size();
}
public class ExampleViewHolder extends RecyclerView.ViewHolder {
public TextView desc;
public TextView qta;
private LinearLayout linearLayout_childItems;
private Context context;
ExampleViewHolder(View itemView) {
super(itemView);
desc = itemView.findViewById(R.id.Desc);
qta = itemView.findViewById(R.id.Qta);
linearLayout_childItems = itemView.findViewById(R.id.ll_child_items);
context = itemView.getContext();
int intMaxNoOfChild = 0;
for (int index = 0; index < mExampleList.size(); index++) {
int intMaxSizeTemp = mExampleList.get(index).getChildDataItems().size();
if (intMaxSizeTemp > intMaxNoOfChild) intMaxNoOfChild = intMaxSizeTemp;
}
for (int indexView = 0; indexView < intMaxNoOfChild; indexView++) {
TextView textView = new TextView(context);
textView.setId(indexView);
textView.setPadding(0, 20, 0, 20);
textView.setGravity(Gravity.CENTER);
textView.setBackground(ContextCompat.getDrawable(context, R.drawable.background_sub_module_text));
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
linearLayout_childItems.addView(textView, layoutParams);
}
}
}
public void removeItem(int position) {
mExampleList.remove(position);
notifyItemRemoved(position);
}
}
Here is a screenshot of my activity
To display data in a RecylerView, you provide a list of items to its adapter.
To add a new element you just need to add the item to the list and call notifyDataSetChange() on the adapter.
For example you can create a new method in you adapter, like that:
public void addItem(ItemPTERM item){
mExampleList.add(item);
notifyDataSetChange();
}
EDIT
So what you can do is to allow your item to have a list of 'variants'.
For example if you select 'Pizza' and want to had 'big' and 'pepperoni', your item will be the pizza and 'big'and 'pepperoni' part of the variants list.
Food {
String name;
List<Variant> variants;
//... Other attributes
public void setVariant(Variant variant){
if (variants == null) // We want to initialise the array only when necessary
variants = new ArrayList<>();
variants.add(variant);
}
#Nullable
public List<Variant> getVariants(){
return variants;
}
}
Variant{
String name;
//... Other attributes
}
When you select a variant you add it to the appropriate Food object.
Approach 1
Then in your adapter, in the onBindViewHolder you can dynamically add the variants to the item;
This way is not the most optimised but should work:
Item layout
<!-- Food Views-->
...
<!-- Container for the variants -->
<LinearLayout
android:id="#+id/variants_list"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content"/>
</LinearLayout>
Variant layout
<?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="horizontal">
<TextView
android:id="#+id/variant_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/variant_other_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Adapter
final LayoutInflater mInflater;
List<Food> mFoods;
public FoodAdapter(Context context, ...){
//...
mInflater = LayoutInflater.from(context);
}
#Override
public void onBindViewHolder(#NonNull ExampleViewHolder holder, int position) {
Food food = mFood.get(position);
// Display food attributes
//...
// Clear the container to set fresh info
holder.variantsContainer.removeAllViews();
List<Variant> variants = food.getVariants();
if (variants != null && variants.size() > 0){
// Display the variants when necessary
for(Variant v : variants){
View vView = mInflater.inflate(R.layout.variant_layout);
TextView nameTV = vView.findViewById(R.id.variant_name);
nameTV.setText(v.getName());
// Set other fields values
// ...
holder.variantsContainer.addView(vView);
}
}
}
Approach 2
Another way to do it is to have a list of food object containing a list of variant as above, but to not use this list directly in the adapter.
Instead the adapter will use it's own list which will be both Food and Variant objects mix together. Your adapter should then be able to display 2 kinds of objects. This way is more flexible but you have to recreate the entire adapter list every time you change something.
For instance:
Let say you have 3 Food objects in the list. The first one has 1 variant, the second one none and the third one 2.
Then your adapter will get a list like that:
List<Object> items;
items will contain:
{food1, variant1a, food2, food3, variant3a, variant3b}
And then your adapter will have to use two different layout to display either the Food or the Variant object.
#NonNull
#Override
public ExampleViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if (viewType == 0) { // if food
View vFood = mInflater.inflate(R.layout.food_layout, parent, false);
return new FoodViewHolder(vFood);
} else { // variant
View vVariant = mInflater.inflate(R.layout.variant_layout, parent, false);
return new VariantViewHolder(vVariant;
}
}
#Override
public int getItemViewType(int position) {
return (mItems.get(position) instanceof Food) ? 0 : 1;
}

android recyclerview setVisibility View.GONE still occupies space

I want to list only the disliked items in my recyclerview. I have a full list of items in rv in MainActivity (did not set visibility here). I can set for each items like or dislike by clicking on imagebutton. The MainActivity shows full list of items (cardviews) that shows imagebutton likes or not. If item is liked, this is stored in firebase db as separate entry under Likes with item key (firebase key .push) and not under Items. (in firebase db I have Users, Items, Likes).
Here is my subactivity code, DislikedItemsActivity, where I want to show only items that are disliked by using setVisibility(View.GONE) for items that are liked. This still holds the space between items for the View.GONE items as well (though these cardviews are empty).
mRecyclerView = (RecyclerView) findViewById(R.id.rvItemList);
mRecyclerView .setHasFixedSize(true);
final LinearLayoutManager linearLayoutManager = new
LinearLayoutManager(this);
linearLayoutManager.setReverseLayout(true);
linearLayoutManager.setStackFromEnd(true);
mRecyclerView.setLayoutManager(linearLayoutManager);
final FirebaseRecyclerAdapter<Item, MainActivity.ItemViewHolder>
firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Item,
MainActivity.ItemViewHolder>(
Item.class,
R.layout.list_item,
MainActivity.ItemViewHolder.class,
mDatabase
) {
#Override
protected void populateViewHolder(final MainActivity.ItemViewHolder viewHolder, final Item model, final int position) {
final String itemKey = getRef(position).getKey();
mDatabaseItemsLiked.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// if item is not liked, thus no user set in db ( I want to see only items that are liked in my recyclerview)
if (!dataSnapshot.child(itemKey).hasChild(mAuth.getCurrentUser().getUid())) {
viewHolder.mView.setVisibility(View.VISIBLE);
viewHolder.itemNameSetup(model.getItemName());
viewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent itemSheetIntent = new Intent(DislikedItemsActivity.this, ItemSheetActivity.class);
adatlapIntent.putExtra("item_key", itemKey);
startActivity(itemSheetIntent);
}
});
} else {
viewHolder.mView.setVisibility(View.GONE);
mRecyclerView.getAdapter().notifyItemRemoved(position); //this puts together the visible items, but when scrolling, it gets messed up
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, databaseError.toString());
}
});
}
#Override
public void onBindViewHolder(MainActivity.TermekViewHolder viewHolder, int position) {
super.onBindViewHolder(viewHolder, position);
}
};
mRecyclerView.setAdapter(firebaseRecyclerAdapter);
}
I looked for many solutions like onBindViewHolder, notifyDataChanged, set margin to 0, set layout size in xml to wrap_content. The best I could get is to have the not-liked items without space with mRecyclerView.getAdapter().notifyItemRemoved(position);, but scrolling the list backwards the whole rv gets messed up (duplicate entry, empty spaces, disordered list).
I don't know how to list only the disliked items from the complete item list from MainActivity rv in a new activity? My code above shows only disliked items, but only until I scroll to end of list, if I scroll backwards the rv gets messed up. I logged the positions of views (18items) in onBindViewHolder and first it counts all items in sequence (17,16,15,14...0), but scrolling from end of list to backwards the position jumps from 0 to 4 like 7times (changes always how many times) then it is the same for item 5,6, until item 17 (all of their positions showed in onBindViewHolder 7 or 8 times during scrolling ie. 5,5,5,5,6,6,6,6) and only for backward scrolling and during backward move rv shows only disliked items or empty views or duplicate entry of disliked item.
my 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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="#drawable/hatter"
tools:context="com.example.user.itemlist.ItemsLikedActivity">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/rvItemList"
>
</android.support.v7.widget.RecyclerView>
(don't know how to add pics) When disliked list appears, it shows first item (cardview takes up the full screen), and when I start to scroll the list (from 1. visible item to next visible item) if there is space(item1 vis and next vis item is 4), rearrange and I can see that next visible item(item4) moves to 1. visible item, then the rest of the list is arranged well, until I start to scroll back, then it rearranges the rv with spaces and with double entry. The list goes back and forth until both end (which is the length of full items list and not just the disliked items), but visible items gets all messed up.
use this code to remove occupied space :
ViewGroup.LayoutParams params = holder.itemView.getLayoutParams();
params.height = 0;
holder.itemView.setLayoutParams(params);
I found solution to filter the complete database. In my question I wanted to get only the liked/disliked items in a separate activity, though my previous code showed filtered items, but with gaps.
In below code, I changed the DatabaseReferences (mDatabase -node with complete item list and mDatabaseItemsLiked -node with item uid and user uid).
This gave only empty cards with only number as the likedItems, but to get name from the mDatabase (complete list), I used dataSnapshot.getValue(Item.class).getItemName().
firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Item,
MainActivity.ItemViewHolder>(
Item.class,
R.layout.list_item,
MainActivity.ItemViewHolder.class,
mDatabaseItemsLiked
) {
#Override
protected void populateViewHolder(final MainActivity.ItemViewHolder
viewHolder, final Item model, final int position) {
final String itemKey = getRef(position).getKey();
mDatabase.child(itemKey).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
viewHolder.itemNameSetup(dataSnapshot.getValue(Item.class).getItemName());
viewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent itemSheetIntent = new Intent(LikedItemsActivity.this, ItemSheetActivity.class);
adatlapIntent.putExtra("item_key", itemKey);
startActivity(itemSheetIntent);
}
});
This works for me without any problem. I hope it is network efficient.
You can try to store the liked ítem in boolean array and later in populateViewHolder check if ítem has like o no and set visibility.
I would do like that:
In your class declare :
private boolean [] itemLiked;
In your constructor :
this.itemLiked = new boolean [arrayOfAllItems.size]
On click event:
itemLiked[position] = true; //Where position is row position
onBindViewholder or in your case populateViewHolder:
if (!itemLiked[position]) {
viewHolder.mView.setVisibility(View.GONE); }
Hope it helps, good luck!
EDITED
I do not understand exactly what you want to do, that's why I leave you the code for two cases.
Case 1. Mark and dis-mark the rows.
Case 2. Save to database or delete.
In continuation the complete code
Activity XML add RecyclerView:
<android.support.v7.widget.RecyclerView
android:id="#+id/my_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Make a custom layout for row:
<TextView
android:id="#+id/question_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
android:text="QUESTION"/>
<ImageButton
android:id="#+id/like"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="#android:drawable/ic_input_add"
android:background="#android:color/transparent"
android:layout_marginRight="4dp"/>
<ImageButton
android:id="#+id/dislike"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="#android:drawable/ic_delete"
android:background="#android:color/transparent"
android:layout_marginRight="4dp"/>
Make a Model class:
public class SomeModel {
private String question;
public SomeModel(String question) {
this.question = question;
}
public String getQuestion() {
return question;
}
}
Make Adapter Class:
public class SomeAdapter extends RecyclerView.Adapter {
private ArrayList<SomeModel> arrayList;
private boolean [] item_has_like, item_hase_vote;
public SomeAdapter(ArrayList<SomeModel> arrayList) {
this.arrayList = arrayList;
this.item_has_like = new boolean[arrayList.size()];
this.item_hase_vote = new boolean[arrayList.size()];
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder myViewHolder = null;
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.draw_row, parent, false);
myViewHolder = new MyViewHolder(view);
return myViewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final MyViewHolder myViewHolder = (SomeAdapter.MyViewHolder)holder;
final SomeModel item = arrayList.get(position);
int backGround;
/**In background you can save whateveryou need, example:
* backGround= R.drawable.some_background;
* backGround= View.GONE;
*.....
**/
if (item_hase_vote[position]){
if (item_has_like[position])
{
backGround= Color.GREEN;//
} else {
backGround= Color.RED;
}
} else {
backGround= Color.TRANSPARENT;
}
myViewHolder.questionTV.setText(item.getQuestion());
myViewHolder.questionTV.setBackgroundColor(backGround);
}
#Override
public int getItemCount() {
return arrayList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private TextView questionTV;
private ImageView like, dislike;
public MyViewHolder(final View itemView) {
super(itemView);
questionTV = (TextView)itemView.findViewById(R.id.question_tv);
like = (ImageView)itemView.findViewById(R.id.like);
dislike = (ImageView)itemView.findViewById(R.id.dislike);
like.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Item been voted
item_hase_vote[getAdapterPosition()] = true;
//Item got Like save in boolean array by row position
item_has_like[getAdapterPosition()] = true;
//notify your adapter
notifyDataSetChanged();
/*OR Here comes the code where You save Item in Your Data Base.*/
}
});
dislike.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Item been voted
item_hase_vote[getAdapterPosition()] = true;
// Item got DisLike save in boolean array by row position
item_has_like[getAdapterPosition()] = false;
//notify your adapter
notifyDataSetChanged();
/*OR Here You Remove item on Dislike
arrayList.remove(getAdapterPosition());
notifyItemRemoved(getAdapterPosition());
notifyItemRangeChanged(getAdapterPosition(),arrayList.size());
*/
}
});
}
}
}
And Your Activity:
public class SomeActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_some);
ArrayList<SomeModel> arrayList = new ArrayList<>();
for (int i = 0; i <77 ; i++) {
arrayList.add(new SomeModel("Question " + i));
}
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.my_rv);
SomeAdapter adapter = new SomeAdapter(arrayList);
recyclerView.setAdapter(adapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(layoutManager);
}
}
Saving the selected item by the adapterPosition in boolean, String, int....[], the Adapter always gona know whats going on with every item and like that your list always gona be arranged.
Good Luck!
I also got the same problem. what i thought was if RelativeLayout load one after one, height=0, the specs will remove.So it works for me.
This is my ViewHolder. I Introduces my reletivelayout here.
public static class BlogViewHolder extends RecyclerView.ViewHolder {
View mView;
TextView txtdate;
RelativeLayout con_rel;
String name_day = "no name";
public BlogViewHolder(View itemView) {
super(itemView);
mView=itemView;
con_rel=(RelativeLayout)itemView.findViewById(R.id.con_rel);
txtdate = (TextView)itemView.findViewById(R.id.day);
}
}
The I set height and width
con_ref=FirebaseDatabase.getInstance().getReference().child("/consultation");
FirebaseRecyclerAdapter<Consultation,SelectConsaltation.BlogViewHolder>recyclerAdapter=new FirebaseRecyclerAdapter< Consultation,SelectConsaltation.BlogViewHolder>(
Consultation.class,
R.layout.consultation_card,
SelectConsaltation.BlogViewHolder.class,
con_ref
) {
#Override
protected void populateViewHolder(final SelectConsaltation.BlogViewHolder viewHolder, final Consultation model, final int Consultation) {
Shedule_ref.child(model.getScheduleID()).child("Day").addValueEventListener(new ValueEventListener() {
ViewGroup.LayoutParams params = viewHolder.con_rel.getLayoutParams();
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
name_day = dataSnapshot.getValue(String.class);
if (doctor_id_from_doctor.equals( model.getDoctorID() )){
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
Date strDate = null;
try {
strDate = sdf.parse(model.getDate());
} catch (ParseException e) {
e.printStackTrace();
}
if(System.currentTimeMillis()<=strDate.getTime() ) {
params.height = 300;
params.width =800;
viewHolder.con_rel.setLayoutParams(params);
viewHolder.setDate(model.getDate(),name_day);
}
else {
**params.height = 0;
params.width = 0;
viewHolder.con_rel.setLayoutParams(params);**
}
}
else {
params.height = 0;
params.width = 0;
viewHolder.con_rel.setLayoutParams(params);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
};
recyclerView.setAdapter(recyclerAdapter);
}
My card view code
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="#+id/con_rel"
android:layout_marginRight="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="3dp"
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="wrap_content" android:background="#a2ffffff">
<LinearLayout
android:layout_width="match_parent"
android:layout_marginTop="3dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/day"
android:layout_marginTop="10dp"
android:textSize="18sp"
android:layout_marginLeft="5dp"
android:textColor="#color/colorBlack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/_07th_of_sunday_january_2018_at_9_00am"/>
<TextView
android:id="#+id/nextnumber"
android:layout_marginLeft="5dp"
android:textSize="18sp"
android:textColor="#color/colornextnumber"
android:textStyle="bold"
android:layout_marginTop="20dp"
android:text="#string/next_avealable_number_is_04"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/booknow"
android:textStyle="bold"
android:textSize="18sp"
android:layout_marginTop="20dp"
android:layout_marginLeft="240dp"
android:layout_marginBottom="10dp"
android:textColor="#color/colorbookNow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/book_now"/>
</LinearLayout>
</RelativeLayout>
Here is my inreface

Android RecyclerView DefaultItemAnimator - Items moving weirdly

I have a problem with a simple multicolumn layout RecyclerView which is best reproducible on tablets. I have created a basic example with fully functional source code below, maybe it is easier to quickly run it, instead of trying to understand what I mean ;)
The main problem on tablets (where I have a grid layout) is, that items get strangely rearranged when hiding specific items and displaying them again (via notifyItemRemoved() and notifyItemInserted()). I think this can be even reproduced with removing and inserting only the first item. The layout manager inserts an extra row at the top and moves items from the first line below to fill it. (The example below removes and inserts every third item, starting with the first one)
I have different types of items (in the example red, green and blue items) and I want to toggle a specific type of items (in the example you can toggle the red items with a click on the FloatingActionButton).
The weird behavior that I want to fix can be reproduced by just clicking the FAB twice without scrolling before. This would first filter the red items and then display them again. When toggling the filter the second time you will notice that there is a row of items inserted above and items from the first row are moved to fill it.
If you didn't notice during the animation, just scroll up after toggling the filter.
I would expect that the first row stays where it is and that just the red items get displayed again.
Does anybody know how to fix that?
Here is the activity code:
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private TestAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
((FloatingActionButton) findViewById(R.id.fab)).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
toggleFiltered();
}
});
final int columnCount = getColumnCount(300);
GridLayoutManager layoutManager = new GridLayoutManager(this, columnCount);
recyclerView.setLayoutManager(layoutManager);
adapter = new TestAdapter();
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(new DefaultItemAnimator());
}
private void toggleFiltered() {
final boolean filtered = adapter.isFiltered();
adapter.setFiltered(!filtered);
if(filtered) {
adapter.notifyItemInserted(0);
adapter.notifyItemInserted(3);
adapter.notifyItemInserted(6);
adapter.notifyItemInserted(9);
adapter.notifyItemInserted(12);
} else {
adapter.notifyItemRemoved(0);
adapter.notifyItemRemoved(3);
adapter.notifyItemRemoved(6);
adapter.notifyItemRemoved(9);
adapter.notifyItemRemoved(12);
}
}
int getColumnCount(final int dpThreshold){
final Display display = getWindowManager().getDefaultDisplay();
final DisplayMetrics outMetrics = new DisplayMetrics();
display.getMetrics(outMetrics);
final float density = getResources().getDisplayMetrics().density;
final float dpWidth = outMetrics.widthPixels / density;
return ((int) dpWidth) / dpThreshold;
}
class TestAdapter extends RecyclerView.Adapter {
public static final int TYPE_RED = 0;
public static final int TYPE_GREEN = 1;
public static final int TYPE_BLUE = 2;
private boolean filtered = false;
public boolean isFiltered() {
return filtered;
}
public void setFiltered(final boolean filtered) {
this.filtered = filtered;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
final View view = new View(parent.getContext());
view.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 500));
int color = Color.RED;
if(viewType == TYPE_GREEN){
color = Color.GREEN;
} else if (viewType == TYPE_BLUE){
color = Color.BLUE;
}
return new TestViewHolder(view, color);
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
}
#Override
public int getItemViewType(final int position) {
if(!filtered) {
if ((position + 3) % 3 == 0) {
return TYPE_RED;
}
if ((position + 2) % 3 == 0) {
return TYPE_GREEN;
}
} else {
if ((position) % 2 == 0) {
return TYPE_GREEN;
}
}
return TYPE_BLUE;
}
#Override
public int getItemCount() {
if(filtered) {
return 10;
}
return 15;
}
class TestViewHolder extends RecyclerView.ViewHolder {
public TestViewHolder(final View itemView, final int color) {
super(itemView);
itemView.setBackgroundColor(color);
}
}
}
}
And here is the layout code:
<android.support.constraint.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/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="#+id/root_layout"
app:layout_constraintLeft_toLeftOf="#+id/root_layout"
app:layout_constraintRight_toRightOf="#+id/root_layout"
app:layout_constraintTop_toTopOf="#+id/root_layout"/>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="#+id/root_layout"
app:layout_constraintRight_toRightOf="#+id/root_layout"
android:layout_margin="16dp"
/>
</android.support.constraint.ConstraintLayout>
Firstly, I should mention that the problem is not in DefaultItemAnimator. If you set your animator to null, you would have the same behavior. And actually there is no problem at all. The behavior is expected, as you're just inserting the item into the first position of your TestAdapter, and then other items are shifting, but your RecyclerView is staying at the same position.
So all you need is just add recyclerView.scrollToPosition(0); line in the end of your toggleFiltered() method, so it will scroll up, once you've applied the filtering. And it will work as you wish.
its not weird. because you notifyItemInserted(0). it is insert Item. not notifyDataSetChanged(). adapter is showing item keep going. its Green Item. you notifyItemInserted(0) insert Red, still adapter showing first item is Green(it poisition is 1). because adapter notifyItemInserted(0) has before showing first item is Green.

Android : Set center position item of a RecyclerView

I want to implement a view picker which is like a number view picker but instead of picking numbers, it will pick the view in the center of the picker.
The basic idea is to get the position of centered item and replace the view at that position with a different view. To do so, I have done following -
I have the following layout -
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<View
android:id="#+id/view"
android:background="#ffeb3b"
android:layout_gravity="center"
android:layout_height="56dp"
android:layout_width="match_parent" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rvItems"
android:clipToPadding="false"
android:layout_height="match_parent"
android:layout_width="match_parent" />
</FrameLayout>
The activity for populating view is -
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private RecyclerView rvItems;
private View view;
private List<Data> array = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i=0; i< 100; i++) {
final Data data = new Data();
data.isVisible = true;
data.data = String.valueOf(i);
array.add(data);
}
view = (View) findViewById(R.id.view);
rvItems = (RecyclerView) findViewById(R.id.rvItems);
rvItems.setAdapter(new SliderAdapter(array));
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
rvItems.setLayoutManager(layoutManager);
rvItems.post(new Runnable() {
#Override
public void run() {
int firstVisibleChildPosition = layoutManager.findFirstCompletelyVisibleItemPosition();
int lastVisibleChildPosition = layoutManager.findLastCompletelyVisibleItemPosition();
int centerChildPosition = (firstVisibleChildPosition + lastVisibleChildPosition) / 2;
for (int i=0; i < centerChildPosition; i++) {
final Data data = new Data();
data.isVisible = false;
// Adding empty list at begining and end of position
array.add(0, data);
array.add(array.size(), data);
}
rvItems.getAdapter().notifyDataSetChanged();
}
});
rvItems.addOnScrollListener(new RecyclerView.OnScrollListener() {
int centerChildPosition = -1;
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
int firstVisibleChildPosition = layoutManager.findFirstCompletelyVisibleItemPosition();
int lastVisibleChildPosition = layoutManager.findLastCompletelyVisibleItemPosition();
centerChildPosition = (firstVisibleChildPosition + lastVisibleChildPosition) / 2;
recyclerView.scrollToPosition(centerChildPosition);
}
Log.d(TAG, "center position = " + centerChildPosition );
}
});
}
}
Using the above code, I am able to know the centered child position but not able to fit it properly inside the yellow rectangle view -
How can I make sure that the numbers are within yellow bar? For now, I have added empty views on the top and bottom of the list so that the first and last items in the list can be centered. What is an optimum way to do it?

Horizontal scrolling grid view

I know it is not possible in Android to scroll grid view horizontally. But what I am doing is adding image buttons dynamically inside horizontal scroll view like this:
public class HorizontalScroller extends Activity {
static int l=0;
private Rect mTempRect = new Rect();
static int r1=0;
static int t=0;
static int b=0;
static int x=0;
static int y=0;
//Button[] b1 = new Button[100];
ImageButton btn[][] = new ImageButton[10][10];
//ImageButton b1 = new ImageButton(this);
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout rl = (LinearLayout)findViewById(R.id.widget92);
LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
for(int i=0;i<4;i++)
{
for(int j=0;j<10;j++)
{System.out.println("helo");
/* l=l+100;
r1=r1+100;
t=t+100;
b=b+100;*/
//button();
//ImageButton btn=new ImageButton(this);
/* Rect r = mTempRect;
r.left=10;
r.top=10;
r.right=10;
r.bottom=10;
btn[i][j].getDrawingRect(r);*/
//btn[i][j].setId(j);
Rect r = mTempRect;
r.set(0,0,0,0);
Rect r2 = mTempRect;
r2.set(0,20,0,20);
btn[i][j]=new ImageButton(this);
btn[i][j]. setBackgroundResource(R.drawable.icon);
btn[i][j].setMinimumWidth(20);
btn[i][j].setMinimumHeight(20);
params1.setMargins(5, 5, 5,5);
rl.addView(btn[i][j],params1);
System.out.println("1="+btn[i][j].getTop());
System.out.println("2="+btn[i][j].getLeft());
System.out.println("3="+btn[i][j].getRight());
System.out.println("4="+btn[i][j].getBottom());
}
}
}
}
but I am getting all image buttons in a single line. How can I implement them in a grid like structure?
Implementing a horizontally scrolling GridView involves copying a few of the Android source code classes into your codebase (AdapterView, AbsListView, GridView, ScrollBarDrawable) and adding in code to handle the horizontal code. This is mainly copying some of the code and changing top to left, bottom to right, etc. The main reason for having to copy instead of extending is the final nature of those classes.
I implemented a horizontally scrolling GridView a while ago and finally got around to pushing to github:
https://github.com/jess-anders/two-way-gridview
You can
use a TableLayout inside a HorizontalScrollView, or
stay with your approach with an horizontal LinearLayout but adding vertical LinearLayouts instead of directly the images. E.g., adding three to four images per vertical LinearLayout in portrait, and redrawing to add only two in landscape.
I would try the TableLayout approach first.
PS1: for next time, try to remove all the non-relevant code (the less code is there, the easier is to understand what you did).
PS2: Remember that System.out is usually redirected to /dev/null and thus lost, so I strongly suggest you to use Log.d instead.
Complete example
Adapt this to the onCreate() method or wherever you need it:
public void horizontalScrollGalleryLayout () {
HorizontalScrollView sv = new HorizontalScrollView(this);
LinearLayout llh = new LinearLayout(this);
llh.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams layoutParamsTV = new LinearLayout.LayoutParams(40, 40);
LinearLayout.LayoutParams layoutParamsLL = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
for (int i=0; i<20; i++) {
LinearLayout llv = new LinearLayout(this);
llv.setOrientation(LinearLayout.VERTICAL);
TestView testView1 = new TestView(this, Color.rgb(i*12, 0, 0));
TestView testView2 = new TestView(this, true, Color.rgb(i*12, i*12, 0));
TestView testView3 = new TestView(this, true, Color.rgb(0, i*12, 0));
llv.addView(testView1, layoutParamsTV);
llv.addView(testView2, layoutParamsTV);
llv.addView(testView3, layoutParamsTV);
llh.addView(llv, layoutParamsLL);
}
sv.addView(llh, layoutParamsLL);
setContentView(sv);
}
I'm using a very simple View as an example:
public class TestView extends View {
Context context;
int color;
public TestView(Context context, int color) {
super(context);
this.context = context;
this.color = color;
}
#Override
public void onDraw (Canvas canvas) {
super.onDraw(canvas);
this.setBackgroundColor(Color.LTGRAY);
Paint paint = new Paint (Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
canvas.drawCircle(20, 20, 20, paint);
}
}
There is a very easy trick.
Rotate the grid view by 270 degree and set number of columns as 2.
Rotate each item to 90 degree (so that the items are displayed as original orientation).
This might be useful for some!!
I have done this way:
activity_main.xml:
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<GridView
android:id="#+id/gridView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</GridView>
</LinearLayout>
</HorizontalScrollView>
MainActivity.java:
GridView gridView = (GridView) findViewById(R.id.gridView);
gridView.setNumColumns(arrayList.size());
GridViewAdapter gridViewAdapter = new GridViewAdapter(mContext, arrayList);
gridView.setAdapter(gridViewAdapter);
// Set dynamic width of Gridview
setDynamicWidth(gridView);
Add below method:
private void setDynamicWidth(GridView gridView) {
ListAdapter gridViewAdapter = gridView.getAdapter();
if (gridViewAdapter == null) {
return;
}
int totalWidth;
int items = gridViewAdapter.getCount();
View listItem = gridViewAdapter.getView(0, null, gridView);
listItem.measure(0, 0);
totalWidth = listItem.getMeasuredWidth();
totalWidth = totalWidth*items;
ViewGroup.LayoutParams params = gridView.getLayoutParams();
params.width = totalWidth;
gridView.setLayoutParams(params);
}
Hope this will help you.
I have already posted this answer here, but both questions are
identical...
There is a nice solution in Android from now on : HorizontalGridView.
1. Gradle dependency
dependencies {
compile 'com.android.support:leanback-v17:23.1.0'
}
2. Add it in your layout
your_activity.xml
<!-- your stuff before... -->
<android.support.v17.leanback.widget.HorizontalGridView
android:layout_width="wrap_content"
android:layout_height="80dp"
android:id="#+id/gridView"
/>
<!-- your stuff after... -->
3. Layout grid element
Create a layout for your grid element ( grid_element.xml ). I have created a simple one with only one button in it.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Button"
android:id="#+id/button" />
</LinearLayout>
4. Create an adapter
Highly inspired by this link : https://gist.github.com/gabrielemariotti/4c189fb1124df4556058
public class GridElementAdapter extends RecyclerView.Adapter<GridElementAdapter.SimpleViewHolder>{
private Context context;
private List<String> elements;
public GridElementAdapter(Context context){
this.context = context;
this.elements = new ArrayList<String>();
// Fill dummy list
for(int i = 0; i < 40 ; i++){
this.elements.add(i, "Position : " + i);
}
}
public static class SimpleViewHolder extends RecyclerView.ViewHolder {
public final Button button;
public SimpleViewHolder(View view) {
super(view);
button = (Button) view.findViewById(R.id.button);
}
}
#Override
public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(this.context).inflate(R.layout.grid_element, parent, false);
return new SimpleViewHolder(view);
}
#Override
public void onBindViewHolder(SimpleViewHolder holder, final int position) {
holder.button.setText(elements.get(position));
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(context, "Position =" + position, Toast.LENGTH_SHORT).show();
}
});
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemCount() {
return this.elements.size();
}
}
5. Initialize it in your activity :
private HorizontalGridView horizontalGridView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_activity);
horizontalGridView = (HorizontalGridView) findViewById(R.id.gridView);
GridElementAdapter adapter = new GridElementAdapter(this);
horizontalGridView.setAdapter(adapter);
}
Use recyclerview with setting its gridlayout as layout manager and set it to horizontal scroll
your recycle view.setLayoutManager(new GridLayoutManager(getActivity(),2, LinearLayoutManager.HORIZONTAL, false))
here 2 is the column span for grid

Categories

Resources