RecyclerView creating margins between items - android

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"

Related

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);

TransitionManager doesn't animate color change

I've followed Nick Butcher's Material Improvements I/O 2016 talk, and at about 6:00, he starts talking about animating list items. I've implemented the feature exactly as he was doing it, and the bound changes animate correctly, but color changes don't animate, despite him explicitly saying they would:
This is what the code looks like:
This is the relevant part of the RecyclerView.Adapter class:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item: Pair<String, String> = items[position]
holder.title.text = item.first
holder.subtitle.text = item.second
val isExpanded = position == expandedPosition
holder.subtitle.visibility = if (isExpanded) View.VISIBLE else View.GONE
holder.itemView.isActivated = isExpanded
holder.itemView.setOnClickListener {
expandedPosition = if (isExpanded) -1 else position
TransitionManager.beginDelayedTransition(recyclerView)
notifyDataSetChanged()
}
}
This is the layout I'm using for the items. ConstraintLayout is kind of overkill for the current layout setup, but I reduced the layout to create a minimal example.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/item_background"
android:stateListAnimator="#animator/item_elevation"
android:padding="8dp">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
tools:text="Title 1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<TextView
android:id="#+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Subtitle 1"
app:layout_constraintTop_toBottomOf="#id/title"
app:layout_constraintStart_toStartOf="parent"
/>
</android.support.constraint.ConstraintLayout>
And this is the background I'm using for the item layout:
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:enterFadeDuration="#android:integer/config_mediumAnimTime"
android:exitFadeDuration="#android:integer/config_mediumAnimTime">
<item android:state_activated="true"
android:drawable="#color/colorAccent" />
<item android:drawable="#color/colorPrimaryDark" />
</selector>
Make sure that you have stable ids for your dataset and call Recycler.Adapter#setHasStableIds(true). See
setHasStableIds().
You will also need to override getItemId() in the adapter to return a stable id. Also see notifyDataSetChanged for a short discussion of stable ids.
RecyclerView will attempt to synthesize visible structural change events for adapters that report that they have stable IDs when this method is used. This can help for the purposes of animation and visual object persistence but individual item views will still need to be rebound and relaid out.
Here is an demonstration with stable ids set to false:
and with stable ids set to true. I have make the transition time very long on the color change so it would be apparent.
RecyclerViewAdapter.java
This is the adapter used in the demo. TransitionManager.beginDelayedTransition(mRecyclerView) has been removed to let RecyclerView better handle the animation.
class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
RecyclerViewAdapter() {
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view;
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
return new ItemViewHolder(view);
}
private int mExpandedPosition = RecyclerView.NO_POSITION;
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
final ItemViewHolder vh = (ItemViewHolder) holder;
final TextView subTitle = vh.mSubTitle;
vh.mTitle.setText(titleForPosition(position));
subTitle.setText(subTitleForPosition(position));
final boolean isExpanded = position == mExpandedPosition;
subTitle.setVisibility((isExpanded) ? View.VISIBLE : View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mExpandedPosition = isExpanded ? RecyclerView.NO_POSITION : vh.getAdapterPosition();
notifyDataSetChanged();
}
});
}
private String titleForPosition(int position) {
return "Title " + position;
}
private String subTitleForPosition(int position) {
return "Subtitle " + position;
}
#Override
public int getItemCount() {
return 5;
}
#Override
public long getItemId(int position) {
return titleForPosition(position).hashCode();
}
static class ItemViewHolder extends RecyclerView.ViewHolder {
private final TextView mTitle;
private final TextView mSubTitle;
ItemViewHolder(View itemView) {
super(itemView);
mTitle = itemView.findViewById(R.id.title);
mSubTitle = itemView.findViewById(R.id.subtitle);
}
}
#SuppressWarnings("unused")
private final static String TAG = "RecyclerViewAdapter";
}

How can I set OnClickListener to two buttons in RecyclerView?

