I am having 15 to 30 items in my recyclerview. At the End of the recyclerview I want to show the Image/Layout at bottom. This image will slowly come to top while scroll the recylerview to top. When the list end the image/layout will fully shown. If we scroll down the recyclerview the image/layout should go down. If I stop the scroll at middle the image/layout will show partially. For example the Image/Layout height will be 100 dp. it will be placed in the bottom. It will not visible at first time. When we scroll the Recyclerview that view will be slowly appear. Please give me any idea to achieve this. Sorry for my bad English.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
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="wrap_content"
android:scrollbars="vertical" />
<RelativeLayout
android:id="#+id/bottomView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Will show while Scroll"
android:textSize="30sp"
/>
</RelativeLayout>
</RelativeLayout>
Scrolling Recyclerview
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
footerHeight = +10;
bottomView.setTranslationY(footerHeight);
Log.i("Test","...Scrolling up");
} else {
footerHeight = -10;
bottomView.setTranslationY(footerHeight);
Log.i("Test","...Scrolling down");
}
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState) {
case RecyclerView.SCROLL_STATE_IDLE:
Log.i("Test","...The RecyclerView is not scrolling");
break;
case RecyclerView.SCROLL_STATE_DRAGGING:
Log.i("Test","...Scrolling now");
break;
case RecyclerView.SCROLL_STATE_SETTLING:
Log.i("Test","...Scroll Settling");
break;
}
}
});
Here I just increase/decrease the bottomX view while scrolling. But still I am missing something.
OP:
In this image bottom view is showing always. But initially it want view should be hidden state. While scroll up Bottom view slowly come up. If I scroll down Bottom view should slowly goes down.
Start a new project and try this:
MainActivity.java:
public class MainActivity extends AppCompatActivity {
private static final int DATA_LIST_SIZE = 50;
RecyclerView recyclerView;
TextView footer;
ArrayList<SampleData> dataArrayList;
LinearLayoutManager linearLayoutManager;
int totalHeight = -1;
int invisibleHeight = -1;
int scrolledHeight = -1;
int childHeight = -1;
int footerHeight = -1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recycler_view);
footer = findViewById(R.id.text_view_footer);
dataArrayList = genSampleDataList();
CustomRecyclerViewAdapter adapter = new CustomRecyclerViewAdapter(dataArrayList);
linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
footer.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
footerHeight = footer.getMeasuredHeight();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
View firstVisibleView = recyclerView.getChildAt(0);
if (invisibleHeight == -1) {
childHeight = linearLayoutManager.getDecoratedMeasuredHeight(firstVisibleView);
totalHeight = childHeight * DATA_LIST_SIZE;
invisibleHeight = totalHeight - recyclerView.getHeight() + footerHeight;
}
scrolledHeight = linearLayoutManager.findFirstVisibleItemPosition() * childHeight +
recyclerView.getTop() - firstVisibleView.getTop();
int newRecyclerViewHeight = totalHeight - invisibleHeight + footerHeight -
scrolledHeight * footerHeight / invisibleHeight;
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, newRecyclerViewHeight);
recyclerView.setLayoutParams(params);
footer.setBackgroundColor(Color.rgb(255 * (invisibleHeight - scrolledHeight) / invisibleHeight,
255 * scrolledHeight / invisibleHeight, 0));
}
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
}
});
}
private ArrayList<SampleData> genSampleDataList() {
ArrayList<SampleData> tmpList = new ArrayList<>();
for (int i = 0; i < DATA_LIST_SIZE; i++) {
tmpList.add(new SampleData("Item " + (i + 1), "Description " + (i + 1)));
}
return tmpList;
}
}
SampleData.java:
public class SampleData {
String name;
String description;
public SampleData(String name, String description) {
this.name = name;
this.description = description;
}
}
CustomRecyclerViewAdapter.java:
public class CustomRecyclerViewAdapter extends RecyclerView.Adapter<CustomRecyclerViewAdapter.ViewHolder> {
ArrayList<SampleData> dataList;
public CustomRecyclerViewAdapter(ArrayList<SampleData> dataList) {
this.dataList = dataList;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, null);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
SampleData sampleData = dataList.get(position);
holder.textViewName.setText(sampleData.name);
holder.textViewDescription.setText(sampleData.description);
}
#Override
public int getItemCount() {
return dataList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView textViewName;
TextView textViewDescription;
public ViewHolder(#NonNull View itemView) {
super(itemView);
textViewName = itemView.findViewById(R.id.text_view_name);
textViewDescription = itemView.findViewById(R.id.text_view_description);
}
}
}
activity_main.xml:
<?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">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:scrollbars="vertical" />
<TextView
android:id="#+id/text_view_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/recycler_view"
android:gravity="center"
android:text="Will show while Scroll"
android:textSize="30sp" />
</RelativeLayout>
item_view.xml:
<TextView
android:id="#+id/text_view_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="25sp"
android:textStyle="bold" />
<TextView
android:id="#+id/text_view_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp" />
</LinearLayout>
One solution if I've read your question correctly is in your model class to include link or Uri of ImageView in a String.
Then in your RecyclerView adapter do some boolean checking to see if item added has a link to it and if it has load it with library called Picasso for example. Picasso is simple just one line of code. If you are using image from phone you might just add uri to image.
And when items are added on last item add link to image or set it yourself.
Related
How can I snap to particular position for LinearSnapHelper() in horizontal RecyclerView? There is a function scrolltoposition for RecyclerView which scroll to that position but did not keep it in center for this snaphelper.
I am looking for something like below image. So when I set to particular position, it will keep it in center. I dont find anything related to select position for SnapHelper
i find this , but this doesn't help me.
Any help would be appreciated.
If I understand your question, you are looking for a way to jump to a position and have that position centered in the RecyclerView.
Maybe you have tried RecyclerView.scrollToPosition() but that doesn't snap to the view. You may also have tried RecyclerView.smoothScrollToPosition() and that works better but you may want to avoid all the movement if you have a lot of items and are scrolling a long way.
The reason that scrollToPosition() doesn't work is that it doesn't trigger the LinearSnapHelper which uses a scroll listener to detect when to snap. Since smoothScrollToPosition() does trigger the LinearSnapHelper, we will use scrollToPosition() to get us in the area of the target view then use smoothScrollToPosition() to get the view centered as follows:
private RecyclerView mRecycler;
private void newScrollTo(final int pos) {
RecyclerView.ViewHolder vh = mRecycler.findViewHolderForLayoutPosition(pos);
if (vh != null) {
// Target view is available, so just scroll to it.
mRecycler.smoothScrollToPosition(pos);
} else {
// Target view is not available. Scroll to it.
mRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
// From the documentation:
// This callback will also be called if visible item range changes after a layout
// calculation. In that case, dx and dy will be 0.This callback will also be called
// if visible item range changes after a layout calculation. In that case,
// dx and dy will be 0.
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
mRecycler.removeOnScrollListener(this);
if (dx == 0) {
newScrollTo(pos);
}
}
});
mRecycler.scrollToPosition(pos);
}
}
Sample app
MainActivity.java
public class MainActivity extends AppCompatActivity {
private final LinearLayoutManager mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
private final List<String> mItems = new ArrayList<>();
private RecyclerView mRecycler;
private final int mItemCount = 2000;
private final Handler mHandler = new Handler();
private final LinearSnapHelper mLinearSnapHelper = new LinearSnapHelper();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i = 0; i < mItemCount; i++) {
mItems.add(i + "");
}
mRecycler = findViewById(R.id.recyclerView);
final RecyclerViewAdapter adapter = new RecyclerViewAdapter(null);
adapter.setItems(mItems);
mRecycler.setLayoutManager(mLayoutManager);
mRecycler.setAdapter(adapter);
mLinearSnapHelper.attachToRecyclerView(mRecycler);
newScrollTo(1);
// fireScrollTo();
}
private int maxScrolls = mItemCount;
private void fireScrollTo() {
if (--maxScrolls > 0) {
int pos = (int) (Math.random() * mItemCount);
newScrollTo(pos);
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
fireScrollTo();
}
}, 2000);
}
}
private void newScrollTo(final int pos) {
mRecycler.smoothScrollToPosition(pos);
RecyclerView.ViewHolder vh = mRecycler.findViewHolderForLayoutPosition(pos);
if (vh != null) {
// Target view is available, so just scroll to it.
mRecycler.smoothScrollToPosition(pos);
} else {
// Target view is not available. Scroll to it.
mRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
// From the documentation:
// This callback will also be called if visible item range changes after a layout
// calculation. In that case, dx and dy will be 0.This callback will also be called
// if visible item range changes after a layout calculation. In that case,
// dx and dy will be 0.
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
mRecycler.removeOnScrollListener(this);
if (dx == 0) {
newScrollTo(pos);
}
}
});
mRecycler.scrollToPosition(pos);
}
}
}
activity_main.xml
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_width="3px"
android:layout_height="match_parent"
android:background="#android:color/holo_red_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingStart="660px"
android:paddingEnd="660px"/>
</android.support.constraint.ConstraintLayout>
RecyclerViewAdapter.java
class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<String> mItems;
RecyclerViewAdapter(List<String> items) {
mItems = items;
}
#Override
public #NonNull
RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false);
view.getLayoutParams().width = 220;
view.getLayoutParams().height = 220;
// view.setPadding(220 * 3, 0, 220 * 3, 0);
((TextView) view).setGravity(Gravity.CENTER);
return new ItemViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
ItemViewHolder vh = (ItemViewHolder) holder;
String itemText = mItems.get(position);
vh.mItemTextView.setText(itemText);
int bgColor = (position % 2 == 0)
? android.R.color.holo_blue_light
: android.R.color.holo_green_light;
holder.itemView.setBackgroundColor(
holder.itemView.getContext().getResources().getColor(bgColor));
}
#Override
public int getItemCount() {
return (mItems == null) ? 0 : mItems.size();
}
#Override
public int getItemViewType(int position) {
return TYPE_ITEM;
}
static class ItemViewHolder extends RecyclerView.ViewHolder {
private TextView mItemTextView;
ItemViewHolder(View item) {
super(item);
mItemTextView = item.findViewById(android.R.id.text1);
}
}
public void setItems(List<String> items) {
mItems = items;
}
#SuppressWarnings("unused")
private final static String TAG = "RecyclerViewAdapter";
private final static int TYPE_ITEM = 1;
}
Add this where ever you want to scroll your recycler view
recyclerView.scrollToPosition(position)
recyclerView.post {
var view = recyclerView.layoutManager?.findViewByPosition(position);
if (view == null) {
// do nothing
}
var snapDistance = snapHelper.calculateDistanceToFinalSnap(recyclerView.layoutManager!!, view!!)
if (snapDistance?.get(0) != 0 || snapDistance[1] != 0) {
recyclerView.scrollBy(snapDistance?.get(0)!!, snapDistance?.get(1));
}
}
By using this LinearSnap Helper which is attached to your recycler view
var snapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(recyclerView)
For horizontal RecyclerView, you should use PagerSnapHelper() instead of LinearSnapHelper().
Just did some research and trace the SnapHelper's source code, it turns out the solution could be very simple:
class MyPagerSnapHelper: PagerSnapHelper() {
fun smoothScrollToPosition(layoutManager: RecyclerView.LayoutManager, position: Int) {
val smoothScroller = createScroller(layoutManager) ?: return
smoothScroller.targetPosition = position
layoutManager.startSmoothScroll(smoothScroller)
}
}
And then you can pass RecyclerView's LayoutManager and target position here
snapHelper.smoothScrollToPosition(recyclerView.layoutManager!!, index)
Currently I'm at the end of my ideas on following issue with LinearLayoutManagers and RecyclerViews on Android:
What scenario I wanted to achieve
A horizontal RecyclerView on which the user can swipe very fast without any limitations on fling. The items being fullscreen sized making them as big as the recyclerview itself. When the fling has stopped or the user stops manually, the recycler should scroll to one item (mimicing a viewPager a bit)
(I'm using support revision 25.1.0)
code snippets
The Pager-class itself
public class VelocityPager extends RecyclerView {
private int mCurrentItem = 0;
#NonNull
private LinearLayoutManager mLayoutManager;
#Nullable
private OnPageChangeListener mOnPageChangeListener = null;
#NonNull
private Rect mViewRect = new Rect();
#NonNull
private OnScrollListener mOnScrollListener = new OnScrollListener() {
private int mLastItem = 0;
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (mOnPageChangeListener == null) return;
mCurrentItem = mLayoutManager.findFirstVisibleItemPosition();
final View view = mLayoutManager.findViewByPosition(mCurrentItem);
view.getLocalVisibleRect(mViewRect);
final float offset = (float) mViewRect.left / ((View) view.getParent()).getWidth();
mOnPageChangeListener.onPageScrolled(mCurrentItem, offset, 0);
if (mCurrentItem != mLastItem) {
mOnPageChangeListener.onPageSelected(mCurrentItem);
mLastItem = mCurrentItem;
}
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (mOnPageChangeListener == null) return;
mOnPageChangeListener.onPageScrollStateChanged(newState);
}
};
public VelocityPager(#NonNull Context context) {
this(context, null);
}
public VelocityPager(#NonNull Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public VelocityPager(#NonNull Context context, #Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mLayoutManager = createLayoutManager();
init();
}
#NonNull
private LinearLayoutManager createLayoutManager() {
return new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
addOnScrollListener(mOnScrollListener);
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
removeOnScrollListener(mOnScrollListener);
}
#Override
public void onScrollStateChanged(int state) {
// If you tap on the phone while the RecyclerView is scrolling it will stop in the middle.
// This code fixes this. This code is not strictly necessary but it improves the behaviour.
if (state == SCROLL_STATE_IDLE) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();
int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
// views on the screen
int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition);
int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition);
// distance we need to scroll
int leftMargin = (screenWidth - lastView.getWidth()) / 2;
int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();
int leftEdge = lastView.getLeft();
int rightEdge = firstView.getRight();
int scrollDistanceLeft = leftEdge - leftMargin;
int scrollDistanceRight = rightMargin - rightEdge;
if (leftEdge > screenWidth / 2) {
smoothScrollBy(-scrollDistanceRight, 0);
} else if (rightEdge < screenWidth / 2) {
smoothScrollBy(scrollDistanceLeft, 0);
}
}
}
private void init() {
setLayoutManager(mLayoutManager);
setItemAnimator(new DefaultItemAnimator());
setHasFixedSize(true);
}
public void setCurrentItem(int index, boolean smoothScroll) {
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageSelected(index);
}
if (smoothScroll) smoothScrollToPosition(index);
if (!smoothScroll) scrollToPosition(index);
}
public int getCurrentItem() {
return mCurrentItem;
}
public void setOnPageChangeListener(#Nullable OnPageChangeListener onPageChangeListener) {
mOnPageChangeListener = onPageChangeListener;
}
public interface OnPageChangeListener {
/**
* This method will be invoked when the current page is scrolled, either as part
* of a programmatically initiated smooth scroll or a user initiated touch scroll.
*
* #param position Position index of the first page currently being displayed.
* Page position+1 will be visible if positionOffset is nonzero.
* #param positionOffset Value from [0, 1) indicating the offset from the page at position.
* #param positionOffsetPixels Value in pixels indicating the offset from position.
*/
void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
/**
* This method will be invoked when a new page becomes selected. Animation is not
* necessarily complete.
*
* #param position Position index of the new selected page.
*/
void onPageSelected(int position);
/**
* Called when the scroll state changes. Useful for discovering when the user
* begins dragging, when the pager is automatically settling to the current page,
* or when it is fully stopped/idle.
*
* #param state The new scroll state.
* #see VelocityPager#SCROLL_STATE_IDLE
* #see VelocityPager#SCROLL_STATE_DRAGGING
* #see VelocityPager#SCROLL_STATE_SETTLING
*/
void onPageScrollStateChanged(int state);
}
}
The item's xml layout
(Note: the root view has to be clickable for other purposes inside the app)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true">
<LinearLayout
android:id="#+id/icon_container_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_gravity="top|end"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:alpha="0"
android:background="#drawable/info_background"
android:orientation="horizontal"
android:padding="4dp"
tools:alpha="1">
<ImageView
android:id="#+id/delete"
style="#style/SelectableItemBackground"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:contentDescription="#string/desc_delete"
android:padding="12dp"
android:src="#drawable/ic_delete_white_24dp"
android:tint="#color/icons" />
</LinearLayout>
<LinearLayout
android:id="#+id/icon_container_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:alpha="0"
android:background="#drawable/info_background"
android:orientation="vertical"
android:padding="4dp"
tools:alpha="1">
<ImageView
android:id="#+id/size"
style="#style/SelectableItemBackground"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:contentDescription="#string/desc_size"
android:padding="12dp"
android:src="#drawable/ic_straighten_white_24dp"
android:tint="#color/icons" />
<ImageView
android:id="#+id/palette"
style="#style/SelectableItemBackground"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:contentDescription="#string/desc_palette"
android:padding="12dp"
android:src="#drawable/ic_palette_white_24dp"
android:tint="#color/icons" />
</LinearLayout>
</RelativeLayout>
The xml layout with the pager itself
(Quite nested? Might be a cause of the problem? I don't know... )
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="end">
<SwipeRefreshLayout
android:id="#+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.CoordinatorLayout
android:id="#+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false">
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.my.example.OptionalViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="horizontal"
app:layout_behavior="com.my.example.MoveUpBehavior" />
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#android:color/transparent"
android:clickable="false"
android:fitsSystemWindows="false"
app:contentInsetLeft="0dp"
app:contentInsetStart="0dp"
app:contentInsetStartWithNavigation="0dp"
app:layout_collapseMode="pin"
app:navigationIcon="#drawable/ic_menu_white_24dp" />
</android.support.design.widget.CoordinatorLayout>
</SwipeRefreshLayout>
<include layout="#layout/layout_drawer" />
</android.support.v4.widget.DrawerLayout>
part of my adapter that is relevant for ViewHolders
#Override
public int getItemCount() {
return dataset.size();
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.v("Adapter", "CreateViewHolder");
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final View rootView = layoutInflater.inflate(R.layout.page, parent, false);
return new MyViewHolder(rootView);
}
#Override
public void onBindViewHolder(MyViewHolder page, int position) {
Log.v("Adapter", String.format("BindViewHolder(%d)", position));
final ViewData viewData = dataset.get(position);
page.bind(viewData);
listener.onViewAdded(position, viewData.getData());
}
#Override
public void onViewRecycled(MyViewHolder page) {
if (page.getData() == null) return;
listener.onViewRemoved(page.getData().id);
}
#Override
public int getItemViewType(int position) {
return 0;
}
The ViewHolder
public class MyViewHolder extends RecyclerView.ViewHolder implements MyListener {
#BindView(R.id.info_container)
ViewGroup mInfoContainer;
#BindView(R.id.icon_container_top)
ViewGroup mIconContainerTop;
#BindView(R.id.icon_container_bottom)
ViewGroup mIconContainerBottom;
#BindView(R.id.info_rows)
ViewGroup mInfoRows;
#BindView(R.id.loading)
View mIcLoading;
#BindView(R.id.sync_status)
View mIcSyncStatus;
#BindView(R.id.delete)
View mIcDelete;
#BindView(R.id.ic_fav)
View mIcFavorite;
#BindView(R.id.size)
View mIcSize;
#BindView(R.id.palette)
View mIcPalette;
#BindView(R.id.name)
TextView mName;
#BindView(R.id.length)
TextView mLength;
#BindView(R.id.threads)
TextView mThreads;
#BindView(R.id.price)
TextView mPrice;
#Nullable
private MyModel mModel = null;
#Nullable
private Activity mActivity;
public MyViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
mActivity= (Activity) itemView.getContext();
if (mActivity!= null) mActivity.addMyListener(this);
}
#OnClick(R.id.delete)
protected void clickDeleteBtn() {
if (mActivity == null || mActivity.getMode() != Mode.EDIT) return;
if (mModel == null) return;
Animations.pop(mIcDelete);
final int modelId = mModel.id;
if (mModel.delete()) {
mActivity.delete(modelId);
}
}
#OnClick(R.id.size)
protected void clickSizeBtn() {
if (mActivity== null) return;
mActivity.setUIMode(Mode.EDIT_SIZE);
Animations.pop(mIcSize);
}
#OnClick(R.id.palette)
protected void clickPaletteBtn() {
if (mActivity== null) return;
mActivity.setUIMode(Mode.EDIT_LENGTH);
Animations.pop(mIcPalette);
}
private void initModelViews() {
if (mData == null) return;
final Locale locale = Locale.getDefault();
mName.setValue(String.format(locale, "Model#%d", mModel.id));
mLength.setValue(Html.fromHtml(String.format(locale, itemView.getContext().getString(R.string.template_length), mModel.meters)));
}
/**
* set the icon container to be off screen at the beginning
*/
private void prepareViews() {
new ExpectAnim().expect(mIconContainerTop).toBe(outOfScreen(Gravity.END), visible())
.toAnimation()
.setNow();
new ExpectAnim().expect(mIconContainerBottom).toBe(outOfScreen(Gravity.END), visible())
.toAnimation()
.setNow();
}
#Nullable
public MyModel getData() {
return mModel;
}
private void enableEdit() {
new ExpectAnim()
.expect(mIconContainerBottom)
.toBe(atItsOriginalPosition())
.toAnimation()
.start();
}
private void disableEdit() {
new ExpectAnim()
.expect(mIconContainerBottom)
.toBe(outOfScreen(Gravity.END))
.toAnimation()
.start();
}
private void enableInfo() {
new ExpectAnim()
.expect(mInfoContainer)
.toBe(atItsOriginalPosition())
.toAnimation()
.start();
}
private void disableInfo() {
new ExpectAnim()
.expect(mInfoContainer)
.toBe(outOfScreen(Gravity.BOTTOM))
.toAnimation()
.start();
}
private void enableDelete() {
if (mIconContainerTop == null) return;
new ExpectAnim()
.expect(mIconContainerTop)
.toBe(atItsOriginalPosition(), visible())
.toAnimation()
.start();
}
private void disableDelete() {
if (mIconContainerTop == null) return;
new ExpectAnim()
.expect(mIconContainerTop)
.toBe(outOfScreen(Gravity.END), invisible())
.toAnimation()
.start();
}
public void bind(#NonNull final ViewData viewData) {
mModel = viewData.getData();
prepareViews();
initModelViews();
}
}
So, here's my issue with these!
When intializing the adapter I insert about 15 to 17 items via an observable. This seems to be correct:
but when swiping horizontally the recyclerView's callbacks seem to be totally messed up and produce weird results:
Do you see that the recycler does not try to recycle old viewHolders at all? The image just shows a small portion of the "spamming" that is going on. Sometimes it will create a new viewHolder even more than two times for the same position while I scroll the recycler slowly!
Another side problem is: The listener currently should allow me to pass the bind / recycle events to an underlying game engine which will create destroy entities on the screen. Due the excessive spamming of the events it will currently create those entities also excessively!
I excpected the Recycler to create a new ViewHolder for the first (let's say in my example 17) times and then just reuse the items how it should.
Please help, I'm stuck on this problem for 2 days now and I'm frustrated after searching people with same issues but without luck.
Thank you!
There's obviously a problem with ViewHolder recycling. I'm guessing the animations you're running inside MyViewHolder might prevent RecyclerView from recycling holders properly. Make sure you cancel animations at some point, e.g. in RecyclerView.Adapter#onViewDetachedFromWindow().
After you've fixed this, I suggest you follow #EugenPechanec's suggestion to reduce the amount of custom calculations done in the OnScrollListeners. It's better to rely on support library classes and tweak the behavior a little.
When the fling has stopped or the user stops manually, the recycler should scroll to one item (mimicing a viewPager a bit)
Use the official LinearSnapHelper which snaps center of child view to center of RecyclerView.
Use a GravitySnapHelper library which can also snap to start of or end of RecyclerView, just like Google Play store does.
Both of these solutions are applied similarly:
new LinearSnapHelper().attachToRecyclerView(recyclerView);
A horizontal RecyclerView on which the user can swipe very fast without any limitations on fling.
"Without limitations" translates to "infinite speed" meaning a fling would instantly jump to target position. That's probably not what you want.
After going through SnapHelper source I found out that there is a rule: one inch takes 100 milliseconds to scroll. You can override this behavior.
final SnapHelper snapHelper = new LinearSnapHelper() {
#Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
}
};
snapHelper.attachToRecyclerView(recyclerView);
That's the default speed (where MILLISECONDS_PER_INCH = 100). Experiment and find out what fits your needs, start with "one inch takes 50 ms to scroll" and so on.
I am using a RecyclerView inside SwipeRefreshLayout.The RecyclerView has another 2 RecyclerView (for now; it may increase). In my second RecyclerView i am trying to implement infinite scrolling. But my RecyclerView.getItemCount() and RecyclerView.getChildCount() are giving same value. Also the 2nd re has GridLayoutManager and GridlayoutManager.findFirstVisibleItemPosition() always gives 0 and GridLayoutManager.findLastVisibleItemPosition() always gives list size - 1 in OnScrolled of the RecyclerView. What is causing this and what should i do to implement the infinite scrolling.
fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_container"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingTop="#dimen/padding_standard"
android:paddingBottom="#dimen/padding_standard">
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
parent_recyclerview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:layout_marginBottom="#dimen/padding_standard"
android:orientation="vertical">
<TextView
android:id="#+id/section_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/padding_standard"
android:layout_marginBottom="#dimen/margin_standard"
android:textColor="#color/label_text"
android:textSize="#dimen/text_size_standard"
android:textStyle="bold"
tools:text="MOments"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/section_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"/>
<ProgressBar
android:id="#+id/events_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:visibility="gone"/>
</LinearLayout>
onScrollListener for child recycler view
RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
GridLayoutManager manager = (GridLayoutManager) recyclerView.getLayoutManager();
int itemSize = manager.getItemCount();
int firstVisibleItem = manager.findFirstVisibleItemPosition();
int visibleChIldCount = manager.getChildCount();
Logger.e(TAG,"=============== START =====================");
Logger.e(TAG, "itemSize: " + itemSize);
Logger.e(TAG, "firstVisibleitem: " + firstVisibleItem);
Logger.e(TAG, "visibleChIldCount: " + visibleChIldCount);
Logger.e(TAG,"mLayoutManager.firstCOmpletely: "+ manager.findFirstCompletelyVisibleItemPosition());
Logger.e(TAG,"mLayoutManager. lastcompletey: "+ manager.findLastCompletelyVisibleItemPosition());
Logger.e(TAG,"mLayoutManager.findLastVisible: "+ manager.findLastVisibleItemPosition());
Logger.e(TAG,"=================END ================");
if (itemSize >= firstVisibleItem + visibleChIldCount){
Logger.e("", "loading");
mLoadMoreListener.loadMore();
} else {
Logger.e(TAG, "not Loading");
}
}
};
Sorry for the late reply. But i will post my solution here if in case someone else is looking for them.
In the adapter of the parent recycler view i have set tag for the view
#Override
public SectionRowHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mLayoutInflater.inflate(R.layout.view_section, parent, false);
view.setTag(viewType);
return new SectionRowHolder(view);
}
The SectionRowHolder is simple ViewHolder with a RecyclerView.OnScrollListener property with getter and setter.
public class SectionRowHolder extends RecyclerView.ViewHolder {
protected RecyclerView recyclerView;
RecyclerView.OnScrollListener mOnScrollListener;
public SectionRowHolder(View view) {
super(view);
this.recyclerView = (RecyclerView) view.findViewById(R.id.section_list);
}
public RecyclerView.OnScrollListener getCustomScrollListener() {
return mOnScrollListener;
}
public void setCustomScrollListener(RecyclerView.OnScrollListener mOnScrollListener) {
this.mOnScrollListener = mOnScrollListener;
}
}
Then in the onBindViewHolder for the child with infinite scrolling I have implemented the load more logic in scroll listener and set to the child RecyclerView.
RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
boolean loadEnable = false;
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
mTotalScrolled += dy;
if ((mTotalScrolled + LOAD_MORE_ENABLE_HEIGHT) > recyclerView.getHeight() && loadEnable) {
loadEnable = false;
mLoadMoreListener.loadMore();
} else {
loadEnable = true;
}
}
};
holder.setCustomScrollListener(scrollListener);
holder.recyclerView.addOnScrollListener(scrollListener);
Here LOAD_MORE_ENABLE_HEIGHT is offset from bottom of the child recycler view to initialize the loadmore() logic and mLoadMoreListener is callback to the fragment or activity.
Finally for passing the scoll listener from parent recycler view to the child recycler vew, in my parent RecyclerView's onScrollListener
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
View v = mRecyclerView.findViewWithTag(CHILD_RECYCLERVIEW_TAG);
SectionedLifeAtAdapter.SectionRowHolder viewHolder =
(SectionedLifeAtAdapter.SectionRowHolder) mRecyclerView
.findContainingViewHolder(v);
if (viewHolder.getCustomScrollListener() != null)
viewHolder.getCustomScrollListener().onScrolled((RecyclerView) v
.findViewById(R.id.section_list), dx, dy);
Logger.e(TAG, ">>> call to on scrolled listener >>>");
}
});
Here CHILD_RECYCLERVIEW_TAG is the view type that you set in the onCreateViewHolder of the parent Adapter.
It kind of look like messy but it did the job for me without any issues.
RecyclerView by default, does come with a nice deletion animation, as long as you setHasStableIds(true) and provide correct implementation on getItemId.
Recently, I had added divider into RecyclerView via https://stackoverflow.com/a/27037230/72437
The outcome looks as following
https://www.youtube.com/watch?v=u-2kPZwF_0w
https://youtu.be/c81OsFAL3zY (To make the dividers more visible when delete animation played, I temporary change the RecyclerView background to red)
The dividers are still visible, when deletion animation being played.
However, if I look at GMail example, when deletion animation being played, divider lines are no longer visible. They are being covered a solid color area.
https://www.youtube.com/watch?v=cLs7paU-BIg
May I know, how can I achieve the same effect as GMail, by not showing divider lines, when deletion animation played?
The solution is fairly easy. To animate a decoration, you can and should use view.getTranslation_() and view.getAlpha(). I wrote a blog post some time ago on this exact issue, you can read it here.
Translation and fading off
The default layout manager will fade views out (alpha) and translate them, when they get added or removed. You have to account for this in your decoration.
The idea is simple:
However you draw your decoration, apply the same alpha and translation to your drawing by using view.getAlpha() and view.getTranslationY().
Following your linked answer, it would have to be adapted like the following:
// translate
int top = child.getBottom() + params.bottomMargin + view.getTranslationY();
int bottom = top + mDivider.getIntrinsicHeight();
// apply alpha
mDivider.setAlpha((int) child.getAlpha() * 255f);
mDivider.setBounds(left + view.getTranslationX(), top,
right + view.getTranslationX(), bottom);
mDivider.draw(c);
A complete sample
I like to draw things myself, since I think drawing a line is less overhead than layouting a drawable, this would look like the following:
public class SeparatorDecoration extends RecyclerView.ItemDecoration {
private final Paint mPaint;
private final int mAlpha;
public SeparatorDecoration(#ColorInt int color, float width) {
mPaint = new Paint();
mPaint.setColor(color);
mPaint.setStrokeWidth(width);
mAlpha = mPaint.getAlpha();
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// we retrieve the position in the list
final int position = params.getViewAdapterPosition();
// add space for the separator to the bottom of every view but the last one
if (position < state.getItemCount()) {
outRect.set(0, 0, 0, (int) mPaint.getStrokeWidth()); // left, top, right, bottom
} else {
outRect.setEmpty(); // 0, 0, 0, 0
}
}
#Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
// a line will draw half its size to top and bottom,
// hence the offset to place it correctly
final int offset = (int) (mPaint.getStrokeWidth() / 2);
// this will iterate over every visible view
for (int i = 0; i < parent.getChildCount(); i++) {
final View view = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// get the position
final int position = params.getViewAdapterPosition();
// and finally draw the separator
if (position < state.getItemCount()) {
// apply alpha to support animations
mPaint.setAlpha((int) (view.getAlpha() * mAlpha));
float positionY = view.getBottom() + offset + view.getTranslationY();
// do the drawing
c.drawLine(view.getLeft() + view.getTranslationX(),
positionY,
view.getRight() + view.getTranslationX(),
positionY,
mPaint);
}
}
}
}
Firstly, sorry for the massive answer size. However, I felt it necessary to include my entire test Activity so that you can see what I have done.
The issue
The issue that you have, is that the DividerItemDecoration has no idea of the state of your row. It does not know whether the item is being deleted.
For this reason, I made a POJO that we can use to contain an integer (that we use as both an itemId and a visual representation and a boolean indicating that this row is being deleted or not.
When you decide to delete entries (in this example adapter.notifyItemRangeRemoved(3, 8);), you must also set the associated Pojo to being deleted (in this example pojo.beingDeleted = true;).
The position of the divider when beingDeleted, is reset to the colour of the parent view. In order to cover up the divider.
I am not very fond of using the dataset itself to manage the state of its parent list. There is perhaps a better way.
The result visualized
The Activity:
public class MainActivity extends AppCompatActivity {
private static final int VERTICAL_ITEM_SPACE = 8;
private List<Pojo> mDataset = new ArrayList<Pojo>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for(int i = 0; i < 30; i++) {
mDataset.add(new Pojo(i));
}
final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new VerticalSpaceItemDecoration(VERTICAL_ITEM_SPACE));
recyclerView.addItemDecoration(new DividerItemDecoration(this));
RecyclerView.ItemAnimator ia = recyclerView.getItemAnimator();
ia.setRemoveDuration(4000);
final Adapter adapter = new Adapter(mDataset);
recyclerView.setAdapter(adapter);
(new Handler(Looper.getMainLooper())).postDelayed(new Runnable() {
#Override
public void run() {
int index = 0;
Iterator<Pojo> it = mDataset.iterator();
while(it.hasNext()) {
Pojo pojo = it.next();
if(index >= 3 && index <= 10) {
pojo.beingDeleted = true;
it.remove();
}
index++;
}
adapter.notifyItemRangeRemoved(3, 8);
}
}, 2000);
}
public class Adapter extends RecyclerView.Adapter<Holder> {
private List<Pojo> mDataset;
public Adapter(#NonNull final List<Pojo> dataset) {
setHasStableIds(true);
mDataset = dataset;
}
#Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_cell, parent, false);
return new Holder(view);
}
#Override
public void onBindViewHolder(final Holder holder, final int position) {
final Pojo data = mDataset.get(position);
holder.itemView.setTag(data);
holder.textView.setText("Test "+data.dataItem);
}
#Override
public long getItemId(int position) {
return mDataset.get(position).dataItem;
}
#Override
public int getItemCount() {
return mDataset.size();
}
}
public class Holder extends RecyclerView.ViewHolder {
public TextView textView;
public Holder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.text);
}
}
public class Pojo {
public int dataItem;
public boolean beingDeleted = false;
public Pojo(int dataItem) {
this.dataItem = dataItem;
}
}
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private final int[] ATTRS = new int[]{android.R.attr.listDivider};
private Paint mOverwritePaint;
private Drawable mDivider;
/**
* Default divider will be used
*/
public DividerItemDecoration(Context context) {
final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS);
mDivider = styledAttributes.getDrawable(0);
styledAttributes.recycle();
initializePaint();
}
/**
* Custom divider will be used
*/
public DividerItemDecoration(Context context, int resId) {
mDivider = ContextCompat.getDrawable(context, resId);
initializePaint();
}
private void initializePaint() {
mOverwritePaint = new Paint();
mOverwritePaint.setColor(ContextCompat.getColor(MainActivity.this, android.R.color.background_light));
}
#Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin;
int bottom = top + mDivider.getIntrinsicHeight();
Pojo item = (Pojo) child.getTag();
if(item.beingDeleted) {
c.drawRect(left, top, right, bottom, mOverwritePaint);
} else {
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
}
}
public class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration {
private final int mVerticalSpaceHeight;
public VerticalSpaceItemDecoration(int mVerticalSpaceHeight) {
this.mVerticalSpaceHeight = mVerticalSpaceHeight;
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
outRect.bottom = mVerticalSpaceHeight;
}
}
}
The Activity Layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
android:background="#android:color/background_light"
tools:context="test.dae.myapplication.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
The RecyclerView "row" Layout
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/text"
android:padding="8dp">
</TextView>
I think the ItemDecorator you use to draw a divider after every row is messing things up when swipe to delete is performed.
Instead of Using ItemDecorator to draw a Divider in a recyclerview, add a view at the end of your RecyclerView child layout design.which will draw a divider line like ItemDecorator.
<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="wrap_content"
>
<!-- child layout Design !-->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#android:color/darker_gray"
android:layout_gravity="bottom"
/>
</Linearlayout>
I am creating a horisontal RecyclerView in my app.
It has to show 2 images on the screen at a time (so width of each image has to be 50% of the screen).
For now it works fine but each item consums all width of the screen.
Here is my code
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_main_ads);
LinearLayoutManager mLinearLayoutManager = new LinearLayoutManager(getActivity());
mLinearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
RecyclerViewAdapter adapter = new RecyclerViewAdapter(tmp, R.layout.lv_main_screen);
mRecyclerView.setAdapter(adapter);
Here is layout of an item
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1">
<ImageView
android:id="#+id/iv_main_ad"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:scaleType="fitXY"
android:src="#drawable/baner_gasoline"
/>
</LinearLayout>
As you can see I tried to use Layout_gravity="0.5",
But it doesn't help.
I tried to specify layout_width = ...dp but I can not get exactly half of the screen.
I am thinking of adding another ImageView into item layout, but in this case I will have troubles with the adapter, because I want to implemnt circled (infinity) horizontal listview
here is my adapter:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyHolder> {
private List<Integer> mImages;
private int itemLayout;
public RecyclerViewAdapter(ArrayList<Integer> imageResourceIds, int itemLayout) {
this.mImages = imageResourceIds;
this.itemLayout = itemLayout;
}
#Override
public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(itemLayout, parent, false);
return new MyHolder(v);
}
#Override
public void onBindViewHolder(MyHolder holder, int position) {
holder.adIv.setImageResource(mImages.get(position));
}
#Override
public int getItemCount() {
return mImages.size();
}
public static class MyHolder extends RecyclerView.ViewHolder {
protected ImageView adIv;
private MyHolder(View v) {
super(v);
this.adIv = (ImageView) v.findViewById(R.id.iv_main_ad);
}
}
}
For You need to calculate the width of the screen and set the width dynamically below is the my code
Add below code in your ViewHolder initilisation
llImg = (LinearLayout) itemView.findViewById(R.id.llImg);
llImg.getLayoutParams().width = (int) (Utils.getScreenWidth(itemView.getContext()) / 2);
llImg.getLayoutParams().height = (int) (Utils.getScreenWidth(itemView.getContext()) / 2);
imgView = (ImageView) itemView.findViewById(R.id.imgView);
The Layout file is here
<LinearLayout
android:id="#+id/llImg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center">
<ImageView
android:id="#+id/imgView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
Make one Utils.java
public static int getScreenWidth(Context context) {
if (screenWidth == 0) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
screenWidth = size.x;
}
return screenWidth;
}
Hope this will help you !