Get center visible item of RecycleView when scrolling - android

This is what I want:
As image above, I want to draw a center line on RecycleView, then get the center item when scrolling (as well as move left or right)
Here is my try to draw a horizontal RecycleView:
HorizontalAdapter adapter = new HorizontalAdapter(data);
LinearLayoutManager layoutManager
= new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recycleView.setLayoutManager(layoutManager);
recycleView.setAdapter(adapter);
Is there any way to know which item is moved to the center of RecycleView? And how can I scroll RecycleView to left or right just one position?
Update: I tried to use a scroll listener to get the middle position, but it doesn't work as an aspect.
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int firstPos = layoutManager.findFirstVisibleItemPosition();
int lastPos = layoutManager.findLastVisibleItemPosition();
int middle = Math.abs(lastPos - firstPos) / 2 + firstPos;
int selectedPos = -1;
for (int i = 0; i < adapter.getItemCount(); i++) {
if (i == middle) {
adapter.getItem(i).setSelected(true);
selectedPos = i;
} else {
adapter.getItem(i).setSelected(false);
}
}
adapter.notifyDataSetChanged();
}
And get the result:
I only want to change the selected item (make text to white color) when it is on the blue Rect

I made something just like this. I can do exactly what you need.
First of all, this is how is my alogrithm work
This is my recyclerView Adapter
public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
private ArrayList<LabelerDate> dateDataList;
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int paddingWidthDate = 0;
private int selectedItem = -1;
public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) {
this.dateDataList = dateData;
this.paddingWidthDate = paddingWidthDate;
}
#Override
public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_date,
parent, false);
return new DateViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_padding,
parent, false);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.width = paddingWidthDate;
view.setLayoutParams(layoutParams);
return new DateViewHolder(view);
}
}
#Override
public void onBindViewHolder(DateViewHolder holder, int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
if(labelerDate.dateType.equals(BirthDayActivity.DateType.C31))
holder.tvDate.setText(String.valueOf(labelerDate.valueDate));
holder.tvDate.setVisibility(View.VISIBLE);
holder.imgSmall.setVisibility(View.VISIBLE);
if (position == selectedItem) {
holder.tvDate.setTextColor(Color.parseColor("#094673"));
holder.tvDate.setTextSize(35);
holder.imgSmall.setBackgroundResource(R.color.textviewbold);
} else {
holder.tvDate.setTextColor(Color.GRAY);
holder.tvDate.setTextSize(35);
holder.imgSmall.setBackgroundResource(R.color.gray);
}
}
}
public void setSelecteditem(int selecteditem) {
this.selectedItem = selecteditem;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return dateDataList.size();
}
#Override
public int getItemViewType(int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (labelerDate.dateType.equals(BirthDayActivity.DateType.NONE)) {
return VIEW_TYPE_PADDING;
}
return VIEW_TYPE_ITEM;
}
public class DateViewHolder extends RecyclerView.ViewHolder {
public TextView tvDate;
public ImageView imgSmall;
public DateViewHolder(View itemView) {
super(itemView);
tvDate = (TextView) itemView.findViewById(R.id.tvNumberDate);
imgSmall = (ImageView) itemView.findViewById(R.id.small_marked_dob);
}
}}
This is most important alogrithm:
public void getRecyclerviewDate() {
recyclerViewDate = (RecyclerView) findViewById(R.id.recyclerViewDay);
ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
finalWidthDate = recyclerViewDate.getMeasuredWidth();
itemWidthDate = getResources().getDimension(R.dimen.item_dob_width);
paddingDate = (finalWidthDate - itemWidthDate) / 2;
firstItemWidthDate = paddingDate ;
allPixelsDate = 0;
final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerViewDate.setLayoutManager(dateLayoutManager);
recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
synchronized (this) {
if(newState == RecyclerView.SCROLL_STATE_IDLE){
calculatePositionAndScrollDate(recyclerView);
}
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
allPixelsDate += dx;
}
});
if (labelerDates == null)
labelerDates = new ArrayList<>();
labelerDates.addAll(genLabelerDate(currentMonth, currentYear));
dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
recyclerViewDate.setAdapter(dateAdapter);
return true;
}
});
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void calculatePositionAndScrollDate(RecyclerView recyclerView) {
int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
if (expectedPositionDate == -1) {
expectedPositionDate = 0;
} else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) {
expectedPositionDate--;
}
scrollListToPositionDate(recyclerView, expectedPositionDate);
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void scrollListToPositionDate(RecyclerView recyclerView, int expectedPositionDate) {
float targetScrollPosDate = expectedPositionDate * itemWidthDate + firstItemWidthDate - paddingDate;
float missingPxDate = targetScrollPosDate - allPixelsDate;
if (missingPxDate != 0) {
recyclerView.smoothScrollBy((int) missingPxDate, 0);
}
}
private void setDateValue() {
int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
setColorDate = expectedPositionDateColor + 1;
//set color here
dateAdapter.setSelecteditem(setColorDate);
}
#Override
protected void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
allPixelsDate = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS_DATE);
allPixelsDateChanged = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS_DATE_CHANGED);
}
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putFloat(BUNDLE_LIST_PIXELS_DATE, allPixelsDate);
outState.putFloat(BUNDLE_LIST_PIXELS_DATE_CHANGED, allPixelsDateChanged);
}
And this is my result:
Look at this video link, this is my app demo