In my android app I have one activity to show a RecyclerView in which each row is composed by a TextView and 2 buttons. Just like that:
Well, following many explanations of internet I have made this Adapter:
public class ListAdapter extends
RecyclerView.Adapter<ListAdapter.ViewHolder> {
private LayoutInflater layoutInflater;
protected Vector<List> lists;
public ListAdapter(Context context, Vector lists) {
layoutInflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.lists = lists;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = layoutInflater.inflate(R.layout.listadapter, null);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
List list = lists.elementAt(position);
holder.name.setText(list.getName());
//holder.delete.set HOW DO I GET BUTTON HERE?
}
#Override
public int getItemCount() {
return lists.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {
public TextView name;
public Button edit;
public Button delete;
public ViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.listname);
edit = (Button) itemView.findViewById(R.id.edit);
delete = (Button) itemView.findViewById(R.id.delete);
edit.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.edit:
break;
case R.id.delete:
break;
default:
break;
}
}
}
}
I haven't found examples of having 2 buttons (there are only examples of adding images) in each view of the recyclerview, as in the first image. I mean, guess I have 10 List elements in my RecyclerView, how can I have in each of them to one side a TextView with the name of the List and 2 buttons and handle clickListener?. As in the following image, which I have done by hand for you to see what I am talking about:
This is the activity that will show the recyclerView; don't take it into account because I am pretty sure the way is wrong. I get lists information from sqlite database:
public class ShowListOfLists extends AppCompatActivity {
private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_list_of_lists);
LocalDB localDB = new LocalDB(this, "localBD", null, 1);
Vector <List> ListAdapter = localDB.getAllPrivateList();
recyclerView = (RecyclerView) findViewById(R.id.recyclerviewlistas);
recyclerView.setAdapter(new ListAdapter(this, ListAdapter));
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
}
A list have the following attributes:
-idList int
-name String
-Description String
-creationDate Date
-active int (0 or 1)
-deactivationDate Date
Thank you in advance.
/*******************************EDIT****************************************/
Thank to you I have been able to show the recyclerView and it works well. But I see that:
Instead of that:
This the XML code and design of the adapter:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="#drawable/rectangle_bg"
android:orientation="horizontal"
android:weightSum="1"
android:layout_marginTop="5dp">
<TextView
android:id="#+id/listname"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_weight="0.75"
android:gravity="center_vertical"
android:text="TextView"
android:textSize="15sp"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.25"
android:gravity="end"
android:orientation="vertical">
<ImageButton
android:id="#+id/delete"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="#android:color/transparent"
android:contentDescription="Delete"
app:srcCompat="#android:drawable/ic_menu_delete" />
<ImageButton
android:id="#+id/edit"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="#android:color/transparent"
android:contentDescription="Edit"
app:srcCompat="#android:drawable/ic_menu_edit" />
</LinearLayout>
</LinearLayout>
And this is the XML of the recyclerview
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
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="wrap_content"
android:layout_height="match_parent"
tools:context="com.pc.kanayel.runoutof.ShowListOfLists"
android:orientation="vertical"
android:id="#+id/recyclerviewlistas">
</android.support.v7.widget.RecyclerView>
You just need to add some code to your ListAdapter. It is there, where you have to implement onClickListener. The code for your case should look something like the one below.
You can pass any parameter you need to. It depends on the functionality you want to achieve. Here I have demonstrated passing the order of item clicked inside of list.
Option 1 (power/performance efficient)
So, what does the code below actually mean? You have already created a ViewHolder and implemented OnClickListener. That is correct. Now you need to set OnClickListener to two buttons. But what these buttons will do when clicked is defined by the interface we created inside of ViewHolder.
When app will run RecyclerView will create as many ViewHolders as it is needs to fill the available screen with list items by calling onCreateViewHolder() method for each of viewholders. When you scroll up/down these ViewHolders will be reused. OnCreateViewHolder() is not called, only onBindViewHolder() is called to update the content of viewholder. The code below made so that, when ViewHolder is created it will also create an MyClickListener that will be used by OnClickListener of viewholder and when viewholder is reused it will not create new OnClickListener. It means that our method is performance efficient.
Option 2 (not efficient)
You could also setOnClickListener() inside of onBindViewHolder(). However, as I have mentioned in the paragraph above, this method is called every time you scroll to update the content of viewholder. Now it would create new OnClickListener object everytime. While Option 1 has OnClickListener on every ViewHolder and it reuses them.
Code for Option 1
public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder> {
private LayoutInflater layoutInflater;
protected Vector<List> lists;
public ListAdapter(Context context, Vector lists) {
layoutInflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.lists = lists;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = layoutInflater.inflate(R.layout.listadapter, null);
ViewHolder holder = new ViewHolder(view, new MyClickListener() {
#Override
public void onEdit(int p) {
// Implement your functionality for onEdit here
}
#Override
public void onDelete(int p) {
// Implement your functionality for onDelete here
}
});
return holder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
List list = lists.elementAt(position);
holder.name.setText(list.getName());
}
#Override
public int getItemCount() {
return lists.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
MyClickListener listener;
TextView name;
Button edit;
Button delete;
public ViewHolder(View itemView, MyClickListener listener) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.listname);
edit = (Button) itemView.findViewById(R.id.edit);
delete = (Button) itemView.findViewById(R.id.delete);
this.listener = listener;
edit.setOnClickListener(this);
delete.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.edit:
listener.onEdit(this.getLayoutPosition());
break;
case R.id.delete:
listener.onDelete(this.getLayoutPosition());
break;
default:
break;
}
}
}
public interface MyClickListener {
void onEdit(int p);
void onDelete(int p);
}
}
Edit for your second question
To make list items take all the width set the width of your recyclerview to match_parent. It is also better to make it a child view of some layout. For instance as given below.
<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="com.pc.kanayel.runoutof.ShowListOfLists">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerviewlistas"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<RelativeLayout>
And change this code inside of your adapter:
View view = layoutInflater.inflate(R.layout.listadapter, null);
to this:
View view = layoutInflater.inflate(R.layout.listadapter, parent, false);
You can do simply like this:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
List list = lists.elementAt(position);
holder.name.setText(list.getName());
holder.edit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Do your stuff
}
});
holder.delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Do your stuff
}
});
}

