RecyclerView behaviour issue in nestedscrollview - android

I have a nested scrollview as parent and 2 recyclerviews as its children. What my issue is the recycler view draws it's children at one shot instead of drawing on scroll. How can I prevent this. I read that if we add
android:nestedScrollingEnabled="false"
property this issue comes. But I added this property to make the scroll smoother. Given below is my xml file.
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/color_fafafa"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:nestedScrollingEnabled="false"
android:paddingLeft="#dimen/dp_5"
android:paddingRight="#dimen/dp_5"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
Can anybody tell me how can I solve this issue?

If you only have 2 RecyclerViews as children I would suggest removing one and and using RecyclerViews ViewType.
Use an Adapter like this:
public class ExampleAdapter extends RecyclerView.Adapter<BindableViewHolder> {
private static final int VIEW_TYPE_CLASS_A = 0;
private static final int VIEW_TYPE_CLASS_B = 1;
private List<ClassA> class_a_list;
private List<ClassB> class_b_list;
public ExampleAdapter(List<ClassA> class_a_list, List<ClassB> class_b_list) {
this.class_a_list = class_a_list;
this.class_b_list = class_b_list;
}
#Override
public BindableViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case VIEW_TYPE_CLASS_A:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_class_a, parent, false);
return new ClassAHolder(view);
default:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_class_b, parent, false);
return new ClassBHolder(view);
}
}
#Override
public void onBindViewHolder(BindableViewHolder holder, int position) {
if(position < class_a_list.size()) {
((ClassAHolder) holder).bind(class_a_list.get(position));
} else {
((ClassBHolder) holder).bind(class_b_list.get(position - class_a_list.size()));
}
}
#Override
public int getItemCount() {
return class_a_list.size() + class_b_list.size();
}
#Override
public int getItemViewType(int position) {
if(position < class_a_list.size()) {
return VIEW_TYPE_CLASS_A;
} else {
return VIEW_TYPE_CLASS_B;
}
}
}
With the help of getItemViewType(int position) you determine which kind of View there should be in the specific position.
Then you can use ViewHolders like these:
public abstract class BindableViewHolder<T> extends RecyclerView.ViewHolder {
public BindableViewHolder(View itemView) {
super(itemView);
}
public abstract void bind(T data);
}
public class ClassAHolder extends BindableViewHolder<ClassA> {
public ClassAHolder(View itemView) {
super(itemView);
}
#Override
public void bind(ClassA data) {
// populate your views
}
}
public class ClassBHolder extends BindableViewHolder<ClassB> {
public ClassBHolder(View itemView) {
super(itemView);
}
#Override
public void bind(ClassB data) {
// populate your views
}
}

Related

RecyclerView not counting above 10