Sometimes is needed the entire example code block together, because we may miss something. Here is what I have, feel free to correct anything since I may be doing some little mistake somewhere. And Yes, this answer is an extension of #tranhieu answer. Thanks #tranhieu.
MainActivity.java
package com.test;
import android.app.Activity;
import android.graphics.Color;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.TextView;
import java.util.ArrayList;
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
public float firstItemWidthDate;
public float paddingDate;
public float itemWidthDate;
public int allPixelsDate;
public int finalWidthDate;
private DateAdapter dateAdapter;
private ArrayList<LabelerDate> labelerDates = new ArrayList<>();
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getRecyclerviewDate();
}
public void getRecyclerviewDate() {
final RecyclerView recyclerViewDate = (RecyclerView) findViewById(R.id.rv_tasks_date);
if (recyclerViewDate != null) {
recyclerViewDate.postDelayed(new Runnable() {
#Override
public void run() {
setDateValue();
}
}, 300);
recyclerViewDate.postDelayed(new Runnable() {
#Override
public void run() {
recyclerViewDate.smoothScrollToPosition(dateAdapter.getItemCount()-1);
setDateValue();
}
}, 5000);
}
ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
finalWidthDate = recyclerViewDate.getMeasuredWidth();
itemWidthDate = getResources().getDimension(R.dimen.item_dob_width);
paddingDate = (finalWidthDate - itemWidthDate) / 2;
firstItemWidthDate = paddingDate;
allPixelsDate = 0;
final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerViewDate.setLayoutManager(dateLayoutManager);
recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
synchronized (this) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
calculatePositionAndScrollDate(recyclerView);
}
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
allPixelsDate += dx;
}
});
if (labelerDates == null) {
labelerDates = new ArrayList<>();
}
genLabelerDate();
dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
recyclerViewDate.setAdapter(dateAdapter);
dateAdapter.setSelecteditem(dateAdapter.getItemCount() - 1);
return true;
}
});
}
private void genLabelerDate() {
for (int i = 0; i < 32; i++) {
LabelerDate labelerDate = new LabelerDate();
labelerDate.setNumber(Integer.toString(i));
labelerDates.add(labelerDate);
if (i == 0 || i == 31) {
labelerDate.setType(DateAdapter.VIEW_TYPE_PADDING);
} else {
labelerDate.setType(DateAdapter.VIEW_TYPE_ITEM);
}
}
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void calculatePositionAndScrollDate(RecyclerView recyclerView) {
int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
if (expectedPositionDate == -1) {
expectedPositionDate = 0;
} else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) {
expectedPositionDate--;
}
scrollListToPositionDate(recyclerView, expectedPositionDate);
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void scrollListToPositionDate(RecyclerView recyclerView, int expectedPositionDate) {
float targetScrollPosDate = expectedPositionDate * itemWidthDate + firstItemWidthDate - paddingDate;
float missingPxDate = targetScrollPosDate - allPixelsDate;
if (missingPxDate != 0) {
recyclerView.smoothScrollBy((int) missingPxDate, 0);
}
setDateValue();
}
//
private void setDateValue() {
int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
int setColorDate = expectedPositionDateColor + 1;
// set color here
dateAdapter.setSelecteditem(setColorDate);
}
public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
private ArrayList<LabelerDate> dateDataList;
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int paddingWidthDate = 0;
private int selectedItem = -1;
public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) {
this.dateDataList = dateData;
this.paddingWidthDate = paddingWidthDate;
}
#Override
public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,
parent, false);
return new DateViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,
parent, false);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.width = paddingWidthDate;
view.setLayoutParams(layoutParams);
return new DateViewHolder(view);
}
}
#Override
public void onBindViewHolder(DateViewHolder holder, int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
holder.tvDate.setText(labelerDate.getNumber());
holder.tvDate.setVisibility(View.VISIBLE);
Log.d(TAG, "default " + position + ", selected " + selectedItem);
if (position == selectedItem) {
Log.d(TAG, "center" + position);
holder.tvDate.setTextColor(Color.parseColor("#76FF03"));
holder.tvDate.setTextSize(35);
} else {
holder.tvDate.setTextColor(Color.WHITE);
holder.tvDate.setTextSize(18);
}
} else {
holder.tvDate.setVisibility(View.INVISIBLE);
}
}
public void setSelecteditem(int selecteditem) {
this.selectedItem = selecteditem;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return dateDataList.size();
}
#Override
public int getItemViewType(int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (labelerDate.getType() == VIEW_TYPE_PADDING) {
return VIEW_TYPE_PADDING;
} else {
return VIEW_TYPE_ITEM;
}
}
public class DateViewHolder extends RecyclerView.ViewHolder {
public TextView tvDate;
public DateViewHolder(View itemView) {
super(itemView);
tvDate = (TextView) itemView.findViewById(R.id.txt_date);
}
}
}
private class LabelerDate {
private int type;
private String number;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
}
activity_main.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="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_tasks_date"
android:layout_width="match_parent"
android:layout_height="48dp" />
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:layout_marginTop="48dp"
android:src="#android:drawable/ic_dialog_info" />
</FrameLayout>
</LinearLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
android:id="#+id/txt_date"
android:layout_width="#dimen/item_dob_width"
android:layout_height="48dp"
android:text="32"
android:textColor="#android:color/white"
android:background="#android:color/darker_gray"
android:textSize="28sp"
android:gravity="center"/>
</LinearLayout>
dimens.xml
<resources>
<dimen name="item_dob_width">100dp</dimen>
</resources>

Oh boy. I've been searching for this answer for almost a week and then found out the solution. Custom LayoutManagers? No. ItemDecorator? Nope.
Here's the easiest way to do it:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingStart="150dp"
android:paddingEnd="150dp"
android:clipToPadding="false" />
The critical part is:
android:paddingStart="150dp"
android:paddingEnd="150dp"
android:clipToPadding="false"
And then just assign SnapHelper to your RecylcerView:
val snapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(recyclerView)
This is it. The easiest and most perfect solution to the problem

I'm used the SnapHelper right here:
// init snaphelper
SnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView)
// init layout manager
LinearLayoutManager layoutManager = new LinearLayoutManager(mainActivity);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
// init adapter
adatper.setSnapHelper(snapHelper);
adatper.setLayoutManager(layoutManager);
adatper.initAdapter(new Float((DisplayHelper.getDisplayWidth(mainActivity) / 2) - (fooViewWidth / 2)).intValue());
recyclerView.setAdapter(adatper);
As said by TranHieu the solution of inserting 2 item for padding (at start and at end positions) is good.
I don't like the use of ViewTreeObserver because of poor readability of code. With this technique then you must also manage redrawing of the items if they are recycled.
If you are using customview classes you can set its width directly into these classes.
For example this is my padding class
/**
* Created by firegloves on 25/09/15.
*/
#EViewGroup(R.layout.view_padding)
public class PaddingView extends FooView {
Context mCtx;
public PaddingView(Context context) {
super(context);
mCtx = context;
}
public void setWidth(int width) {
setLayoutParams(new LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT));
}
}
In my adapter I store the desired padding item width, that is equal to (displayWidth / 2) - (realItemWidth / 2)
This is my adapter, don't look at methods not matching RecyclerView.Adapter, pay attention to the initAdapter method and to the onCreateItemView method
#EBean
public class FooAdapterRecycler extends RecyclerViewAdapterBase<Foo, FooView> {
private final int TYPE_PADDING_VIEW = 0;
private final int TYPE_REAL_VIEW = 1;
#RootContext
Context ctx;
#Bean(Finder.class)
IFinder finder;
SnapHelper snapHelper;
RecyclerView.LayoutManager layoutManager;
private int paddingWidth = 0;
/**
* preleva i dati dal finder
*/
public void initAdapter(int paddingWidth) {
/*******************************
* THIS CODE IS THE IMPORTANT ONE
******************************/
this.paddingWidth = paddingWidth;
// add 1 item for initial space
mItems = new ArrayList<>();
Foo foo = new Foo();
mItems.add(foo);
// get real items from finder
mItems.addAll(finder.findAll());
// add 1 item for final space
mItems = new ArrayList<>();
Foo foo2 = new Foo();
mItems.add(foo2);
}
#Override
public int getItemViewType(int position) {
if (position == 0 || position == getItemCount()-1) {
return TYPE_PADDING_VIEW;
} else {
return TYPE_REAL_VIEW;
}
}
#Override
protected FooView onCreateItemView(ViewGroup parent, int viewType) {
/*******************************
* THIS CODE IS THE IMPORTANT ONE
******************************/
if (viewType == TYPE_PADDING_VIEW) {
PaddingView view = PaddingView_.build(ctx);
view.setWidth(paddingWidth);
return view;
} else {
return FooView_.build(ctx);
}
}
public void setSnapHelper(SnapHelper snapHelper) {
this.snapHelper = snapHelper;
}
public void setLayoutManager(RecyclerView.LayoutManager layoutManager) {
this.layoutManager = layoutManager;
}
}
I'm using AndroidAnnotations library but it's not required
Hope that helps

