Fast Scrolling on RecyclerView - android

I would like to enable fling/fast scrolling on the recycler view. What I mean is that if user performs fling motion, the view continues scrolling and starts decelerating (I guess that's called fast scrolling).
I have a RecyclerView in a ScrollView
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scroll">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/items"/>
</ScrollView>
I set layout manager to Linear
RecyclerView itemsView = findViewById(R.id.items);
stocksView.setLayoutManager(new LinearLayoutManager(this));
I do have a custom adapter attached to the RecyclerView
public abstract class RecyclerAdapter<E>
extends RecyclerView.Adapter<ItemView>
{
private ItemTouchHelper touchHelper;
protected final List<E> items = new ArrayList<>();
protected RecyclerAdapter()
{
}
#Override
public void onBindViewHolder(#NonNull final ItemView holder, int position)
{
}
#Override
public void onAttachedToRecyclerView(#NonNull RecyclerView recyclerView)
{
super.onAttachedToRecyclerView(recyclerView);
touchHelper = new ItemTouchHelper(new ItemTouchHelperCallback(this));
touchHelper.attachToRecyclerView(recyclerView);
}
#Override
public int getItemCount()
{
return items.size();
}
}
And my ItemTouchHelperCallback is
public class ItemTouchHelperCallback
extends ItemTouchHelper.Callback
{
ItemTouchHelperCallback(RecyclerAdapter adapter)
{
}
#Override
public boolean isItemViewSwipeEnabled()
{
return true;
}
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
{
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlags = 0;
return ItemTouchHelper.Callback.makeMovementFlags(dragFlags, swipeFlags);
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target)
{
if (source.getItemViewType() != target.getItemViewType())
{
return false;
}
return true;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i)
{
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState)
{
// We only want the active item to change
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE)
{
// Let the view holder know that this item is being moved or dragged
((ItemView) viewHolder).onItemSelected();
}
super.onSelectedChanged(viewHolder, actionState);
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
{
super.clearView(recyclerView, viewHolder);
// Tell the view holder it's time to restore the idle state\
((ItemView) viewHolder).onItemClear();
}
}
Yet somehow whenever I try to scroll it stops the second I lift my finger. How do I get it to continue trying to scroll.

Remove the ScrollView from your layout, and don't use wrap_content for the RecyclerView's height. RecyclerView already provides scrolling by itself, and you can use whatever layout_ attributes you had on the ScrollView to make the RecyclerView the right size.

You wrote this:
I have a RecyclerView in a ScrollView
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scroll">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/items"/>
But RecyclerView already supports fling, remove ScrollView from xml file. And set the height of RecyclerView match_parent, like below:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/items"/>

While using scroll view you need to off nestedscroll enable.
recyclerview.setNestedScrollingEnabled(false);

1- Change ScrollView to NestedScrollView
2- Use app:layout_behavior="#string/appbar_scrolling_view_behavior" in both Recyclerview and NestedScrollView
<NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:id="#+id/scroll">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:id="#+id/items"/>
3- Use recyclerview.setNestedScrollingEnabled(false);

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

Layout_behavior with an Adapter inside another one

I have been searching and found some close questions and answer but none of them works.
I'm using CoordinatorLayout for a list RecyclerView with AppBarLayout and ToolBar.
My Goal:
The ideia is very simple, when you roll the view first the AppBar Collapse in the Toolbar and after that the RecyclerView start to scroll.
My Hierarchy:
My Fragment RecyclerView(vertical) has a Adapter-A with a new layout, and this adapter call a second adapter-B for the RecyclerView(horizontal).
So I Have a struct like this:
(is not a code, just to show how works)
Fragment AppBarLayout{..}
Fragment Recycler View (Vertical) {
Adapter-A Text;
Adapter-A Recycler View (Horizontal){
Adapter-B Img;
Adapter-B Text;
}
}
What is the problem:
If I click in the RecyclerView(Vertical) or Adapter-A Img it works fine.
But If I click in Adapter B content(Img and Text) it scroll both Recycler Views but not the AppBar.
What I'm doing:
I use
app:layout_behavior="#string/appbar_scrolling_view_behavior"
recyclerList.setHasFixedSize(true)
recyclerList.setNestedScrollingEnabled(true);
in both Recycler Views.
app:layout_scrollFlags="scroll|exitUntilCollapsed|enterAlways"
in the CollapsingToolbarLayout
I try to make this in java code:
recyclerList.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// If AppBar is fully expanded, revert the scroll.
if (!shouldScroll) {
recyclerList.scrollToPosition(0);
//Here I should make the AppBar Scroll, but AppBarLayout.scrollTo(dx, dy) don't work.
}
}
});
mainHomeAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
shouldScroll = verticalOffset != 0;
}
});
My code:
mainHome.xml
<android.support.design.widget.CoordinatorLayout
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="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/main_home_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<android.support.design.widget.AppBarLayout
android:id="#+id/main_home_app_bar_layout"
android:layout_width="match_parent"
android:layout_height="192dp"
android:background="#drawable/gradient_bg">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed|enterAlways">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="50dp"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
mainHome.java
#Override
protected void assignViews() {
mainHomeList = (RecyclerView) findViewById(R.id.main_home_list);
mainHomeAppBarLayout = (AppBarLayout) findViewById(R.id.main_home_app_bar_layout);
}
#Override
protected void prepareViews() {
mainHomeList.setHasFixedSize(true);
mainHomeList.setNestedScrollingEnabled(true);
linearLayoutManager = new LinearLayoutManager(getApplicationContext());
mainHomeList.setLayoutManager(linearLayoutManager);
initAdapterIfNecessary();
if (mainHomeList.getAdapter() == null)
mainHomeList.setAdapter(adapter);
}
adapter-A.java
public class MainHomeModulesAdapter extends RecyclerView.Adapter<MainHomeModulesAdapter.GroupViewHolder> {
private OnListItemClickedListener onListItemClickedListener = null;
private OnListItemClickedTwoListener onListItemClickedTwoListener = null;
private ArrayList<JSONMainModule> mainModules = new ArrayList<>();
#Override
public MainHomeModulesAdapter.GroupViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
View itemView = LayoutInflater.from(context).inflate(R.layout.a_main_home_module_item, parent, false);
return (new MainHomeModulesAdapter.GroupViewHolder(itemView));
}
#Override
public void onBindViewHolder(MainHomeModulesAdapter.GroupViewHolder holder, int position) {
//Place where put layout information
holder.mainHomeModuleList.setLayoutManager(new GridLayoutManager(context, Utils.calcGridSpaceCount(context, 2))); //For two elements
holder.mainHomeModuleList.setAdapter(holder.mainHomeContentAdapter);
holder.mainHomeContentAdapter.updateListContent(mainModules.get(position).getModuleContent(), mainModules.get(position).getModule());
}
#Override
public int getItemCount() {
return mainModules.size();
}
public void setOnListItemClickedListener(OnListItemClickedListener onListItemClickedListener) {
this.onListItemClickedListener = onListItemClickedListener;
}
public void setOnListItemClickedTwoListener(OnListItemClickedTwoListener onListItemClickedTwoListener){
this.onListItemClickedTwoListener = onListItemClickedTwoListener;
}
public void updateListContent(ArrayList<JSONMainModule> mainModules) {
this.mainModules = mainModules;
notifyDataSetChanged();
}
public JSONMainModule getListContent(int pos) {
return mainModules.get(pos);
}
class GroupViewHolder extends ParentViewHolder {
TextView mainHomeModuleText;
Button mainHomeModuleBtn;
RecyclerView mainHomeModuleList;
MainHomeContentAdapter mainHomeContentAdapter; //Adapter-B
private GroupViewHolder(View itemView) {
super(itemView);
mainHomeModuleText = (TextView) itemView.findViewById(R.id.main_home_module_title);
mainHomeModuleBtn = (Button) itemView.findViewById(R.id.main_home_module_btn);
mainHomeModuleBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (ATUtils.isDoubleClick()) return;
onListItemClickedListener.onClicked(getAdapterPosition());
}
});
//Child - Main Contents
mainHomeModuleList = (RecyclerView) itemView.findViewById(R.id.main_home_module_list);
mainHomeModuleList.setHasFixedSize(true);
mainHomeModuleList.setNestedScrollingEnabled(true);
mainHomeContentAdapter = new MainHomeContentAdapter();
mainHomeContentAdapter.setOnListItemClickedListener(new OnListItemClickedListener() {
#Override
public void onClicked(int pos) {
onListItemClickedTwoListener.onClicked(pos, getAdapterPosition());
}
});
}
}
}
Found a Solution.
When create the parent Vertical ListView set setNestedScrollingEnabled as true.
And when create the child Horizontal ListView set setNestedScrollingEnabled as false.