My RecyclerView does not count above 10 items. After the 10th item, it shows item 9 again and then item 1 and 2.
This RecyclerView produces the bug:
mViewModel.getmPremixableIngredientsLive().observe(getViewLifecycleOwner(), new Observer<ArrayList<Ingredient>>() {
#Override
public void onChanged(ArrayList<Ingredient> premixableIngredients) {
ShowIngredientsRecyclerViewAdapter premixableComponentsRecyclerViewAdpater = new ShowIngredientsRecyclerViewAdapter(premixableIngredients);
mBinding.premixableIngredientsRecyclerview.setAdapter(premixableComponentsRecyclerViewAdpater);
GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), 1);
mBinding.premixableIngredientsRecyclerview.setLayoutManager(gridLayoutManager);
}
});
This is the code of my adapter:
public class ShowIngredientsRecyclerViewAdapter extends RecyclerView.Adapter<ShowIngredientsRecyclerViewAdapter.ViewHolder> {
private ArrayList<Ingredient> mIngredients;
private IngredientRecyclerViewItemBinding mBinding;
public ShowIngredientsRecyclerViewAdapter(ArrayList<Ingredient> ingredients) {
mIngredients = ingredients;
}
#NonNull
#Override
public ShowIngredientsRecyclerViewAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
mBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.ingredient_recycler_view_item,
parent,
false
);
return new ViewHolder(mBinding.getRoot());
}
#Override
public void onBindViewHolder(#NonNull ShowIngredientsRecyclerViewAdapter.ViewHolder holder, int position) {
mBinding.position.setText((position + 1) + ".");
mBinding.componentName.setText(mIngredients.get(position).getmComponent().getmName());
mBinding.amount.setText((mIngredients.get(position).getGrammPerCow() / 1000) + "kg/cow");
mBinding.dragBtn.setVisibility(View.GONE);
}
#Override
public int getItemCount() {
return mIngredients.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(#NonNull View itemView) {
super(itemView);
}
}
}
xml layout for the item:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="#+id/ingredient_recyclerview_item_linearLayout"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:paddingLeft="0dp"
android:paddingStart="0dp">
<TextView
android:id="#+id/position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"/>
<TextView
android:id="#+id/component_name"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Component 1"/>
<TextView
android:id="#+id/amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="50kg"/>
<ImageButton
android:id="#+id/drag_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="#drawable/ic_drag_grey"
android:background="#android:color/transparent"
android:layout_marginStart="8dp"/>
</LinearLayout>
</layout>
You are not implementing databinding correctly in your recyclerview. You shouldn't declare mBinding as a global variable in the adapter. Instead, it should be a global variable in the custom viewholder class. I share an example recyclerview adapter code with databinding below. You can adapt it to your case. Notice that binding is not a global variable in the adapter, but in the custom viewholder. And the viewholder accept binding as argument, so we pass binding instance to the viewholder when we create the viewholder and we use that instance when onBindViewHolder is called.
public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductViewHolder> {
private final List<Product> mProductList;
private final ProductItemClickListener mListener;
ProductAdapter(#NonNull List<Product> productList, ProductItemClickListener listener) {
mProductList =productList;
mListener = listener;
}
#NonNull
#Override
public ProductViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
ItemProductBinding binding = DataBindingUtil
.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_product,
parent, false);
return new ProductViewHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull ProductViewHolder holder, int position) {
holder.bind(mProductList.get(position), mListener);
}
#Override
public int getItemCount() {
return mProductList.size();
}
class ProductViewHolder extends RecyclerView.ViewHolder{
final ItemProductBinding binding;
ProductViewHolder(ItemProductBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
void bind(Product currentProduct, ProductItemClickListener clickListener){
//For each item, corresponding product object is passed to the binding
binding.setProduct(currentProduct);
binding.setProductItemClick(clickListener);
//This is to force bindings to execute right away
binding.executePendingBindings();
}
}
public interface ProductItemClickListener {
void onProductItemClicked(Product product);
}
}

OnClickListener for headers of RecyclerView is not getting invoked

I want to sort my recyclerview on click of a column header (e.g if Name and age are two columns of the recyclerview, I want to sort the list when I click on header Age). I have added the headers by including the headers layout in my activity layout that contains recyclerview.
<RelativeLayout
android:id="#+id/activity_main"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<include layout="#layout/header_item"/>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipeContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:divider="#null"
android:paddingBottom="#dimen/intrinsic_padding"
android:paddingTop="50dp"/>
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
In my activity class with above layout, I set the adapter for recyclerview and add onClickListener for Textview Age.
public class MainActivity extends AppCompatActivity {
TextView ageTextview;
private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<User> usersList=initUsers();
// Create RecyclerView List
mRecyclerView=findViewById(R.id.my_recycler_view);
mRecyclerView.setHasFixedSize(true);
mLayoutManager=new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter=new RecyclerAdapter(usersList, getApplicationContext());
mRecyclerView.setAdapter(mAdapter);
ageTextview=findViewById(R.id.ageHeader);
ageTextview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(context, "Column -- Name", Toast.LENGTH_SHORT).show();
Collections.sort(usersList, new Comparator<User>() {
#Override
public int compare(User user1, User user2) {
if (user1.getAge() < user2.getAge())
return -1;
else if (user1.getAge()==user2.getAge())
return 0;
else
return 1;
}
});
mAdapter.notifyDataSetChanged();
}
});
}
}
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ArrayList<User> users;
private Context context;
RecyclerAdapter(ArrayList<User> users, Context context)
{
this.users=users;
this.context=context;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.item_user, parent, false);
return new VHItem(view);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof VHItem) {
User user = users.get(position);
((VHItem)holder).userName.setText(user.getUserName());
((VHItem)holder).age.setText(String.valueOf(user.getAge()));
}
}
#Override
public int getItemCount() {
if (users !=null)
{
System.out.println("Number of users: "+ users.size());
return users.size();
}
else {
return 0;
}
}
public static class VHItem extends RecyclerView.ViewHolder {
public View view;
TextView userName;
TextView age;
VHItem(View view)
{
super(view);
this.view=view;
this.userName=view.findViewById(R.id.username);
this.age=view.findViewById(R.id.age);
}
}
}
When I click on Age header, there is no effect. Neither sorting happens nor Toast is displayed. Looks like onClick() method is not getting invoked at all. The log does not show any errors.
In the layout file header_item, I have tried adding android:clickable="true", android:focusable="false". But nothing works.
Please help me understand if I am doing anything wrong. Is adding headers using a separate layout file the problem?
Thanks
Try to get OnClickListner from ViewHolder. Like this,
class ViewHolder extends RecyclerView.ViewHolder{
public TextView ageTextview;
public ViewHolder(View itemView) {
super(itemView);
ageTextview = temView.findViewById(R.id.ageTextview) ;
ageTextview.setOnClickListener(..);
}
}