USING SNAPHELPER - A SMOOTHER SOLUTION
Here it is another solution using SnapHelper. Starting from the answer of #TranHieu here:
https://stackoverflow.com/a/34647005/3944251
and the compressed by #sector11 here:
https://stackoverflow.com/a/38411582/3944251
I wrote the following code which is also based in both answers above, but it's simpler and offers a smoother solution using SnapHelper presented in android support library 24.2.0.
Here you have the MainActivity class. The rest is the same with #sector11's answer.
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSnapHelper;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.TextView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
public float firstItemWidthDate;
public float itemWidthDate;
public int allPixelsDate;
public int finalWidthDate;
private DateAdapter dateAdapter;
private ArrayList<LabelerDate> labelerDates;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
labelerDates = new ArrayList<>();
getRecyclerviewDate();
}
public void getRecyclerviewDate() {
final RecyclerView recyclerViewDate = (RecyclerView) findViewById(R.id.rv_tasks_date);
recyclerViewDate.postDelayed(new Runnable() {
#Override
public void run() {
//recyclerViewDate.smoothScrollToPosition(dateAdapter.getItemCount()-1);
setDateValue();
}
}, 300);
ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
finalWidthDate = recyclerViewDate.getMeasuredWidth();
itemWidthDate = getResources().getDimension(R.dimen.item_dob_width);
firstItemWidthDate = (finalWidthDate - itemWidthDate) / 2;
allPixelsDate = 0;
final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerViewDate.setLayoutManager(dateLayoutManager);
/* Create a LinearSnapHelper and attach the recyclerView to it. */
final LinearSnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerViewDate);
recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
allPixelsDate += dx;
recyclerView.post(new Runnable() {
public void run() {
setDateValue();
}
});
}
});
genLabelerDate();
dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
recyclerViewDate.setAdapter(dateAdapter);
dateAdapter.setSelecteditem(dateAdapter.getItemCount() - 1);
return true;
}
});
}
private void genLabelerDate() {
for (int i = 0; i < 32; i++) {
LabelerDate labelerDate = new LabelerDate();
labelerDate.setNumber(Integer.toString(i));
labelerDates.add(labelerDate);
if (i == 0 || i == 31) {
labelerDate.setType(DateAdapter.VIEW_TYPE_PADDING);
} else {
labelerDate.setType(DateAdapter.VIEW_TYPE_ITEM);
}
}
}
//
private void setDateValue() {
int expectedPositionDateColor = Math.round(allPixelsDate / itemWidthDate);
int setColorDate = expectedPositionDateColor + 1;
// set color here
dateAdapter.setSelecteditem(setColorDate);
}
public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
private ArrayList<LabelerDate> dateDataList;
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int paddingWidthDate = 0;
private int selectedItem = -1;
public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) {
this.dateDataList = dateData;
this.paddingWidthDate = paddingWidthDate;
}
#Override
public DateAdapter.DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
if (viewType == VIEW_TYPE_PADDING) {
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.width = paddingWidthDate;
view.setLayoutParams(layoutParams);
}
return new DateViewHolder(view);
}
#Override
public void onBindViewHolder(DateAdapter.DateViewHolder holder, int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
holder.tvDate.setText(labelerDate.getNumber());
holder.tvDate.setVisibility(View.VISIBLE);
Log.d(TAG, "default " + position + ", selected " + selectedItem);
if (position == selectedItem) {
Log.d(TAG, "center" + position);
holder.tvDate.setTextColor(Color.parseColor("#76FF03"));
holder.tvDate.setTextSize(35);
} else {
holder.tvDate.setTextColor(Color.WHITE);
holder.tvDate.setTextSize(18);
}
} else {
holder.tvDate.setVisibility(View.INVISIBLE);
}
}
public void setSelecteditem(int selecteditem) {
this.selectedItem = selecteditem;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return dateDataList.size();
}
#Override
public int getItemViewType(int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (labelerDate.getType() == VIEW_TYPE_PADDING) {
return VIEW_TYPE_PADDING;
} else {
return VIEW_TYPE_ITEM;
}
}
public class DateViewHolder extends RecyclerView.ViewHolder {
public TextView tvDate;
public DateViewHolder(View itemView) {
super(itemView);
tvDate = (TextView) itemView.findViewById(R.id.txt_date);
}
}
}
private class LabelerDate {
private int type;
private String number;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
}

You can use a LinearSnapHelper
Attach to your recyclerView like
val snapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(this)
Then, to get the center view, use
snapHelper.findSnapView(horizontalScrollView.layoutManager)?

As mentioned in the other answer, there is no direct way to do this.
This is probably how you can achieve what you described in the question.
Know the number of items visible on the screen.
Select the middle item programmatically every time the view is scrolled.
Keep a partially transparent image as an overlay on the middle item on the recyclerview. (You'll need to compute the coordinates based on the width of the recycler view or width of the screen and the width of the overlay image you choose to put.
Refresh the selected value in a text view below the recycler view every time there is a scroll.
The image overlays have to be placed in a way they appear connected and as one single control.

For this feature use EcoGallery library:
https://github.com/falnatsheh/EcoGallery

At first, I needed something similar, not this. But I was able to adapt #TranHieu solution to my needs, so I voted up his solution.
I wanted to create full-screen horizontal recyclerview that after user sroll updates scrollPosition to mostVisibleItem.
setup:
private void setUpScrolling() {
mRecyclerVIew.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
mRecyclerVIew.getViewTreeObserver().removeOnPreDrawListener(this);
CustomScrollListener listener = (CustomScrollListener) mScrollListener;
listener.width = mRecyclerVIew.getMeasuredWidth();
listener.dx = 0;
return true;
}
});
}
listener:
private class CustomScrollListener extends OnScrollListener {
private int mLastDx = 0;
int width = 0;
int dx = 0;
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (mLastDx != dx) {
scrollToMostVisibleItem();
} else {
dx = 0;
mLastDx = 0;
}
}
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
this.dx += dx;
}
private void scrollToMostVisibleItem() {
int direction = (dx > 0) ? 1 : -1;
dx = Math.abs(dx);
int shiftCount = Math.round(dx / width);
int pixelShift = dx % width;
if (pixelShift > width / 2) {
shiftCount++;
}
float targetScrollPixels = shiftCount * width;
float finalScrollPixels = (targetScrollPixels - dx) * direction;
if (finalScrollPixels != 0) {
mRecyclerVIew.smoothScrollBy((int) finalScrollPixels, 0);
mLastDx = (int) finalScrollPixels;
dx = 0;
}
}
}

I used another approach in my case.
you can find the deatils here: RecyclerView - How highlight central visible item during scroll1
In my opinion, my solution is more easy than the others.

If someone is looking for a more generic implementation, here is my code based on the answers of this thread:
Add the CenterLinearSnapHelper
public class CenterLinearSnapHelper extends LinearSnapHelper {
//Constants
public static final String TAG = CenterLinearSnapHelper.class.getSimpleName();
//Attributes
private Context context;
private float itemWidth;
private OnPaddingComputationListener listener;
//Constructors
/**
* A linear snap helper which helps centering the items in a recyclerview.
*
* #param itemWidth The (fixed) width of a child view in pixels.
*/
public CenterLinearSnapHelper(float itemWidth) {
this.itemWidth = itemWidth;
}
public void attachToRecyclerView(#Nullable RecyclerView recyclerView,
#NonNull OnPaddingComputationListener listener) throws IllegalStateException {
this.listener = listener;
//Calculates the padding for the first and end item
calculatePadding(recyclerView);
//Create a LinearSnapHelper and attach the recyclerView to it.
attachToRecyclerView(recyclerView);
}
public float getItemWidth() {
return itemWidth;
}
private void calculatePadding(RecyclerView recyclerView) {
if (recyclerView == null)
return;
ViewTreeObserver observer = recyclerView.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerView.getViewTreeObserver().removeOnPreDrawListener(this);
int finalWidth = recyclerView.getMeasuredWidth();
float padding = (finalWidth - itemWidth) / 2;
listener.onPadding(padding, finalWidth);
return true;
}
});
}
public interface OnPaddingComputationListener {
void onPadding(float padding, int finalWidth);
}
}
In your Activity/Fragment where you create your RecyclerView:
float itemWidth = getResources().getDimension(R.dimen.favorite_room_width);
CenterLinearSnapHelper snapHelper = new CenterLinearSnapHelper(itemWidth);
snapHelper.attachToRecyclerView(binding.listFavorites, (padding, finalWidth) -> {
//Set the adapter
roomAdapter = new RoomAdapter(requireContext(), rooms);
roomAdapter.addPaddingItems((int) padding);
roomAdapter.setOnToggleClickListener(FavoritesFragment.this);
binding.listFavorites.setAdapter(roomAdapter);
});
In your adapter:
public void addPaddingItems(int padding) {
if (padding < 0)
throw new IllegalStateException("Padding cannot be smaller than 0");
this.padding = padding;
//Add 2 new items as the first and last
//NOTE: If you update your existing dataset (e.g add new items), you should redo the calculation!
rooms.add(0, new Room("First"));
rooms.add(rooms.size(), new Room("Last"));
}
#Override
public int getItemViewType(int position) {
if (padding >= 0 && (position == 0 || position == rooms.size() - 1)) {
return VIEW_TYPE_PADDING;
}
return VIEW_TYPE_ITEM;
}
#NonNull
#Override
public RoomViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
ViewFavoriteRoomBinding binding = DataBindingUtil.inflate(inflater, R.layout.view_favorite_room, parent, false);
if (viewType == VIEW_TYPE_PADDING) {
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) binding.getRoot().getLayoutParams();
layoutParams.width = padding;
binding.getRoot().setLayoutParams(layoutParams);
}
RoomViewHolder viewHolder = new RoomViewHolder(context, binding, onToggleClickListener);
viewHolder.getRecyclerView().setRecycledViewPool(viewPool);
return viewHolder;
}