RecyclerView behaviour issue in nestedscrollview

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
}
}

Show RecyclerView in popup window

I have a RecyclerView, when RecyclerView item clicked, want to open a popup window which contains another RecyclerView. It is almost done, but in popup window, cardviews don't appear. I can't figure out why, can any one help?
1- My Main RecyclerView Adapter
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private ArrayList<Mission> mDataset;
private Context mContext;
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(ArrayList<Mission> myDataset, Context context) {
mDataset = myDataset;
this.mContext = context;
}
// Create new views (invoked by the layout manager)
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View v = LayoutInflater.from(mContext)
.inflate(R.layout.mission_card_item, parent, false);
// set the view's size, margins, paddings and layout parameters
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.mTextView.setText(mDataset.get(position).getName());
holder.mPuanView.setText(mDataset.get(position).getPoint());
holder.mRankView.setText(mDataset.get(position).getRank());
holder.btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext,"Buton Clicked", Toast.LENGTH_SHORT).show();
}
});
}
#Override
public int getItemCount() {
return mDataset.size();
}
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public class MyViewHolder extends RecyclerView.ViewHolder {
public CardView mCardView;
public TextView mTextView;
public TextView mPuanView;
public TextView mRankView;
public Button btnAdd;
public MyViewHolder(final View itemView) {
super(itemView);
mCardView = (CardView) itemView.findViewById(R.id.card_view);
mTextView = (TextView) itemView.findViewById(R.id.tv_text);
mRankView = (TextView) itemView.findViewById(R.id.tv_rank);
mPuanView = (TextView) itemView.findViewById(R.id.tv_puan);
btnAdd = (Button) itemView.findViewById(R.id.button_add);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showPopup();
Toast.makeText(itemView.getContext(),"Element " + getAdapterPosition() + " clicked", Toast.LENGTH_SHORT).show();
Log.d("hello", "Element " + getAdapterPosition() + " clicked.");
}
});
}
}
public void showPopup(){
final View popupView = LayoutInflater.from(mContext).inflate(R.layout.recycler_popup_window, null);
final PopupWindow popupWindow = new PopupWindow(popupView, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
Button btn = (Button) popupView.findViewById(R.id.button);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popupWindow.dismiss();
}
});
RecyclerView recyclerView = (RecyclerView) popupView.findViewById(R.id.rv_recycler_view);
ArrayList<String> data = new ArrayList<>();
data.add("my data");
data.add("my test data");
PopupRecyclerViewAdapter adapter = new PopupRecyclerViewAdapter(mContext,data);
recyclerView.setAdapter(adapter);
popupWindow.showAtLocation(popupView,Gravity.CENTER, 0, 0);
}
}
2- My second RecyclerView adapter, its for popup window
public class PopupRecyclerViewAdapter extends RecyclerView.Adapter<PopupRecyclerViewAdapter.MyViewHolder>{
private Context mContext;
private ArrayList<String> data;
public PopupRecyclerViewAdapter(Context mContext, ArrayList<String> data) {
this.mContext = mContext;
this.data = data;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(mContext).inflate(R.layout.recycler_popup_card_item, parent,false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.mTextView.setText(data.get(position));
}
#Override
public int getItemCount() {
return data.size();
}
//View Holder
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public MyViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.tv_text2);
}
}
}
3- Layout for Recycler popup window
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_recycler_view2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/button"
android:background="#ff4545">
</android.support.v7.widget.RecyclerView>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Close"
android:id="#+id/button"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
4- CardView Layout for popup RecyclerView
<?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="match_parent">
<android.support.v7.widget.CardView
android:id="#+id/card_view"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_margin="10dp"
android:layout_height="wrap_content"
card_view:cardCornerRadius="4dp"
card_view:elevation="14dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:layout_width="match_parent"
android:layout_height="175dp"
android:id="#+id/imageView2"
android:src="#mipmap/testimage"
android:layout_marginBottom="10dp"
android:scaleType="centerCrop"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/tv_text2"
android:text="Blah blah blah..."
android:gravity="center"
android:layout_marginBottom="10dp"/>
</LinearLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>
Add the below lines in your recyclerview popup:
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyler_view.setLayoutManager(mLayoutManager);
You may, like me, find this becomes rather a lot of boilerplate code just for a simple Popup Window with a list of clickable items, and so I made a custom Drop Down class which is easily reusable. You just need the PopupWindow custom class, and the RecyclerView adapter, as well as an item data class (in Kotlin), and this is then easily reusable.
The DropDown class:
class DropDown(context: Context, items: List<DropDownItem>, val listener: DropDownClickListener) : PopupWindow(context) {
private val binding = DropdownLayoutBinding.inflate(LayoutInflater.from(context))
init {
contentView = binding.root
setBackgroundDrawable(null)
elevation = 8f
isOutsideTouchable = true
isFocusable = true
binding.recyclerView.adapter = DropDownItemAdapter(items) { item -> onItemClicked(item) }
setOnDismissListener { listener.onMenuDismissed() }
}
private fun onItemClicked(item: DropDownItem) {
listener.onMenuItemClicked(item)
dismiss()
}
fun show(anchor: View) {
showAsDropDown(anchor, 0, 20)
}
interface DropDownClickListener {
fun onMenuItemClicked(item: DropDownItem)
fun onMenuDismissed()
}
}
data class DropDownItem(
val text: String,
val icon: Int
)
With accompanying layout code:
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/rounded_corners"
android:id="#+id/recycler_view"
xmlns:tools="http://schemas.android.com/tools"
tools:listitem="#layout/item_drop_down"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Explanation: This reusable new DropDown class uses ViewBinding of a simple layout that is simply a RecyclerView (with whatever Item layout you want, again mine is a reusable one called item_drop_down, a TextView with a Drawable.)
The constructor just needs three things, the context, the list of drop down items (I made a DropDownItem data class, which for simplicity I put in the same file), and then the listener - an interface also defined in this class).
In the init for this class, we set the content view, setBackgroundDrawable to null to get rid of an ugly border, give it some elevation, then make it dismissable by touching outside or pressing the back button. We then set the recyclerview adapter here from the list of items in the constructor. Finally, we set the on dismiss listener and defer to our interface.
Now the adapter code:
class DropDownItemAdapter(val items: List<DropDownItem>, val clickListener: (DropDownItem) -> Unit) : RecyclerView.Adapter<DropDownItemAdapter.ViewHolder>() {
override fun getItemCount() = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(ItemDropDownBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
inner class ViewHolder(val binding: ItemDropDownBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: DropDownItem) {
binding.textView.text = item.text
binding.textView.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(binding.root.context, item.icon), null, null, null)
itemView.setOnClickListener { clickListener(item) }
}
}
}
This is just a straight forward RecyclerView adapter, and rather than another interface, the only thing this needs to notify is when an item is clicked, so simply accept a function parameter in the constructor for handling the clicks.
Altogether, it looks like this in the calling code:
DropDown(view.context, dropDownMenu, this).show(view)
Where dropDownMenu is a list of DropDownItem.
You can then handle the interface functions as desired:
override fun onMenuItemClicked(item: DropDownItem) {
when (item.text) {
"Delete" -> // respond as desired
"Edit" -> // simply inspect the text, or alter your DropDownItem to have an ID
}
}
override fun onMenuDismissed() {
// it may be useful to know when the popup is dismissed
// in my case, I select the item which has been long pressed, and want to know when to un-select it
}
With a nice rounded corners background on my DropDown layout, and being able to know when popup showing and when dismissed to highlight my chosen item, I get this nice effect:

Categories

Resources