Set Sticky Header and Items Layouts Recyclerview

I want to build a complex layout using recyclerview android. In the layout, I want to have a camera button to the top left fixed and a recyclerview wrapped around it with gallery images. I have checked flexbox layout manager for recyclerview but it doesn't seem to match my use-case.
I want the header to be non-repeating and not to scroll with other items vertically. Here's the layout for the header:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/shareLayout"
android:layout_width="185dp"
android:layout_height="135dp"
android:layout_below="#id/trendingToolbar"
android:background="#color/black">
<ImageView
android:id="#+id/cameraShareIV"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
app:srcCompat="#drawable/camera_white" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/cameraShareIV"
android:layout_centerHorizontal="true">
<TextView
android:id="#+id/infoTxt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginLeft="20dp"
android:gravity="center_horizontal"
android:text="#string/share_pic_video"
android:textColor="#android:color/white"
android:textSize="13sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/infoTxt"
android:layout_marginLeft="16dp"
android:text="#string/share_timeout_txt"
android:textColor="#color/colorPrimaryDark"
android:textSize="11sp"
android:textStyle="bold" />
</RelativeLayout>
and in my activity, here's the XML:
<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"
tools:context="base.android.com.thumbsapp.UI.Fragments.TrendingFragment">
<include layout="#layout/trending_toolbar"
android:id="#+id/trendingToolbar"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/trendingRV"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/trendingToolbar"/>
Previously, I had the header inside the activity XML but had no way to wrap a recyclerview around it. So, I have decide to use an adapter like below:
public class TrendingAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final String TAG = TrendingAdapter.class.getSimpleName();
private Context context;
private List<Trending> itemList;
private static final int HEADER = 0;
private static final int ITEMS = 1;
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
switch (viewType){
case HEADER:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.trending_header, parent, false);
return new TrendingHeaderViewHolder(v);
case ITEMS:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.trending_items_layout, parent, false);
return new TrendingItemsViewHolder(v);
}
return null;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Trending tr = itemList.get(position);
if (holder instanceof TrendingHeaderViewHolder){
((TrendingHeaderViewHolder) holder).cameraShareIV.setOnClickListener( view -> {
// TODO: 4/2/2018 select image from gallery
});
} else if (holder instanceof TrendingItemsViewHolder){
// TODO: 4/2/2018 populate gallery items here with picasso
}
}
#Override
public int getItemCount() {
return itemList.size();
}
#Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
}
I'm confused how to make the header stick and also what to do for getItemViewType method.
Is this the right way to approach this?
Can anyone help out? Thanks.
For this lay out i suggest better option is use this header view
https://github.com/edubarr/header-decor
To make things simple i suggest you to look into this library
In your XML Place RecylerView into StickyHeaderView,choose horizontal or vertical orientation for your RecylerView
<tellh.com.stickyheaderview_rv.StickyHeaderView
android:id="#+id/stickyHeaderView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"
android:scrollbars="vertical" />
</tellh.com.stickyheaderview_rv.StickyHeaderView>
Create data bean class for each item type in RecyclerView. They should extend DataBean. Override the method
public boolean shouldSticky() to decide whether the item view should be suspended on the top.
public class User extends DataBean {
private String login;
private int id;
private String avatar_url;
private boolean shouldSticky;
#Override
public int getItemLayoutId(StickyHeaderViewAdapter adapter) {
return R.layout.item_user;
}
public void setShouldSticky(boolean shouldSticky) {
this.shouldSticky = shouldSticky;
}
// Decide whether the item view should be suspended on the top.
#Override
public boolean shouldSticky() {
return shouldSticky;
}
}
public class ItemHeader extends DataBean {
private String prefix;
#Override
public int getItemLayoutId(StickyHeaderViewAdapter adapter) {
return R.layout.header;
}
#Override
public boolean shouldSticky() {
return true;
}
}
Create ViewBinder to bind different type views with specific data beans. As you see, provideViewHolder(View itemView) corresponds for onCreateViewHolder in RecyclerView, and bindView corresponds for onBindViewHolder in RecyclerView.
public class ItemHeaderViewBinder extends ViewBinder<ItemHeader, ItemHeaderViewBinder.ViewHolder> {
#Override
public ViewHolder provideViewHolder(View itemView) {
return new ViewHolder(itemView);
}
#Override
public void bindView(StickyHeaderViewAdapter adapter, ViewHolder holder, int position, ItemHeader entity) {
holder.tvPrefix.setText(entity.getPrefix());
}
#Override
public int getItemLayoutId(StickyHeaderViewAdapter adapter) {
return R.layout.header;
}
static class ViewHolder extends ViewBinder.ViewHolder {
TextView tvPrefix;
public ViewHolder(View rootView) {
super(rootView);
this.tvPrefix = (TextView) rootView.findViewById(R.id.tv_prefix);
}
}
}
Instantiate StickyHeaderViewAdapter for RecyclerView and register ViewBinders for each item types.
rv = (RecyclerView) findViewById(R.id.recyclerView);
rv.setLayoutManager(new LinearLayoutManager(this));
List<DataBean> userList = new ArrayList<>();
adapter = new StickyHeaderViewAdapter(userList)
.RegisterItemType(new UserItemViewBinder())
.RegisterItemType(new ItemHeaderViewBinder());
rv.setAdapter(adapter);