Related

how to achieve on androidTV scrolling mechanism like Netflix with RecyclerView

I am trying to achieve horizontal scroll mechanism on AndroidTV like Netflix
I tried to use SnapHelper without success and also override LinearLayoutManager smoothScroll
it looks nice but is not perfect and also bit lag.
When I am perform long press, the movement doesn't smoothie and sometimes its stuck.
I am receiving focus callback from the next item while I am tapping left or right on the controller and then using recyclerview.smoothScroolToPostion.
anyone can help me with that?
recyclerView.setAdapter(new MyAdapter(new FocusListener() {
#Override
public void dispatchFocusChanged(View view, boolean gainFocus) {
if(gainFocus) {
int position = recyclerView.getChildAdapterPosition(view);
recyclerView.smoothScrollToPosition(position);
}
}
}));
LinearLayoutManager
#Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
LinearSmoothScroller smoothScroller =
new LinearSmoothScroller(recyclerView.getContext()) {
//This returns the milliseconds it takes to
//scroll one pixel.
#Override
protected float calculateSpeedPerPixel
(DisplayMetrics displayMetrics) {
return 500f/displayMetrics.densityDpi;
}
#Override
protected int getHorizontalSnapPreference() {
return SNAP_TO_END;
}
#Override
protected int calculateTimeForDeceleration(int dx) {
return (int) Math.ceil(calculateTimeForScrolling(dx) / .3356);
}
#Override
protected int calculateTimeForScrolling(int dx) {
return 200;//super.calculateTimeForScrolling(dx);
}
};
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
SnapHelper
public class Snap extends LinearSnapHelper {
private Context context;
private OrientationHelper orientationHelper;
private Scroller scroller;
private int maxScrollDistance = 0;
private final static int MAX_SCROLL_ON_FLING_DURATION_MS = 1000;
#Override
public void attachToRecyclerView(#Nullable RecyclerView recyclerView) throws IllegalStateException {
if (recyclerView != null) {
context = recyclerView.getContext();
scroller = new Scroller(context, new DecelerateInterpolator());
} else {
scroller = null;
context = null;
}
super.attachToRecyclerView(recyclerView);
}
#Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
return findFirstView(layoutManager, getHelper(layoutManager));
}
public View findFirstView(RecyclerView.LayoutManager layoutManager, OrientationHelper orientationHelper) {
if (layoutManager == null) return null;
int childCount = layoutManager.getChildCount();
if (childCount == 0) return null;
int absClosest = Integer.MAX_VALUE;
View closestView = null;
int start = orientationHelper.getStartAfterPadding();
for (int i = 0; i < childCount; i++) {
View child = layoutManager.getChildAt(i);
int childStart = orientationHelper.getDecoratedStart(child);
int absDistanceToStart = Math.abs(childStart - start);
if (absDistanceToStart < absClosest) {
absClosest = absDistanceToStart;
closestView = child;
}
}
return closestView;
}
private OrientationHelper getHelper(RecyclerView.LayoutManager layoutManager) {
if (orientationHelper == null) {
orientationHelper = OrientationHelper.createHorizontalHelper(layoutManager);
}
return orientationHelper;
}
#Override
public int[] calculateDistanceToFinalSnap(RecyclerView.LayoutManager layoutManager, View targetView) {
int[] out = new int[2];
out[0] = distanceToStart(targetView, getHelper(layoutManager));
return out;
}
#Override
public int[] calculateScrollDistance(int velocityX, int velocityY) {
int[] out = new int[2];
OrientationHelper helper = orientationHelper;
if (null == helper)
return out;
if (maxScrollDistance == 0) {
maxScrollDistance = (helper.getEndAfterPadding() - helper.getStartAfterPadding()) / 2;
}
scroller.fling(0, 0, velocityX, velocityY, -maxScrollDistance, maxScrollDistance, 0, 0);
out[0] = scroller.getFinalX();
out[1] = scroller.getFinalY();
return out;
}
private int distanceToStart(View targetView, OrientationHelper helper) {
int childStart = helper.getDecoratedStart(targetView) + convertDpToPixel(40);
int containerStart = helper.getStartAfterPadding();
return 0;//childStart - containerStart;
}
public int convertDpToPixel(float dp) {
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
float px = dp * (metrics.densityDpi / 160f);
return (int) Math.round(px);
}
#Nullable
#Override
protected RecyclerView.SmoothScroller createScroller(final RecyclerView.LayoutManager layoutManager) {
if (layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)
return super.createScroller(layoutManager);
Context context = this.context;
return new LinearSmoothScroller(context) {
#Override
protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
int[] snapDistance = calculateDistanceToFinalSnap(layoutManager, targetView);
int dx = snapDistance[0];
int dy = snapDistance[1];
int dt = calculateTimeForDeceleration(Math.abs(dx));
int time = Math.max(1, Math.min(MAX_SCROLL_ON_FLING_DURATION_MS, dt));
action.update(dx, dy, time, mDecelerateInterpolator);
}
};
}}

Implement an scroll to select an item ListView