How to dynamically change content in CardView

I'm new to android programming. I have been trying to make Reminder app that takes Time, Date and Description as Inputs.
I want to add a swipe action to the card. Initially, the Card shows time and date, but when the user swipes on the card, the content in the card must change, it should completely show a new layout which has a TextView containing Description in it.
I have searched everywhere, but all of them have given about removing or moving the card in the RecyclerView.
What I want is, how can I dynamically change the content(or layout) in the card with animation using swipe action?
this is my Card layout card_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout `enter code here`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="170dp">
<android.support.v7.widget.CardView
android:layout_height="150dp"
android:layout_width="320dp"
android:id="#+id/card_view"
card_view:cardCornerRadius="16dp"
card_view:cardElevation="2dp"
>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:orientation="vertical"
android:background="#color/blue_color">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00:00"
android:fontFamily="sans-serif-light"
android:id="#+id/cardTime"
android:textSize="#dimen/textview_fontsize"
android:textColor="#color/textview_color"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DESCRIPTION"
android:fontFamily="sans-serif-light"
android:id="#+id/description"
android:textSize="#dimen/textview_fontsize"
android:textColor="#color/textview_color"
android:visibility="gone"
/>
<TextView
android:text=""
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toEndOf="#+id/cardTime"
android:layout_marginStart="12dp"
android:layout_marginTop="17dp"
android:id="#+id/cardPeriod"
android:textColor="#color/textview_color"
android:fontFamily="sans-serif-light"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/textview_color"
android:layout_alignBottom="#+id/cardTime"
android:id="#+id/time_line" />
<TextView
android:text="09/01/1997"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:layout_marginStart="20dp"
android:id="#+id/cardDate"
android:textColor="#color/textview_color"
android:fontFamily="sans-serif-light"
android:textSize="#dimen/date_fontsize"/>
</RelativeLayout>
</android.support.v7.widget.CardView>
MainActivity.java
public class MainActivity extends AppCompatActivity implements AlarmDialog.AlarmDialogListener {
ArrayList<CardGen> cardsList = new ArrayList<CardGen>();
RecyclerView recyclerView;
CardAdapter cardAdapter = new CardAdapter(cardsList);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.cardList);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(cardAdapter);
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
CardAdapter.CardViewHolder holder = (CardAdapter.CardViewHolder) viewHolder;
if (direction == ItemTouchHelper.LEFT){
holder.timeView.setVisibility(View.GONE);
holder.description.setVisibility(View.VISIBLE);
} else {
}
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE){
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);
}
public void createAlarm(View view){
DialogFragment dialogFragment = new AlarmDialog();
dialogFragment.show(getFragmentManager(), "AlarmDialog");
}
#Override
public void onDialogPositiveClick(DialogFragment dialog) {
Dialog dialog1 = dialog.getDialog();
TimePicker timePicker = (TimePicker) dialog1.findViewById(R.id.timePicker);
int hour = timePicker.getHour();
String tempHour = Integer.toString(hour);
String minute = Integer.toString(timePicker.getMinute());
if(minute.length()==1){minute = "0" + minute;}
Toast.makeText(this, tempHour, Toast.LENGTH_SHORT).show();
System.out.println("this is adapter begin");
CardGen cardGen = new CardGen();
if(hour>12){tempHour = Integer.toString(hour-12);}
else if(hour==0){tempHour="12";}
if(tempHour.length()==1){tempHour = "0"+tempHour;}
cardGen.hour = tempHour+":"+minute;
if(hour>=12){cardGen.period="PM";}else{cardGen.period="AM";}
cardGen.fragmentManager = getFragmentManager();
cardAdapter.addList(cardGen);
System.out.println(Integer.toString(cardsList.size()));
System.out.println("this is the middle");
System.out.println("this is the end");
}
#Override
public void onDialogNegativeClick(DialogFragment dialog) {
}
}
RecyclerAdapter (CardAdapter.java)
public class CardAdapter extends RecyclerView.Adapter<CardAdapter.CardViewHolder> {
private ArrayList<CardGen> cardsList;
public CardAdapter(ArrayList<CardGen> cardsList){
this.cardsList = cardsList;
}
#Override
public int getItemCount() {
return cardsList.size();
}
#Override
public void onBindViewHolder(final CardViewHolder holder, int position) {
final CardGen cardGen = cardsList.get(position);
holder.timeView.setText(cardGen.hour);
holder.periodView.setText(cardGen.period);
final TextView temp = holder.dateView;
holder.dateView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DateSelection newFragment = new DateSelection();
newFragment.setElements(temp);
newFragment.show(cardGen.fragmentManager, "datePicker");
}
});
}
#Override
public CardViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.cards, parent, false);
return new CardViewHolder(itemView);
}
public void addList(CardGen card){
cardsList.add(card);
notifyItemInserted(cardsList.size());
}
public void removeList(int position){
cardsList.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, cardsList.size());
}
public class CardViewHolder extends RecyclerView.ViewHolder{
TextView timeView,periodView,dateView,description;
Button transfer;
public CardViewHolder(View v){
super(v);
timeView = (TextView) v.findViewById(R.id.cardTime);
periodView = (TextView) v.findViewById(R.id.cardPeriod);
dateView = (TextView) v.findViewById(R.id.cardDate);
description = (TextView) v.findViewById(R.id.description);
transfer = (Button) v.findViewById(R.id.transfer);
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.example.bharath.organiserexample.MainActivity">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/cardList">
</android.support.v7.widget.RecyclerView>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="62dp"
android:background="#null"
android:src="#mipmap/add_button"
android:layout_gravity="bottom|right"
android:onClick="createAlarm" />
</FrameLayout>
There are a couple options. The easiest would be to have your cardview contain your description but set the visibility to GONE initially, then when the swipe event happens invert the initial state to GONE and the description to VISIBLE. I would create a helper method to do this. As far as capturing the swipe gesture, take a look at this answer.
Swipe to Dismiss for RecyclerView
You will need to be using a recyclerview but this should work if you put your invert view logic in the onSwiped method
EDIT
To get the cardview, use the ViewHolder from the recyclerview
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
MyCustomViewHolder vh = (MyCustomViewHolder) viewHolder;
if(vh.isDescriptionShown()){
vh.showDetails()
}else {
vh.showDescription()
}
}
Probably you should use a database, then add a swipe listener or (Gesture detector). Whenever the card is swiped you remove the current item from the card and then fetch a new item from the database or list. But I think it'll be best with something like a list, so that you can keep track of the current item. And also be able to easily remove a swiped item. When a user adds an item it is added to your list from the database. That way you don't need to worry about the position of the next item.

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

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"

Categories

Resources