RecyclerView creating margins between items

first of all, sorry if this a stupid question. I'm not being lazy.
So, the problem is, im trying to implement CardView/RecyclerView in an Android app. I made it, but the problem is that the cards are spaced one from another, and i don't know how to fix it. I explored the code but everything seems to be fine.
The code :
RecyclerView
<android.support.v7.widget.RecyclerView
android:id="#+id/collapsing_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:padding="#dimen/activity_horizontal_margin">
</android.support.v7.widget.RecyclerView>
CardView item
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:id="#+id/card_view"
card_view:cardBackgroundColor="#color/colorAccent"
android:layout_gravity="center"
android:layout_width="fill_parent"
android:layout_height="100dp"
android:layout_margin="5dp"
card_view:cardCornerRadius="2dp">
<TextView
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="New Text"
android:id="#+id/cardview.name" />
</android.support.v7.widget.CardView>
</LinearLayout>
Adapter :
public class MyRecyclerViewAdapter extends RecyclerView
.Adapter<MyRecyclerViewAdapter
.DataObjectHolder> {
private static String LOG_TAG = "MyRecyclerViewAdapter";
private ArrayList<CardViewItem> mDataset;
private static MyClickListener myClickListener;
public static class DataObjectHolder extends RecyclerView.ViewHolder
implements View
.OnClickListener {
TextView label;
public DataObjectHolder(View itemView) {
super(itemView);
label = (TextView) itemView.findViewById(R.id.cardview_name);
Log.i(LOG_TAG, "Adding Listener");
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
myClickListener.onItemClick(getAdapterPosition(), v);
}
}
public void setOnItemClickListener(MyClickListener myClickListener) {
this.myClickListener = myClickListener;
}
public MyRecyclerViewAdapter(ArrayList<CardViewItem> myDataset) {
mDataset = myDataset;
}
#Override
public DataObjectHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.example_card_view, parent, false);
DataObjectHolder dataObjectHolder = new DataObjectHolder(view);
return dataObjectHolder;
}
#Override
public void onBindViewHolder(DataObjectHolder holder, int position) {
holder.label.setText(mDataset.get(position).getName());;
}
public void addItem(CardViewItem dataObj, int index) {
mDataset.add(index, dataObj);
notifyItemInserted(index);
}
public void deleteItem(int index) {
mDataset.remove(index);
notifyItemRemoved(index);
}
#Override
public int getItemCount() {
return mDataset.size();
}
public interface MyClickListener {
public void onItemClick(int position, View v);
}
}
Hope you guys can help me. Thanks!
EDIT: So, i found the answer. The coulprit was the android:layout_height="match_parent"
in my cardview item. I change it for "wrap_content" and fixed the problem. Thanks for the help guys! :)
Have you tried ItemDecoration with your RecyclerView ? That's usefull for customize your divider in a RecyclerView
You can look that :
My custom ItemDecoration use getItemOffsets() like this :
public class MyItemDecoration extends RecyclerView.ItemDecoration {
private final int decorationHeight;
private Context context;
public MyItemDecoration(Context context) {
this.context = context;
decorationHeight = context.getResources().getDimensionPixelSize(R.dimen.decoration_height);
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (parent != null && view != null) {
int itemPosition = parent.getChildAdapterPosition(view);
int totalCount = parent.getAdapter().getItemCount();
if (itemPosition >= 0 && itemPosition < totalCount - 1) {
outRect.bottom = decorationHeight;
}
}
}
}
Then you can set your R.dimen.decoration_height to the value what you want.
And in my activity, when I instantiate my RecyclerListView I need to do this :
myList.addItemDecoration(MyItemDecoration(this))
myList.setLayoutManager(LinearLayoutManager(this))
myList.setAdapter(adapter)
I hope this will help you
Remove this line from your CardView item .xml
android:layout_margin="5dp"
You probably wanted it to be padding instead of margin
In order to create spacings in between items, we could use RecyclerView's ItemDecorator's:
addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State,
) {
super.getItemOffsets(outRect, view, parent, state)
if (parent.getChildAdapterPosition(view) > 0) {
outRect.top = 8.dp // Change this value with anything you want. Remember that you need to convert integers to pixels if you are working with dps :)
}
}
})
A few things to have in consideration given the code I pasted:
You don't really need to call super.getItemOffsets but I chose to, because I want to extend the behavior defined by the base class. If the library got an update doing more logic behind the scenes, we would miss it.
As an alternative to adding top spacing to the Rect, you could also add bottom spacing, but the logic related to getting the last item of the adapter is more complex, so this might be slightly better.
I used an extension property to convert a simple integer to dps: 8.dp. Something like this might work:
val Int.dp: Int
get() = (this * Resources.getSystem().displayMetrics.density + 0.5f).toInt()
// Extension function works too, but invoking it would become something like 8.dp()
CardView by default adds padding. Try using CardView attribute card_view:contentPadding but set the negative values for the attribute like this
card_view:contentPadding="-3"