I want to use an ListView where you scroll the list to select an item.
It should be like a Seekbar but the thumb should be fixed and you must use the bar to adjust it. One problem am facing is, I have no idea how this kind of widget is called, making it hard for me to search. So I made this image below to give you an better idea. And to be fair, I don't even know if this is the right title for this kind of feature.
I usually see this kind of interface in clock setting (see below image)
Reference scroll horizontal at https://stackoverflow.com/a/38411582/5887320 by use RecyclerView instead of ListView.
This is my customize code to get virtical scroll :
1.dimens.xml
<dimen name="item_dob_width">100dp</dimen>
<dimen name="item_dob_height">50dp</dimen>
2.recyclerview_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center">
<TextView
android:id="#+id/tv_year"
android:layout_width="#dimen/item_dob_width"
android:layout_height="#dimen/item_dob_height"
android:textColor="#android:color/white"
android:background="#android:color/darker_gray"
android:textSize="28sp"
android:gravity="center"/>
</LinearLayout>
3.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="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"/>
</LinearLayout>
4.MainActivity.java
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.TextView;
import java.util.ArrayList;
public class MainActivity extends Activity{
private static final String TAG = MainActivity.class.getSimpleName();
public float firstItemHeightDate;
public float paddingDate;
public float itemHeightDate;
public int allPixelsDate;
public int finalHeightDate;
private DateAdapter dateAdapter;
private ArrayList<LabelerDate> labelerDates = new ArrayList<>();
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recyclerview);
getRecyclerviewDate();
}
public void getRecyclerviewDate() {
final RecyclerView recyclerViewDate = (RecyclerView) findViewById(R.id.recycler_view);
if (recyclerViewDate != null) {
recyclerViewDate.postDelayed(new Runnable() {
#Override
public void run() {
setDateValue();
}
}, 300);
/*recyclerViewDate.postDelayed(new Runnable() {
#Override
public void run() {
recyclerViewDate.smoothScrollToPosition(dateAdapter.getItemCount()-1);
setDateValue();
}
}, 5000);*/
}
ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
finalHeightDate = recyclerViewDate.getMeasuredHeight();
itemHeightDate = getResources().getDimension(R.dimen.item_dob_height);
paddingDate = (finalHeightDate - itemHeightDate) / 2;
firstItemHeightDate = paddingDate;
allPixelsDate = 0;
final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
dateLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerViewDate.setLayoutManager(dateLayoutManager);
recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
synchronized (this) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
calculatePositionAndScrollDate(recyclerView);
}
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
allPixelsDate += dy;
}
});
if (labelerDates == null) {
labelerDates = new ArrayList<>();
}
genLabelerDate();
dateAdapter = new DateAdapter(labelerDates, (int) firstItemHeightDate);
recyclerViewDate.setAdapter(dateAdapter);
dateAdapter.setSelecteditem(dateAdapter.getItemCount() - 1);
return true;
}
});
}
private void genLabelerDate() {
for (int i = 0; i < 32; i++) {
LabelerDate labelerDate = new LabelerDate();
labelerDate.setNumber(Integer.toString(i));
labelerDates.add(labelerDate);
if (i == 0 || i == 31) {
labelerDate.setType(DateAdapter.VIEW_TYPE_PADDING);
} else {
labelerDate.setType(DateAdapter.VIEW_TYPE_ITEM);
}
}
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void calculatePositionAndScrollDate(RecyclerView recyclerView) {
int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemHeightDate) / itemHeightDate);
if (expectedPositionDate == -1) {
expectedPositionDate = 0;
} else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) {
expectedPositionDate--;
}
scrollListToPositionDate(recyclerView, expectedPositionDate);
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void scrollListToPositionDate(RecyclerView recyclerView, int expectedPositionDate) {
float targetScrollPosDate = expectedPositionDate * itemHeightDate + firstItemHeightDate - paddingDate;
float missingPxDate = targetScrollPosDate - allPixelsDate;
if (missingPxDate != 0) {
//recyclerView.smoothScrollBy((int) missingPxDate, 0);//horizontal
recyclerView.smoothScrollBy(0, (int) missingPxDate);//virtical
}
setDateValue();
}
//
private void setDateValue() {
int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemHeightDate) / itemHeightDate);
int setColorDate = expectedPositionDateColor + 1;
//set color here
dateAdapter.setSelecteditem(setColorDate);
}
public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
private ArrayList<LabelerDate> dateDataList;
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int paddingHeightDate = 0;
private int selectedItem = -1;
public DateAdapter(ArrayList<LabelerDate> dateData, int paddingHeightDate) {
this.dateDataList = dateData;
this.paddingHeightDate = paddingHeightDate;
}
#Override
public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item,
parent, false);
return new DateViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item,
parent, false);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.height = paddingHeightDate;
view.setLayoutParams(layoutParams);
return new DateViewHolder(view);
}
}
#Override
public void onBindViewHolder(DateViewHolder holder, int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
holder.tvDate.setText(labelerDate.getNumber());
holder.tvDate.setVisibility(View.VISIBLE);
Log.d(TAG, "default " + position + ", selected " + selectedItem);
if (position == selectedItem) {
Log.d(TAG, "center" + position);
holder.tvDate.setTextColor(Color.parseColor("#76FF03"));
holder.tvDate.setTextSize(35);
} else {
holder.tvDate.setTextColor(Color.BLACK);
holder.tvDate.setTextSize(18);
}
} else {
holder.tvDate.setVisibility(View.INVISIBLE);
}
}
public void setSelecteditem(int selecteditem) {
this.selectedItem = selecteditem;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return dateDataList.size();
}
#Override
public int getItemViewType(int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (labelerDate.getType() == VIEW_TYPE_PADDING) {
return VIEW_TYPE_PADDING;
} else {
return VIEW_TYPE_ITEM;
}
}
public class DateViewHolder extends RecyclerView.ViewHolder {
public TextView tvDate;
public DateViewHolder(View itemView) {
super(itemView);
tvDate = (TextView) itemView.findViewById(R.id.tv_year);
}
}
}
private class LabelerDate {
private int type;
private String number;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
}

Not working Notifydatasetchange on RecyclerView with center selection Horizontal Scrollview?

Auto Scrolling is issue & also move to some specific position using code is difficult.
I am making two recyclerView dependent to each other with the Horizontal Scroll and center selection.
So my problem is using the method of Notifydatasetchanged and reseting recyclerView postion to 0 and it's scrolling selection range because it's returning wrong index...
When i want to get center selection index after changing data.
I am using below example to achieve this with some edits.
Get center visible item of RecycleView when scrolling
I need to change the data on scroll of First recyclerView Adapter to second recyclerView Adapter with data change.
But scrollview set the position in first position
I tried the notifyItemRangeChanged(int, int)
notifyItemRangeInserted(int, int) methods...
Detail Explanation : I am changing the type and reset the value of Look scrollview. I need to change the selected position of bottom scrollview. Specially I can't get the center position by changing data. Means I if i am notifying the adapter than index will remain as it is. I need to do work it like normal adapter after reset data.
Thanks in advance.
public void getRecyclerview_Type() {
final RecyclerView recyclerView_Type = (RecyclerView) findViewById(R.id.recycleView);
if (recyclerView_Type != null) {
recyclerView_Type.postDelayed(new Runnable() {
#Override
public void run() {
setTypeValue();
}
}, 300);
recyclerView_Type.postDelayed(new Runnable() {
#Override
public void run() {
// recyclerView_Type.smoothScrollToPosition(Type_Adapter.getItemCount() - 1);
setTypeValue();
}
}, 5000);
}
ViewTreeObserver treeObserver = recyclerView_Type.getViewTreeObserver();
treeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerView_Type.getViewTreeObserver().removeOnPreDrawListener(this);
finalWidthDate = recyclerView_Type.getMeasuredWidth();
itemWidthDate = getResources().getDimension(R.dimen.item_dob_width_padding);
paddingDate = (finalWidthDate - itemWidthDate) / 2;
firstItemWidthDate = paddingDate;
allPixelsDate = 0;
final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView_Type.setLayoutManager(dateLayoutManager);
recyclerView_Type.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
synchronized (this) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
calculatePositionAndScroll_Type(recyclerView);
}
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
allPixelsDate += dx;
}
});
if (mTypeBeanArrayList == null) {
mTypeBeanArrayList = new ArrayList<>();
}
getMedicationType();
Type_Adapter = new Medication_Type_RecyclerAdapter(Add_Reminder_medicationlook_Activity.this, mTypeBeanArrayList, (int) firstItemWidthDate);
recyclerView_Type.setAdapter(Type_Adapter);
Type_Adapter.setSelecteditem(Type_Adapter.getItemCount() - 1);
return true;
}
});
}
private void getMedicationType() {
for (int i = 0; i < mTypeBeanArrayList.size(); i++) {
Medication_TypeBean medication_typeBean = mTypeBeanArrayList.get(i);
Log.print("Image name :" +medication_typeBean.getType_image_name());
if (i == 0 || i == (mTypeBeanArrayList.size() - 1)) {
medication_typeBean.setType(VIEW_TYPE_PADDING);
} else {
medication_typeBean.setType(VIEW_TYPE_ITEM);
}
mTypeBeanArrayList.set(i, medication_typeBean);
}
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void calculatePositionAndScroll_Type(RecyclerView recyclerView) {
int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
if (expectedPositionDate == -1) {
expectedPositionDate = 0;
} else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) {
expectedPositionDate--;
}
scrollListToPosition_Type(recyclerView, expectedPositionDate);
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void scrollListToPosition_Type(RecyclerView recyclerView, int expectedPositionDate) {
float targetScrollPosDate = expectedPositionDate * itemWidthDate + firstItemWidthDate - paddingDate;
float missingPxDate = targetScrollPosDate - allPixelsDate;
if (missingPxDate != 0) {
recyclerView.smoothScrollBy((int) missingPxDate, 0);
}
setTypeValue();
}
private void setTypeValue() {
int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
int setColorDate = expectedPositionDateColor + 1;
Type_Adapter.setSelecteditem(setColorDate);
mTxt_type_name.setText(mTypeBeanArrayList.get(setColorDate).getMedication_type_name());
mSELECTED_TYPE_ID = setColorDate;
//NotifyLookChangetoType(setColorDate);
}
//Type Adapter
public class Medication_Type_RecyclerAdapter extends RecyclerView.Adapter<Medication_Type_RecyclerAdapter.ViewHolder> {
private ArrayList<Medication_TypeBean> medication_typeBeanArrayList;
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int paddingWidthDate = 0;
private Context mContext;
private int selectedItem = -1;
public Medication_Type_RecyclerAdapter(Context context, ArrayList<Medication_TypeBean> dateData, int paddingWidthDate) {
this.medication_typeBeanArrayList = dateData;
this.paddingWidthDate = paddingWidthDate;
this.mContext = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_medication_type,
parent, false);
return new ViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_medication_type,
parent, false);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.width = paddingWidthDate;
view.setLayoutParams(layoutParams);
return new ViewHolder(view);
}
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Medication_TypeBean medication_typeBean = mTypeBeanArrayList.get(position);
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
// holder.mTxt_Type.setText(medication_typeBean.getMedication_type_name());
//holder.mTxt_Type.setVisibility(View.VISIBLE);
holder.mImg_medication.setVisibility(View.VISIBLE);
int d = R.drawable.ic_type_pill;
try {
//Due to Offline requirements we do code like this get the images from our res folder
if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_pill")) {
d = R.drawable.ic_type_pill;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_patch")) {
d = R.drawable.ic_type_patch;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_capsule")) {
d = R.drawable.ic_type_capsule;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_ring")) {
d = R.drawable.ic_type_ring;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_inhaler")) {
d = R.drawable.ic_type_inhaler;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_spray")) {
d = R.drawable.ic_type_spray;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_bottle")) {
d = R.drawable.ic_type_bottle;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_drop")) {
d = R.drawable.ic_type_drop;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_pessaries")) {
d = R.drawable.ic_type_pessaries;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_sachets")) {
d = R.drawable.ic_type_sachets;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_tube")) {
d = R.drawable.ic_type_tube;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_suppository")) {
d = R.drawable.ic_type_suppository;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_injaction")) {
d = R.drawable.ic_type_injaction;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_spoon")) {
d = R.drawable.ic_type_spoon;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_powder")) {
d = R.drawable.ic_type_powder;
} else {
d = R.drawable.ic_type_pill;
}
Bitmap icon = BitmapFactory.decodeResource(mContext.getResources(),
d);
holder.mImg_medication.setImageBitmap(icon);
} catch (Exception e) {
Log.print(e);
}
// BitmapDrawable ob = new BitmapDrawable(mContext.getResources(), icon);
// img.setBackgroundDrawable(ob);
// holder.mImg_medication.setBackground(ob);
// Log.print("Type Adapter", "default " + position + ", selected " + selectedItem);
if (position == selectedItem) {
Log.print("Type adapter", "center" + position);
// holder.mTxt_Type.setTextColor(Color.parseColor("#76FF03"));
//holder.mImg_medication.setColorFilter(Color.GREEN);
// holder.mTxt_Type.setTextSize(35);
holder.mImg_medication.getLayoutParams().height = (int) mContext.getResources().getDimension(R.dimen.item_dob_width) + 10;
holder.mImg_medication.getLayoutParams().width = (int) mContext.getResources().getDimension(R.dimen.item_dob_width) + 10;
} else {
holder.mImg_medication.getLayoutParams().height = (int) mContext.getResources().getDimension(R.dimen.item_dob_width) - 10;
holder.mImg_medication.getLayoutParams().width = (int) mContext.getResources().getDimension(R.dimen.item_dob_width) - 10;
// holder.mTxt_Type.setTextColor(Color.WHITE);
//holder.mImg_medication.setColorFilter(null);
// holder.mTxt_Type.setVisibility(View.INVISIBLE);
// holder.mTxt_Type.setTextSize(18);
// holder.mImg_medication.getLayoutParams().height = 70;
// holder.mImg_medication.getLayoutParams().width = 70;
}
} else {
holder.mImg_medication.getLayoutParams().height = (int) mContext.getResources().getDimension(R.dimen.item_dob_width) - 10;
holder.mImg_medication.getLayoutParams().width = (int) mContext.getResources().getDimension(R.dimen.item_dob_width) - 10;
// holder.mTxt_Type.setVisibility(View.INVISIBLE);
holder.mImg_medication.setVisibility(View.INVISIBLE);
}
}
public void setSelecteditem(int selecteditem) {
this.selectedItem = selecteditem;
notifyDataSetChanged();
if (medication_lookBeanArrayList != null && Look_Adapter != null) {
NotifyLookChangetoType(selecteditem);
}
}
public int getSelecteditem() {
return selectedItem;
}
#Override
public int getItemCount() {
return medication_typeBeanArrayList.size();
}
#Override
public int getItemViewType(int position) {
Medication_TypeBean medication_typeBean = medication_typeBeanArrayList.get(position);
if (medication_typeBean.getType() == VIEW_TYPE_PADDING) {
return VIEW_TYPE_PADDING;
} else {
return VIEW_TYPE_ITEM;
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
//public TextView mTxt_Type;
public ImageView mImg_medication;
public ViewHolder(View itemView) {
super(itemView);
// mTxt_Type = (TextView) itemView.findViewById(R.id.mTxt);
mImg_medication = (ImageView) itemView.findViewById(R.id.mImg_medication);
}
}
}
//Type Adapter ends ************************************************
I am not sure I get you properly but you want to change the data of one recyclerview and reset the value of other recyclerview
Try using recyclerView.scrollToPosition(INDEX_YOU_WANT_TO_SCROLL_TO);
IF YOU WANT TO ACHIEVE THE CENTER POSTION OF THE DATA USE
recyclerview.scrollToPosition(arraylist.size()/2);
arrayList in which your drawable data is stored

Dynamic Endless RecyclerView scrolling issues