ReclyclerView and CardView, onclick method perform the action on several CardViews at same time

I've got a RecyclerView which populates from an ArrayList. The output is a CardView layout.
In the Cardview, there are 2 buttons amongst other Views.
They only have to read the current value of a TextView, which by default is 1, and increase or decrease it.
The Arraylist contains 8 items.
When I run the app the UI works fine. Trouble is when I try to modify the value of the TextView.
The value is correctly increased and decreased on the CardView I'm working on, but ALSO the value is modified on another CardView. And in that second CardView, modifying its TextView value, also modifies the first one.
So, what am I doing wrong?
This is my Fragment:
public class Fragment_rosas extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_rosas,container,false);
RecyclerView recyclerview_rosas;
RecyclerView.Adapter adaptador_rv_rosas;
RecyclerView.LayoutManager lm_rosas;
List rosas = new ArrayList();
rosas.add(new Tropa(1,R.drawable.minibarbaro, getResources().getString(R.string.barbaro),7,1));
recyclerview_rosas = (RecyclerView) view.findViewById(R.id.recyclerView_tropasRosas);
recyclerview_rosas.setHasFixedSize(true);
lm_rosas = new LinearLayoutManager(getContext());
recyclerview_rosas.setLayoutManager(lm_rosas);
adaptador_rv_rosas = new AdaptadorTropa(rosas);
recyclerview_rosas.setAdapter(adaptador_rv_rosas);
return view;
}
}
And here the part of code on my Adapter:
#Override
public void onBindViewHolder(final TropaViewHolder viewHolder, int i) {
viewHolder.imagen.setImageResource(items.get(i).getImagen());
viewHolder.nombre.setText(items.get(i).getNombre());
viewHolder.maxnivel.setText(String.valueOf(items.get(i).getNivelMax()));
viewHolder.espacioencamp.setText((String.valueOf(items.get(i).getEspacioEnCamp())));
final String nombre = items.get(i).getNombre();
final int maxnivel = items.get(i).getNivelMax();
viewHolder.nivelmas.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String niveltemp = viewHolder.nivel.getText().toString();
String nivelmaxtemp = viewHolder.maxnivel.getText().toString();
int nivel = Integer.parseInt(niveltemp);
int maxxnivel = Integer.parseInt(nivelmaxtemp);
int nuevonivel = nivel+1 ;
if (nuevonivel<=maxxnivel) {
viewHolder.txtv_nivel.setText(String.valueOf(nuevonivel));
}
}
});
My OnCreateViewHolder (nothing really happens here):
#Override
public TropaViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.cardview, viewGroup, false);
return new TropaViewHolder(v);
}
Here is the solution, as mentioned in the comment above, it addresses two problems:
1. positiontoValueMap - saves current value for each position
2. onclicklistener is passed to the ViewHolder in onCreateViewHolder
Adapter Class
public class MyAdapter extends RecyclerView.Adapter {
private Context context;
private List<String> dataList;
private Map<Integer, Integer> positionToValueMap = new HashMap<>();
public MyAdapter(Context context, List<String> dataList) {
this.context = context;
this.dataList = dataList;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.recycler_view_item, null, false);
return new MyViewHolder(view, new OnRecyclerItemClickListener());
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((MyViewHolder) holder).onRecyclerItemClickListener.updatePosition(position);
((MyViewHolder) holder).position.setText("" + position);
((MyViewHolder) holder).title.setText(dataList.get(position));
int valueToDisplay = 1;
if(positionToValueMap.containsKey(position)) {
valueToDisplay = positionToValueMap.get(position);
} else {
positionToValueMap.put(position, valueToDisplay);
}
((MyViewHolder) holder).valueView.setText("value: " + valueToDisplay);
}
#Override
public int getItemCount() {
return dataList.size();
}
private class MyViewHolder extends RecyclerView.ViewHolder {
private OnRecyclerItemClickListener onRecyclerItemClickListener;
private TextView position;
private TextView title;
private TextView valueView;
public MyViewHolder(View itemView, OnRecyclerItemClickListener onRecyclerItemClickListener) {
super(itemView);
itemView.setOnClickListener(onRecyclerItemClickListener);
this.onRecyclerItemClickListener = onRecyclerItemClickListener;
this.position = (TextView) itemView.findViewById(R.id.position);
this.title = (TextView) itemView.findViewById(R.id.title);
this.valueView = (TextView) itemView.findViewById(R.id.value_view);
}
}
private class OnRecyclerItemClickListener implements View.OnClickListener {
private int position = -1;
public void updatePosition(int position) {
this.position = position;
}
#Override
public void onClick(View v) {
int oldValue = positionToValueMap.get(position); // get current value
oldValue++; // increment
positionToValueMap.put(position, oldValue); // save current value
notifyItemChanged(position); // update clicked view so that it picks up the new saved value from the positionToValueMap in onBindViewHolder
}
}
}
RecyclerView item layout
<TextView
android:id="#+id/position"
android:layout_width="30dp"
android:layout_height="50dp"
android:textColor="#android:color/white"
android:gravity="center"
android:background="#android:color/holo_green_light"
android:layout_alignParentLeft="true"/>
<TextView
android:id="#+id/title"
android:layout_width="50dp"
android:layout_height="50dp"
android:textColor="#android:color/white"
android:gravity="center"
android:background="#android:color/holo_green_dark"
android:layout_toRightOf="#id/position" />
<TextView
android:id="#+id/value_view"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textColor="#android:color/white"
android:gravity="center"
android:background="#android:color/holo_green_light"
android:layout_toRightOf="#id/title"
android:layout_alignParentRight="true"/>
</RelativeLayout>
And Activity to test it out
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
recyclerView.setAdapter(new MyAdapter(getApplicationContext(), getSampleData()));
}
private static List<String> getSampleData() {
List<String> dataList = new ArrayList<>();
dataList.add("zero");
dataList.add("one");
dataList.add("two");
dataList.add("three");
dataList.add("four");
dataList.add("five");
dataList.add("six");
dataList.add("seven");
dataList.add("eight");
dataList.add("nine");
dataList.add("ten");
dataList.add("eleven");
dataList.add("twelve");
dataList.add("thirteen");
dataList.add("fourteen");
dataList.add("fifteen");
dataList.add("sixteen");
dataList.add("seventeen");
dataList.add("eighteen");
dataList.add("nineteen");
dataList.add("twenty");
return dataList;
}
}
activity layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"/>
</RelativeLayout>

Categories

Resources