I have to create following layout
So far, I have successfully created the layout and populated all views. However, I am facing problem in the making ReyclerView Endless on first fragment.
Consider the RecyclerView has 10 items on first load, now on scroll I am adding another 10 items and so on. However, the RecyclerView isn't displaying those items, it's height gets fixed at the end of 10th element. I know that the elements are loaded correctly in RecyclerView and if I try to scroll with two fingers on emulator (GenyMotion), the RecyclerView scrolls just fine.
Update :-
Code for RecyclerView's Fragment -
public class CheckInFragmentRecyclerAdapter extends RecyclerView.Adapter<CheckInFragmentRecyclerAdapter.ViewHolder> {
final List<StoreNew> stores;
public CheckInFragmentRecyclerAdapter(final List<StoreNew> stores) {
this.stores = stores;
}
#Override
public CheckInFragmentRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.child_check_in_fragment, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(CheckInFragmentRecyclerAdapter.ViewHolder holder, int position) {
// Setting data
}
#Override
public int getItemCount() {
return stores.size();
}
/**
* Function to clear existing data from list
* #param stores StoreNew instance containing store information
*/
public void update(final List<StoreNew> stores) {
this.stores.clear();
this.stores.addAll(stores);
notifyDataSetChanged();
}
/**
* Function to add more data to list
* #param stores StoreNew instance containing store information
*/
public void addNewList(final List<StoreNew> stores) {
this.stores.addAll(stores);
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
// Initializing component
}
}
}
Update :-
Adding layouts for used screens.
main_screen.xml - This is the home screen
<android.support.v4.widget.NestedScrollView
android:id="#+id/scrollView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/home_footer"
android:layout_below="#+id/toolbar"
android:fillViewport="true">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Sliding Tab for showing images -->
<com.example.slidingtab.SlidingTabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white" />
<!-- ViewPager for Images -->
<android.support.v4.view.ViewPager
android:id="#+id/vpOffers"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_marginTop="8dp" />
<!-- Segmented Control for fragments -->
<info.hoang8f.android.segmented.SegmentedGroup xmlns:segmentedgroup="http://schemas.android.com/apk/res-auto"
android:id="#+id/segmented2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:layout_marginEnd="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:orientation="horizontal"
segmentedgroup:sc_border_width="1dp"
segmentedgroup:sc_corner_radius="4dp"
segmentedgroup:sc_tint_color="#color/black">
<RadioButton
android:id="#+id/rbTab1"
style="#style/segmented_radio_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="true"
android:textSize="#dimen/normal_text"
android:text="#string/check_in" />
<RadioButton
android:id="#+id/rbTab2"
style="#style/segmented_radio_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="#dimen/normal_text"
android:text="#string/upload_bill" />
<RadioButton
android:id="#+id/rbTab3"
style="#style/segmented_radio_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="#dimen/normal_text"
android:text="#string/redeem" />
</info.hoang8f.android.segmented.SegmentedGroup>
<!-- Custom wrap content ViewPager containing fragments -->
<!-- This will make sure that the height of ViewPager is equal to height of Fragment -->
<com.example.ui.custom.WrapContentHeightViewPager
android:id="#+id/vpFragments"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-7dp" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
WrapContentHeightViewPager.java
public class WrapContentHeightViewPager extends ViewPager {
private static final String TAG = WrapContentHeightViewPager.class.getSimpleName();
private int height = 0;
private int decorHeight = 0;
private int widthMeasuredSpec;
private boolean animateHeight;
private int rightHeight;
private int leftHeight;
private int scrollingPosition = -1;
private boolean enabled;
public WrapContentHeightViewPager(Context context) {
super(context);
init();
}
public WrapContentHeightViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
this.enabled = true;
init();
}
private void init() {
addOnPageChangeListener(new OnPageChangeListener() {
public int state;
#Override
public void onPageScrolled(int position, float offset, int positionOffsetPixels) {}
#Override
public void onPageSelected(int position) {
if (state == SCROLL_STATE_IDLE) {
height = 0; // measure the selected page in-case it's a change without scrolling
Log.d(TAG, "onPageSelected:" + position);
}
}
#Override
public void onPageScrollStateChanged(int state) {
this.state = state;
}
});
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return this.enabled && super.onTouchEvent(event);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return this.enabled && super.onInterceptTouchEvent(event);
}
public void setPagingEnabled(boolean enabled) {
this.enabled = enabled;
}
#Override
public void setAdapter(PagerAdapter adapter) {
height = 0; // so we measure the new content in onMeasure
super.setAdapter(new PagerAdapterWrapper(adapter));
}
/**
* Allows to redraw the view size to wrap the content of the bigger child.
*
* #param widthMeasureSpec with measured
* #param heightMeasureSpec height measured
*/
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
widthMeasuredSpec = widthMeasureSpec;
int mode = MeasureSpec.getMode(heightMeasureSpec);
if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
if(height == 0) {
// measure vertical decor (i.e. PagerTitleStrip) based on ViewPager implementation
decorHeight = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if(lp != null && lp.isDecor) {
int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;
if(consumeVertical) {
decorHeight += child.getMeasuredHeight() ;
}
}
}
// make sure that we have an height (not sure if this is necessary because it seems that onPageScrolled is called right after
int position = getCurrentItem();
View child = getViewAtPosition(position);
if (child != null) {
height = measureViewHeight(child);
}
//Log.d(TAG, "onMeasure height:" + height + " decor:" + decorHeight);
}
int totalHeight = height + decorHeight + getPaddingBottom() + getPaddingTop();
heightMeasureSpec = MeasureSpec.makeMeasureSpec(totalHeight, MeasureSpec.EXACTLY);
//Log.d(TAG, "onMeasure total height:" + totalHeight);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
public void onPageScrolled(int position, float offset, int positionOffsetPixels) {
super.onPageScrolled(position, offset, positionOffsetPixels);
// cache scrolled view heights
if (scrollingPosition != position) {
scrollingPosition = position;
// scrolled position is always the left scrolled page
View leftView = getViewAtPosition(position);
View rightView = getViewAtPosition(position + 1);
if (leftView != null && rightView != null) {
leftHeight = measureViewHeight(leftView);
rightHeight = measureViewHeight(rightView);
animateHeight = true;
//Log.d(TAG, "onPageScrolled heights left:" + leftHeight + " right:" + rightHeight);
} else {
animateHeight = false;
}
}
if (animateHeight) {
int newHeight = (int) (leftHeight * (1 - offset) + rightHeight * (offset));
if (height != newHeight) {
//Log.d(TAG, "onPageScrolled height change:" + newHeight);
height = newHeight;
requestLayout();
invalidate();
}
}
}
private int measureViewHeight(View view) {
view.measure(getChildMeasureSpec(widthMeasuredSpec, getPaddingLeft() + getPaddingRight(), view.getLayoutParams().width), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
return view.getMeasuredHeight();
}
protected View getViewAtPosition(int position) {
if(getAdapter() != null) {
Object objectAtPosition = ((PagerAdapterWrapper) getAdapter()).getObjectAtPosition(position);
if (objectAtPosition != null) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child != null && getAdapter().isViewFromObject(child, objectAtPosition)) {
return child;
}
}
}
}
return null;
}
/**
* Wrapper for PagerAdapter so we can ask for Object at index
*/
private class PagerAdapterWrapper extends PagerAdapter {
private final PagerAdapter innerAdapter;
private SparseArray<Object> objects;
public PagerAdapterWrapper(PagerAdapter adapter) {
this.innerAdapter = adapter;
this.objects = new SparseArray<>(adapter.getCount());
}
#Override
public void startUpdate(ViewGroup container) {
innerAdapter.startUpdate(container);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Object object = innerAdapter.instantiateItem(container, position);
objects.put(position, object);
return object;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
innerAdapter.destroyItem(container, position, object);
objects.remove(position);
}
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
innerAdapter.setPrimaryItem(container, position, object);
}
#Override
public void finishUpdate(ViewGroup container) {
innerAdapter.finishUpdate(container);
}
#Override
public Parcelable saveState() {
return innerAdapter.saveState();
}
#Override
public void restoreState(Parcelable state, ClassLoader loader) {
innerAdapter.restoreState(state, loader);
}
#Override
public int getItemPosition(Object object) {
return innerAdapter.getItemPosition(object);
}
#Override
public void notifyDataSetChanged() {
innerAdapter.notifyDataSetChanged();
}
#Override
public void registerDataSetObserver(DataSetObserver observer) {
innerAdapter.registerDataSetObserver(observer);
}
#Override
public void unregisterDataSetObserver(DataSetObserver observer) {
innerAdapter.unregisterDataSetObserver(observer);
}
#Override
public float getPageWidth(int position) {
return innerAdapter.getPageWidth(position);
}
#Override
public CharSequence getPageTitle(int position) {
return innerAdapter.getPageTitle(position);
}
#Override
public int getCount() {
return innerAdapter.getCount();
}
#Override
public boolean isViewFromObject(View view, Object object) {
return innerAdapter.isViewFromObject(view, object);
}
public Object getObjectAtPosition(int position) {
return objects.get(position);
}
}
}
first_fragment.xml
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/lvCheckIn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:requiresFadingEdge="none"
android:fadingEdgeLength="0dp"
android:orientation="vertical" />
Adding more data to the RecyclerView on scrolling -
private RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = adapter.getItemCount();
firstVisibleItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold) && current_page < totalPages) {
// End has been reached
// Do something
current_page++;
// Sending request to server
loading = true;
}
}
};
When data is received via API (adapter already added above)-
adapter.addNewList(homePageNew.checkin_stores.stores);
There seems to be a similar discussion here: ViewPager in a NestedScrollView
Maybe the sample of the Naruto guy (https://github.com/TheLittleNaruto/SupportDesignExample/) could solve your situation.
Edit
I'm not sure if I'm getting the point of your question. Anyway here you can a possible solution to your situation.
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/app_bar_layout"
>
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
>
---- include here everything before the pager ----
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
---- this is your pager
<include layout="#layout/fragment_pager"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/app_bar_layout"
/>
</android.support.design.widget.CoordinatorLayout>
Then you can just listen to the RecycleView in the pager to add items as the RecycleView scrolls to bottom.
I hope it helped
You can see my implementation of the endless RecyclerView scroll:
GalleryActivity.java
public class GalleryActivity extends AppCompatActivity {
private StaggeredGridLayoutManager layoutManager;
private RecyclerView recyclerView;
private ImageRecyclerViewAdapter imageAdapter;
private List<ImageItem> images;
private ImageSearchClient imageSearchClient;
private String query;
private int currentStartPosition = 1;
private boolean loading = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gallery);
getSupportActionBar().setHomeButtonEnabled(true);
query = getIntent().getStringExtra(Utils.QUERY_TAG);
if (query == null)
return;
imageSearchClient = new ImageSearchClient(query);
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
layoutManager = new StaggeredGridLayoutManager(2, 1);
recyclerView.setLayoutManager(layoutManager);
images = new ArrayList<>();
imageAdapter = new ImageRecyclerViewAdapter(this, images);
recyclerView.setAdapter(imageAdapter);
recyclerView.addOnScrollListener(new EndlessRecyclerScrollListener());
loadMoreData();
}
private void loadMoreData() {
loading = true;
imageSearchClient.getService().customSearch(Utils.API_KEY, Utils.CX_KEY, query,
Utils.IMAGE_SEARCH_TYPE,
currentStartPosition,
Utils.ITEMS_COUNT,
new Callback<ImageResponse>() {
#Override
public void success(ImageResponse imageResponse, Response response) {
List<ImageItem> items = imageResponse.getItems();
for (ImageItem item : items) {
images.add(item);
}
imageAdapter.notifyDataSetChanged();
currentStartPosition += items.size();
loading = false;
}
#Override
public void failure(RetrofitError error) {
Log.e(GalleryActivity.class.getSimpleName(),
error.getResponse().getReason());
}
});
}
private class EndlessRecyclerScrollListener extends RecyclerView.OnScrollListener {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int[] visibleItems = layoutManager.findLastVisibleItemPositions(null);
int lastItem = 0;
for (int i : visibleItems) {
lastItem = Math.max(lastItem, i);
}
if (lastItem > 0 && lastItem > images.size() - Utils.ITEMS_COUNT && !loading) {
if (NetworkUtils.hasConnection(GalleryActivity.this)) {
loadMoreData();
} else {
Toast.makeText(GalleryActivity.this, R.string.network_error,
Toast.LENGTH_SHORT).show();
}
}
}
}
}
ImageRecyclerViewAdapter.java (nothing is extraordinary here):
public class ImageRecyclerViewAdapter extends RecyclerView.Adapter<ImageViewHolder> {
private List<ImageItem> itemList;
private Context context;
private int parentWidth = 0;
public ImageRecyclerViewAdapter(Context context, List<ImageItem> itemList) {
this.context = context;
this.itemList = itemList;
}
#Override
public ImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView =
LayoutInflater.from(parent.getContext()).inflate(R.layout.gallery_card_item, null);
ImageViewHolder imageViewHolder = new ImageViewHolder(layoutView);
parentWidth = parent.getWidth();
return imageViewHolder;
}
#Override
public void onBindViewHolder(final ImageViewHolder holder, int position) {
Picasso.with(context)
.load(itemList.get(position).getLink())
.error(R.drawable.error_image)
.placeholder(R.drawable.progress_animation)
.into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
int targetWidth = parentWidth / 2;
float ratio = (float) bitmap.getHeight() / (float) bitmap.getWidth();
float heightFloat = ((float) targetWidth) * ratio;
final android.view.ViewGroup.MarginLayoutParams layoutParams
= (ViewGroup.MarginLayoutParams) holder.image.getLayoutParams();
layoutParams.height = (int) heightFloat;
layoutParams.width = (int) targetWidth;
holder.image.setLayoutParams(layoutParams);
holder.image.setImageBitmap(bitmap);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
}
#Override
public int getItemCount() {
return this.itemList.size();
}
}

get recycler item absolute coordinates

I am trying to make the items in a recyclerview to move when scrolling up or down in a matter which depends on the the Y coordinate of the child views. However I can't seem to be able to get the coordinate of the children.
How can I get the Y coordinate?
I need it in the onBindViewHolder method so that I can set the layout param of the child view according to it.
in your Activity/Fragment :
RecyclerView yourRecyclerView;
FansAdapter yourRecyclerAdapter;
........
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
yourRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
yourRecyclerView.setLayoutManager(new LinearLayoutManager(this));
yourRecyclerView.setHasFixedSize(true);
yourRecyclerAdapter = new YourAdapter (populateYourList(), this);
yourRecyclerView.setAdapter(yourRecyclerAdapter);
yourRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
View firstVisibleChild = recyclerView.getChildAt(0);//change if necessary. determine that item(items) you need to find
final ImageView imageView = (ImageView) firstVisibleChild.findViewById(R.id.item_image);
if (null != imageView) {
imageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int[] locationsFrom = new int[2];
imageView.getLocationInWindow(locationsFrom);
int xStart = locationsFrom[0];
int yStart = locationsFrom[1];
Log.d("LOG_TAG", "your item xEnd=" + xStart +" yEnd=" + yStart);
if (null != yourRecyclerAdapter) {
yourRecyclerAdapter.setX(xStart);
yourRecyclerAdapter.setY(yStart);
}
}
}
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
if (null != yourRecyclerView) {
yourRecyclerView.clearOnScrollListeners();
}
}
and Adapter:
public class YourAdapter extends RecyclerView.Adapter<YourAdapter.YouViewHolder> {
ArrayList<String> fansList;
Context mContext;
private int x;
private int y;
public YourAdapter(ArrayList<String> fansList, Context context) {
this.fansList = fansList;
this.mContext = context;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
#Override
public YouViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
if (viewGroup instanceof RecyclerView) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycle_item_fans, viewGroup, false);
view.setFocusable(true);
return new YouViewHolder(view);
} else {
throw new RuntimeException("Not bound to RecyclerView");
}
}
#Override
public void onBindViewHolder(YouViewHolder holder, int position) {
holder.itemName.setText(fansList.get(position));
Log.v("LOG_TAG","x="+x+" y="+y);// use y or x
}
#Override
public int getItemCount() {
return fansList.size();
}
//holder
public final class YouViewHolder extends RecyclerView.ViewHolder {
public final TextView itemName;
public final ImageView imageView;
public YouViewHolder(View rowView) {
super(rowView);
this.itemName = (TextView) rowView.findViewById(R.id.text_fan_name);
this.imageView = (ImageView) rowView.findViewById(R.id.image_second_free);
}
}
}

Categories

Resources