recyclerview returning same position on scroll - java

Hi everyone i am developing an app where i'm using recyclerview along with mapfragment. I have made a carousel with recyclerview and onscroll of recyclerview i'm trying to change the camera position according to LatLong of particular item now my problem is that when i scroll recyclerview each time it returns me the same position. I had implemented viewpager earlier and there i used onPageSelected method which worked fine but now as i have switched to recyclerview i'm having this issue.
I dont know where i'm going wrong
So please if someone can assist me here.
Thank You
MainActivity code
carousel_recycler = (RecyclerView)findViewById(R.id.carousel_recycler);
providers = new ArrayList<>();
manager = new CenterZoomLayoutManager(this, LinearLayoutManager.HORIZONTAL,false);
final int scrollPosition = manager.findFirstVisibleItemPosition();
carousel_recycler.setLayoutManager(manager);
carousel_recycler.setHasFixedSize(true);
place_data = getResources().getStringArray(R.array.nearby_place);
for(String places : place_data){
NearbyDataProvider nearbyDataProvider = new NearbyDataProvider(listItems[i],places,latitude[j],longitude[k]);
providers.add(nearbyDataProvider);
i++;
j++;
k++;
}
adapter = new CarouselRecycler_Adapter(providers);
carousel_recycler.setAdapter(adapter);
LinearSnapHelper snapHelper = new LinearSnapHelper(){
#Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {
View centerView = findSnapView(layoutManager);
if (centerView == null)
return RecyclerView.NO_POSITION;
int position = layoutManager.getPosition(centerView);
int targetPosition = -1;
if (layoutManager.canScrollHorizontally()) {
if (velocityX < 0) {
targetPosition = position - 1;
} else {
targetPosition = position + 1;
}
}
if (layoutManager.canScrollVertically()) {
if (velocityY < 0) {
targetPosition = position - 1;
} else {
targetPosition = position + 1;
}
}
final int firstItem = 0;
final int lastItem = layoutManager.getItemCount() - 1;
targetPosition = Math.min(lastItem, Math.max(targetPosition, firstItem));
return targetPosition;
}
};
snapHelper.attachToRecyclerView(carousel_recycler);
carousel_recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
index = newState;
if(providers.size()>0){
if(!providers.get(index).getLattitude().isEmpty()
&& !providers.get(index).getLongitude().isEmpty()){
double lat = Double.parseDouble(providers.get(index).getLattitude());
double lang = Double.parseDouble(providers.get(index).getLongitude());
changeCameraPosition(lat,lang);
}
else {
Toast.makeText(MapsMarkerActivity.this, "No last location found", Toast.LENGTH_SHORT).show();
}
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
/*index = scrollPosition;
if(providers.size()>0){
if(!providers.get(scrollPosition).getLattitude().isEmpty()
&& !providers.get(scrollPosition).getLongitude().isEmpty()){
double lat = Double.parseDouble(providers.get(scrollPosition).getLattitude());
double lang = Double.parseDouble(providers.get(scrollPosition).getLongitude());
changeCameraPosition(lat,lang);
}
else {
Toast.makeText(MapsMarkerActivity.this, "No last location found", Toast.LENGTH_SHORT).show();
}
}*/
}
});
}

Will this solve your problem?
rv.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
try {
LinearLayoutManager layoutManager = ((LinearLayoutManager) rv.getLayoutManager());
int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();
Log.e("refresh", "State - " + newState + " : firstVisiblePosition" + firstVisiblePosition);
} catch (Exception e) {
e.printStackTrace();
}
}
});

Related

RecyclerView's Items Position On The Screen Changes On The Run

I have this RecyclerView where any item can be clicked to open, and when it's opened, it grows (picture 1 is a closed item, picture 2 is an opened item)
The layout file of an item holds two states - the closed card, and the opened card. To switch between them, I change only the visibility of any state. Here are the methods that control an item's opening (expanding) or closing (shrinking) :
/**
* handles the expanding functionality.
*/
public void expand() {
shrink = true;
if (expandedItem != -1) {
notifyItemChanged(expandedItem);
}
expandedItem = getLayoutPosition();
toggleExpandShrinkIcon(true);
if (!openedFromParent[1]) {
openedFromParent[1] = true;
} else {
openedFromParent[0] = false;
}
expandedContainer.setVisibility(View.VISIBLE);
shrunkProgressBar.setVisibility(View.INVISIBLE);
}
/**
* handles the shrinking functionality.
*/
public void shrink() {
toggleExpandShrinkIcon(false);
expandedContainer.setVisibility(View.GONE);
shrunkProgressBar.setVisibility(View.VISIBLE);
shrink = false;
}
These methods are located in the RecyclerView's adapter, inside of the ViewHolder's class, and they are public so I could use them also out of the RecyclerView's adapter class (not only by clicking), as I did when one item hovers another.
Recently I added drag-to-hover functionality (using this library) so that I can drag any item on top of any other item, and when one item hovers another item, the lower item gets opened.
When an item gets opened, it pushes all the other items below it to be able to expand without hiding the items under it (like in the first video).
When moving from hovering one item to another, say from the second item to the third, when hovering the second item it gets opened and the third item is pushed down, and when moving to the third item the second item gets closed, but the third item won't go back up.
Then when hovering the third item, it gets opened on the fourth item (see the second video to understand better).
Here's the code in the class that handles the hovering action:
public class HoveringCallback extends ItemTouchHelper.SimpleCallback {
//re-used list for selecting a swap target
private List<RecyclerView.ViewHolder> swapTargets = new ArrayList<>();
//re used for for sorting swap targets
private List<Integer> distances = new ArrayList<>();
private float selectedStartX;
private float selectedStartY;
public interface OnDroppedListener {
void onDroppedOn(ActiveGoalsAdapter.ActiveGoalsViewHolder viewHolder, ActiveGoalsAdapter.ActiveGoalsViewHolder target);
}
private List<OnDroppedListener> onDroppedListeners = new ArrayList<>();
#Nullable
private RecyclerView recyclerView;
#Nullable
ActiveGoalsAdapter.ActiveGoalsViewHolder selected;
#Nullable
private ActiveGoalsAdapter.ActiveGoalsViewHolder hovered;
ItemBackgroundCallback backgroundCallback;
public HoveringCallback() {
super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0);
}
public void attachToRecyclerView(#Nullable RecyclerView recyclerView) {
this.recyclerView = recyclerView;
}
public void addOnDropListener(OnDroppedListener listener) {
onDroppedListeners.add(listener);
}
public void removeOnDropListener(OnDroppedListener listener) {
onDroppedListeners.remove(listener);
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
if (viewHolder == null) {
if (hovered != null) {
notifyDroppedOnListeners(hovered);
}
} else {
selectedStartX = viewHolder.itemView.getLeft();
selectedStartY = viewHolder.itemView.getTop();
}
this.selected = (ActiveGoalsAdapter.ActiveGoalsViewHolder) viewHolder;
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE && viewHolder != null) {
viewHolder.itemView.setBackgroundColor(backgroundCallback.getDraggingBackgroundColor(viewHolder));
}
}
private void notifyDroppedOnListeners(ActiveGoalsAdapter.ActiveGoalsViewHolder holder) {
for (OnDroppedListener listener : onDroppedListeners) {
listener.onDroppedOn(selected, (ActiveGoalsAdapter.ActiveGoalsViewHolder) holder);
}
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setBackgroundColor(backgroundCallback.getDefaultBackgroundColor(viewHolder));
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
}
#Override
public void onChildDraw(Canvas canvas, RecyclerView parent, RecyclerView.ViewHolder viewHolder,
float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(canvas, parent, viewHolder, dX, dY, actionState, isCurrentlyActive);
if (actionState != ItemTouchHelper.ACTION_STATE_DRAG) {
return;
}
if (recyclerView == null || selected == null) {
return;
}
final RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();
final int childCount = lm.getChildCount();
List<RecyclerView.ViewHolder> swapTargets = findSwapTargets((ActiveGoalsAdapter.ActiveGoalsViewHolder) viewHolder, dX, dY);
final int x = (int) (selectedStartX + dX);
final int y = (int) (selectedStartY + dY);
hovered = (ActiveGoalsAdapter.ActiveGoalsViewHolder) chooseDropTarget((ActiveGoalsAdapter.ActiveGoalsViewHolder) viewHolder, swapTargets, x, y);
if (hovered == null) {
this.swapTargets.clear();
this.distances.clear();
}
for (int i = 0; i < childCount; i++) {
final View child = lm.getChildAt(i);
if (viewHolder.itemView == child) {
continue;
}
ActiveGoalsAdapter.ActiveGoalsViewHolder childViewHolder = (ActiveGoalsAdapter.ActiveGoalsViewHolder) parent.findContainingViewHolder(child);
if (childViewHolder == null || childViewHolder.getAdapterPosition() == RecyclerView.NO_POSITION) {
continue;
}
final int count = canvas.save();
if (childViewHolder == hovered) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
childViewHolder.expand();
}
}, 500);
} else {
if(!childViewHolder.isShrunk()) {
childViewHolder.shrink();
if(canvas.getSaveCount() != count) {
canvas.restoreToCount(count);
}
}
}
}
}
private List<RecyclerView.ViewHolder> findSwapTargets(ActiveGoalsAdapter.ActiveGoalsViewHolder viewHolder, float dX, float dY) {
swapTargets.clear();
distances.clear();
final int margin = getBoundingBoxMargin();
final int left = Math.round(selectedStartX + dX) - margin;
final int top = Math.round(selectedStartY + dY) - margin;
final int right = left + viewHolder.itemView.getWidth() + 2 * margin;
final int bottom = top + viewHolder.itemView.getHeight() + 2 * margin;
final int centerX = (left + right) / 2;
final int centerY = (top + bottom) / 2;
final RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();
final int childCount = lm.getChildCount();
for (int i = 0; i < childCount; i++) {
View other = lm.getChildAt(i);
if (other == viewHolder.itemView) {
continue; //myself!
}
if (other.getBottom() < top || other.getTop() > bottom
|| other.getRight() < left || other.getLeft() > right) {
continue;
}
final ActiveGoalsAdapter.ActiveGoalsViewHolder otherVh = (ActiveGoalsAdapter.ActiveGoalsViewHolder) recyclerView.getChildViewHolder(other);
if (canDropOver(recyclerView, selected, otherVh)) {
// find the index to add
final int dx = Math.abs(centerX - (other.getLeft() + other.getRight()) / 2);
final int dy = Math.abs(centerY - (other.getTop() + other.getBottom()) / 2);
final int dist = dx * dx + dy * dy;
int pos = 0;
final int cnt = swapTargets.size();
for (int j = 0; j < cnt; j++) {
if (dist > distances.get(j)) {
pos++;
} else {
break;
}
}
swapTargets.add(pos, otherVh);
distances.add(pos, dist);
}
}
return swapTargets;
}
#Override
public float getMoveThreshold(RecyclerView.ViewHolder viewHolder) {
return 0.05f;
}
#Override
public RecyclerView.ViewHolder chooseDropTarget(RecyclerView.ViewHolder selected,
List<RecyclerView.ViewHolder> dropTargets,
int curX, int curY) {
int right = curX + selected.itemView.getWidth();
int bottom = curY + selected.itemView.getHeight();
ActiveGoalsAdapter.ActiveGoalsViewHolder winner = null;
int winnerScore = -1;
final int dx = curX - selected.itemView.getLeft();
final int dy = curY - selected.itemView.getTop();
final int targetsSize = dropTargets.size();
for (int i = 0; i < targetsSize; i++) {
final ActiveGoalsAdapter.ActiveGoalsViewHolder target = (ActiveGoalsAdapter.ActiveGoalsViewHolder) dropTargets.get(i);
if (dx > 0) {
int diff = target.itemView.getRight() - right;
if (diff < 0 && target.itemView.getRight() > selected.itemView.getRight()) {
final int score = Math.abs(diff);
if (score > winnerScore) {
winnerScore = score;
winner = target;
}
}
}
if (dx < 0) {
int diff = target.itemView.getLeft() - curX;
if (diff > 0 && target.itemView.getLeft() < selected.itemView.getLeft()) {
final int score = Math.abs(diff);
if (score > winnerScore) {
winnerScore = score;
winner = target;
}
}
}
if (dy < 0) {
int diff = target.itemView.getTop() - curY;
if (target.itemView.getTop() < selected.itemView.getTop()) {
final int score = Math.abs(diff);
if (score > winnerScore) {
winnerScore = score;
winner = target;
}
}
}
if (dy > 0) {
int diff = target.itemView.getBottom() - bottom;
if (target.itemView.getBottom() > selected.itemView.getBottom()) {
final int score = Math.abs(diff);
if (score > winnerScore) {
winnerScore = score;
winner = target;
}
}
}
}
return winner;
}
}
How can I solve this? (make the third item go back up, like when the drag is released, at the end of the second video)
Help would be highly appreciated! (:
First picture (closed item):
Second picture (opened item):
First Video (item gets opened & closed in the list):
Second video (item dragged):
Without studying the code of Library I assume positions are being changed so it is highly likely that .notifyItemChanged(position) is invoked, and in that, expand function invoke because there is no check to isolate the expand function from unhandled events
A simple answer can be to save the state and disable the expansion of items
Set isDragActive to true when where the item is long pressed
private static isDragActive = false
#Overrides
public onCreateViewHolder(){
itemView.setOnItemLongClickListener(new OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
int pos, long id) {
isDragActive = true;
return true;
}
});
}
public void expand() {
if(!isDragActive){
shrink = true;
if (expandedItem != -1) {
notifyItemChanged(expandedItem);
}
expandedItem = getLayoutPosition();
toggleExpandShrinkIcon(true);
if (!openedFromParent[1]) {
openedFromParent[1] = true;
} else {
openedFromParent[0] = false;
}
expandedContainer.setVisibility(View.VISIBLE);
shrunkProgressBar.setVisibility(View.INVISIBLE);
}
}
You can also use the library given below instead of above solution
https://github.com/mikepenz/FastAdapter
I think the best bet is to set your code that opens each recyclerview using ACTION_UP so that the code is triggered on the lifting of the finger, not on the sensing of it.
button.setOnTouchListener(new View.OnTouchListener() { #Override public boolean onTouch(View view, MotionEvent motionEvent) { sw
If(MotionEvent.ACTION_UP) {Toast.makeText(MainActivity.this, "Up", Toast.LENGTH_SHORT).show(); }

GridLayoutManager with EndlessRecyclerOnScrollListener recylerView.addOnScrollListener automatically scrolling in android

public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
public String TAG = EndlessRecyclerOnScrollListener.class.getSimpleName();
private int previousTotal = 0; // The total number of items in the dataset after the last load
// True if we are still waiting for the last set of data to load.
private int visibleThreshold = 0; // The minimum amount of items to have below your current scroll position before loading more.
int firstVisibleItem, visibleItemCount, totalItemCount;
private int current_page = 0;
private GridLayoutManager mLinearLayoutManager;
public EndlessRecyclerOnScrollListener(GridLayoutManager linearLayoutManager) {
this.mLinearLayoutManager = linearLayoutManager;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = mLinearLayoutManager.getItemCount();
firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
Log.i("ProdctOffer load", mLinearLayoutManager.findLastCompletelyVisibleItemPosition() + "//" + ProductList.size() + "//" + loading);
if (mLinearLayoutManager.findLastCompletelyVisibleItemPosition() == ProductList.size() - 1 && loading) {
//bottom of list!
current_page++;
onLoadMore(current_page);
}
}
public abstract void onLoadMore(int current_page);
}

RecyclerView Endless Scroll

I know there are lot of questions regarding the same problem but I am not able to find my issue. When I am printing URL in my HttpRequest Class, it's showing the right URL but its still loading the same data.
recyclerView.addOnScrollListener(new asd(manager ) {
boolean loading = false;
int scrollIndex = 1;
#Override
public void onScrolledToEnd() {
if(!loading){
loading = true;
try {
scrollIndex = scrollIndex+10;
String url = "https // xyz com?q=" + query + "&scrollIndex="+ scrollIndex + "&key=" + getResources().getString(R.string.api_key);
new HttpRequest(url,SearchActivity.this).execute();
adapter.notifyItemRangeChanged(list.size(),10);
adapter.notifyDataSetChanged();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
loading = false;
}
});
Here is OnScrollListener
public abstract class asd extends RecyclerView.OnScrollListener {
public static String TAG = asd.class.getSimpleName();
// use your LayoutManager instead
private LinearLayoutManager llm;
public asd(LinearLayoutManager sglm) {
this.llm = sglm;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (!recyclerView.canScrollVertically(1)) {
onScrolledToEnd();
}
}
public abstract void onScrolledToEnd();
}
I use this and it's working, maybe you have to play around a little with values to fit your specific case. However I hope it helps:
mRecyclerView.addOnScrollListener(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);
LinearLayoutManager linearLayoutManager = ((LinearLayoutManager)mRecyclerView.getLayoutManager());
int visibleItemCount = linearLayoutManager.getChildCount();
int totalItemCount = linearLayoutManager.getItemCount();
int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
//Log.d(TAG,"Visible="+visibleItemCount+", total="+totalItemCount+", first="+firstVisibleItemPosition);
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
&& firstVisibleItemPosition >= 0
&& totalItemCount >= pageSize) {
//Log.d(TAG,"Entrato, lancio");
loadMoreItems();
}
}
});
Here pageSize is the number of the elements I'm loading every time; the loadMoreItems() method is responsible to fetch the data from the internet and notify the adapter.
Use this.
LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(mLayoutManager,maxCount) {
#Override
public void onLoadMore(int current_page) {
/// onLoadMore(current_page);
loadmoreContacts(current_page);
Log.e("onLoadMore ", "" + current_page);
}
});
Create new class
public abstract class EndlessRecyclerOnScrollListener extends
RecyclerView.OnScrollListener {
public static String TAG = EndlessRecyclerOnScrollListener.class
.getSimpleName();
int maxCount;
private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 5;
int firstVisibleItem, visibleItemCount, totalItemCount;
private int current_page = 1;
private LinearLayoutManager mLinearLayoutManager;
public EndlessRecyclerOnScrollListener(
LinearLayoutManager linearLayoutManager,int maxCount) {
this.mLinearLayoutManager = linearLayoutManager;
this.maxCount = maxCount;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = mLinearLayoutManager.getItemCount();
firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if(totalItemCount>=maxCount){
loading=false;
return;
}
if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
current_page++;
onLoadMore(current_page);
loading = true;
}
}
public abstract void onLoadMore(int current_page);}

Android - NullPointerException SimpleAdapter - Pinned section ListView?

I created a ListView with pinned which works well once. But when I arrive latest item of ListView crashes on me and says NullPointerException.
I need that when I arrive latest item get next 5 of items .
MainActivity.java :
public class PinnedSectionListActivity extends ListActivity implements OnClickListener {
public static Context context;
public static Context _context;
public static SQLiteDatabase sql;
public Cursor cursor;
public Cursor cursorA;
public boolean flag = false;
public Integer count = 0;
public List<Items> result;
public List<ItemsT> resultT;
static class SimpleAdapter extends ArrayAdapter<Item> implements PinnedSectionListView.PinnedSectionListAdapter {
private List<Items> Results;
private List<ItemsT> ResultsT;
private Integer counts;
private static final int[] COLORS = new int[] {
R.color.green_light, R.color.orange_light,
R.color.blue_light, R.color.red_light };
public SimpleAdapter(Context context,List<Items> MResults,List<ItemsT> MresultT,Integer count , int resource, int textViewResourceId) {
super(context, resource, textViewResourceId);
this.Results = MResults;
this.ResultsT = MresultT;
this.counts = count;
generateDataset(false);
}
public void generateDataset(boolean clear) {
int y = 0;
if (clear) clear();
prepareSections(counts);
int sectionPosition = 0, listPosition = 0;
for (char i=0; i<counts; i++) {
final Items ci = Results.get(i);
Item section = new Item(Item.SECTION, ci.WTitleO);
section.sectionPosition = sectionPosition;
section.listPosition = listPosition++;
onSectionAdded(section, sectionPosition);
add(section);
for (int j=0;j<ci.count;j++) {
final ItemsT xx = ResultsT.get(y);
Item item = new Item(Item.ITEM, xx.WTitleT);
item.sectionPosition = sectionPosition;
item.listPosition = listPosition++;
y++;
add(item);
}
sectionPosition++;
}
}
protected void prepareSections(int sectionsNumber) { }
protected void onSectionAdded(Item section, int sectionPosition) { }
#Override public View getView(int position, View convertView, ViewGroup parent) {
TextView view = (TextView) super.getView(position, convertView, parent);
view.setTextColor(Color.DKGRAY);
view.setTag("" + position);
Item item = getItem(position);
if (item.type == Item.SECTION) {
view.setBackgroundColor(parent.getResources().getColor(COLORS[item.sectionPosition % COLORS.length]));
}
return view;
}
#Override public int getViewTypeCount() {
return 2;
}
#Override public int getItemViewType(int position) {
return getItem(position).type;
}
#Override
public boolean isItemViewTypePinned(int viewType) {
return viewType == Item.SECTION;
}
}
static class Items {
public int conO ;
public String WTitleO;
public int count;
}
static class ItemsT {
public String WTitleT;
}
static class Item {
public static final int ITEM = 0;
public static final int SECTION = 1;
public final int type;
public final String text;
public int sectionPosition;
public int listPosition;
public Item(int type, String text) {
this.type = type;
this.text = text;
}
#Override public String toString() {
return text;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
_context = this;
DB db = new DB(context);
db.CreateFile();
try {
db.CreateandOpenDataBase();
} catch (IOException e) {
e.printStackTrace();
}
sql = db.openDataBase();
test();
}
public void test(){
result = new ArrayList<Items>();
resultT = new ArrayList<ItemsT>();
try {
cursor = sql.rawQuery("SELECT (select count() as number from WebSite_BookPageDB22 as b where b.ParentID = a.ContentID) as count" +
" ,ContentID,Title" +
" FROM WebSite_BookPageDB22 as a" +
" WHERE ArticleID = '" + 61799 + "' AND ParentID = '" + 0 + "'order by PageID ASC LIMIT 5", null);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
Items ci = new Items();
ci.conO = cursor.getInt(cursor.getColumnIndex("ContentID"));
ci.WTitleO = cursor.getString(cursor.getColumnIndex("Title"));
ci.count = cursor.getInt(cursor.getColumnIndex("count"));
if(flag == false) {
count = cursor.getCount();
flag = true;
}
cursorA = sql.rawQuery("select Title from WebSite_BookPageDB22 where ParentID = '" + ci.conO +"' order by PageID ASC", null);
try {
if (cursorA != null && cursorA.moveToFirst()) {
do {
ItemsT cty = new ItemsT();
cty.WTitleT = cursorA.getString(cursorA.getColumnIndex("Title"));
resultT.add(cty);
} while (cursorA.moveToNext());
}
}catch (Exception e){
Log.i("xxx", "You have an error");
}finally {
if (cursorA != null) {
cursorA.close();
}
}
result.add(ci);
} while (cursor.moveToNext());
}
}
} catch (Exception e) {
} finally {
cursor.close();
}
SimpleAdapter adapt = new SimpleAdapter(_context, result, resultT, count, android.R.layout.simple_list_item_1, android.R.id.text1);
setListAdapter(adapt);
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Item item = (Item) getListView().getAdapter().getItem(position);
if (item != null) {
Toast.makeText(this, "Item " + position + ": " + item.text, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Item " + position, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onClick(View v) {
Toast.makeText(this, "Item: " + v.getTag() , Toast.LENGTH_SHORT).show();
}
}
And PinnedSectionListView.java :
public class PinnedSectionListView extends ListView {
//-- inner classes
/** List adapter to be implemented for being used with PinnedSectionListView adapter. */
public static interface PinnedSectionListAdapter extends ListAdapter {
/** This method shall return 'true' if views of given type has to be pinned. */
boolean isItemViewTypePinned(int viewType);
}
/** Wrapper class for pinned section view and its position in the list. */
static class PinnedSection {
public View view;
public int position;
public long id;
}
//-- class fields
// fields used for handling touch events
private final Rect mTouchRect = new Rect();
private final PointF mTouchPoint = new PointF();
private int mTouchSlop;
private View mTouchTarget;
private MotionEvent mDownEvent;
// fields used for drawing shadow under a pinned section
private GradientDrawable mShadowDrawable;
private int mSectionsDistanceY;
private int mShadowHeight;
/** Delegating listener, can be null. */
OnScrollListener mDelegateOnScrollListener;
/** Shadow for being recycled, can be null. */
PinnedSection mRecycleSection;
/** shadow instance with a pinned view, can be null. */
PinnedSection mPinnedSection;
/** Pinned view Y-translation. We use it to stick pinned view to the next section. */
int mTranslateY;
/** Scroll listener which does the magic */
private final OnScrollListener mOnScrollListener = new OnScrollListener() {
#Override public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if(firstVisibleItem+visibleItemCount == totalItemCount && totalItemCount!=0)
{
PinnedSectionListActivity xx = new PinnedSectionListActivity();
xx.flag = false;
xx.test();
/*Intent refresh = new Intent(PinnedSectionListActivity._context, PinnedSectionListActivity.class);
PinnedSectionListActivity._context.startActivity(refresh);*/
}
// get expected adapter or fail fast
ListAdapter adapter = getAdapter();
if (adapter == null || visibleItemCount == 0) return; // nothing to do
final boolean isFirstVisibleItemSection =
isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));
if (isFirstVisibleItemSection) {
View sectionView = getChildAt(0);
if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
destroyPinnedShadow();
} else { // section doesn't stick to the top, make sure we have a pinned shadow
ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);
}
} else { // section is not at the first visible position
int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
if (sectionPosition > -1) { // we have section position
ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
} else { // there is no section for the first visible item, destroy shadow
destroyPinnedShadow();
}
}
};
};
/** Default change observer. */
private final DataSetObserver mDataSetObserver = new DataSetObserver() {
#Override public void onChanged() {
recreatePinnedShadow();
};
#Override public void onInvalidated() {
recreatePinnedShadow();
}
};
//-- constructors
public PinnedSectionListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public PinnedSectionListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
private void initView() {
setOnScrollListener(mOnScrollListener);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
initShadow(true);
}
//-- public API methods
public void setShadowVisible(boolean visible) {
initShadow(visible);
if (mPinnedSection != null) {
View v = mPinnedSection.view;
invalidate(v.getLeft(), v.getTop(), v.getRight(), v.getBottom() + mShadowHeight);
}
}
//-- pinned section drawing methods
public void initShadow(boolean visible) {
if (visible) {
if (mShadowDrawable == null) {
mShadowDrawable = new GradientDrawable(Orientation.TOP_BOTTOM,
new int[] { Color.parseColor("#ffa0a0a0"), Color.parseColor("#50a0a0a0"), Color.parseColor("#00a0a0a0")});
mShadowHeight = (int) (8 * getResources().getDisplayMetrics().density);
}
} else {
if (mShadowDrawable != null) {
mShadowDrawable = null;
mShadowHeight = 0;
}
}
}
/** Create shadow wrapper with a pinned view for a view at given position */
void createPinnedShadow(int position) {
// try to recycle shadow
PinnedSection pinnedShadow = mRecycleSection;
mRecycleSection = null;
// create new shadow, if needed
if (pinnedShadow == null) pinnedShadow = new PinnedSection();
// request new view using recycled view, if such
View pinnedView = getAdapter().getView(position, pinnedShadow.view, PinnedSectionListView.this);
// read layout parameters
LayoutParams layoutParams = (LayoutParams) pinnedView.getLayoutParams();
if (layoutParams == null) {
layoutParams = (LayoutParams) generateDefaultLayoutParams();
pinnedView.setLayoutParams(layoutParams);
}
int heightMode = MeasureSpec.getMode(layoutParams.height);
int heightSize = MeasureSpec.getSize(layoutParams.height);
if (heightMode == MeasureSpec.UNSPECIFIED) heightMode = MeasureSpec.EXACTLY;
int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();
if (heightSize > maxHeight) heightSize = maxHeight;
// measure & layout
int ws = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);
int hs = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
pinnedView.measure(ws, hs);
pinnedView.layout(0, 0, pinnedView.getMeasuredWidth(), pinnedView.getMeasuredHeight());
mTranslateY = 0;
// initialize pinned shadow
pinnedShadow.view = pinnedView;
pinnedShadow.position = position;
pinnedShadow.id = getAdapter().getItemId(position);
// store pinned shadow
mPinnedSection = pinnedShadow;
}
/** Destroy shadow wrapper for currently pinned view */
void destroyPinnedShadow() {
if (mPinnedSection != null) {
// keep shadow for being recycled later
mRecycleSection = mPinnedSection;
mPinnedSection = null;
}
}
/** Makes sure we have an actual pinned shadow for given position. */
void ensureShadowForPosition(int sectionPosition, int firstVisibleItem, int visibleItemCount) {
if (visibleItemCount < 2) { // no need for creating shadow at all, we have a single visible item
destroyPinnedShadow();
return;
}
if (mPinnedSection != null
&& mPinnedSection.position != sectionPosition) { // invalidate shadow, if required
destroyPinnedShadow();
}
if (mPinnedSection == null) { // create shadow, if empty
createPinnedShadow(sectionPosition);
}
// align shadow according to next section position, if needed
int nextPosition = sectionPosition + 1;
if (nextPosition < getCount()) {
int nextSectionPosition = findFirstVisibleSectionPosition(nextPosition,
visibleItemCount - (nextPosition - firstVisibleItem));
if (nextSectionPosition > -1) {
View nextSectionView = getChildAt(nextSectionPosition - firstVisibleItem);
final int bottom = mPinnedSection.view.getBottom() + getPaddingTop();
mSectionsDistanceY = nextSectionView.getTop() - bottom;
if (mSectionsDistanceY < 0) {
// next section overlaps pinned shadow, move it up
mTranslateY = mSectionsDistanceY;
} else {
// next section does not overlap with pinned, stick to top
mTranslateY = 0;
}
} else {
// no other sections are visible, stick to top
mTranslateY = 0;
mSectionsDistanceY = Integer.MAX_VALUE;
}
}
}
int findFirstVisibleSectionPosition(int firstVisibleItem, int visibleItemCount) {
ListAdapter adapter = getAdapter();
int adapterDataCount = adapter.getCount();
if (getLastVisiblePosition() >= adapterDataCount) return -1; // dataset has changed, no candidate
if (firstVisibleItem+visibleItemCount >= adapterDataCount){//added to prevent index Outofbound (in case)
visibleItemCount = adapterDataCount-firstVisibleItem;
}
for (int childIndex = 0; childIndex < visibleItemCount; childIndex++) {
int position = firstVisibleItem + childIndex;
int viewType = adapter.getItemViewType(position);
if (isItemViewTypePinned(adapter, viewType)) return position;
}
return -1;
}
int findCurrentSectionPosition(int fromPosition) {
ListAdapter adapter = getAdapter();
if (fromPosition >= adapter.getCount()) return -1; // dataset has changed, no candidate
if (adapter instanceof SectionIndexer) {
// try fast way by asking section indexer
SectionIndexer indexer = (SectionIndexer) adapter;
int sectionPosition = indexer.getSectionForPosition(fromPosition);
int itemPosition = indexer.getPositionForSection(sectionPosition);
int typeView = adapter.getItemViewType(itemPosition);
if (isItemViewTypePinned(adapter, typeView)) {
return itemPosition;
} // else, no luck
}
// try slow way by looking through to the next section item above
for (int position=fromPosition; position>=0; position--) {
int viewType = adapter.getItemViewType(position);
if (isItemViewTypePinned(adapter, viewType)) return position;
}
return -1; // no candidate found
}
void recreatePinnedShadow() {
destroyPinnedShadow();
ListAdapter adapter = getAdapter();
if (adapter != null && adapter.getCount() > 0) {
int firstVisiblePosition = getFirstVisiblePosition();
int sectionPosition = findCurrentSectionPosition(firstVisiblePosition);
if (sectionPosition == -1) return; // no views to pin, exit
ensureShadowForPosition(sectionPosition,
firstVisiblePosition, getLastVisiblePosition() - firstVisiblePosition);
}
}
#Override
public void setOnScrollListener(OnScrollListener listener) {
if (listener == mOnScrollListener) {
super.setOnScrollListener(listener);
} else {
mDelegateOnScrollListener = listener;
}
}
#Override
public void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
post(new Runnable() {
#Override public void run() { // restore pinned view after configuration change
recreatePinnedShadow();
}
});
}
#Override
public void setAdapter(ListAdapter adapter) {
// assert adapter in debug mode
if (BuildConfig.DEBUG && adapter != null) {
if (!(adapter instanceof PinnedSectionListAdapter))
throw new IllegalArgumentException("Does your adapter implement PinnedSectionListAdapter?");
if (adapter.getViewTypeCount() < 2)
throw new IllegalArgumentException("Does your adapter handle at least two types" +
" of views in getViewTypeCount() method: items and sections?");
}
// unregister observer at old adapter and register on new one
ListAdapter oldAdapter = getAdapter();
if (oldAdapter != null) oldAdapter.unregisterDataSetObserver(mDataSetObserver);
if (adapter != null) adapter.registerDataSetObserver(mDataSetObserver);
// destroy pinned shadow, if new adapter is not same as old one
if (oldAdapter != adapter) destroyPinnedShadow();
super.setAdapter(adapter);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mPinnedSection != null) {
int parentWidth = r - l - getPaddingLeft() - getPaddingRight();
int shadowWidth = mPinnedSection.view.getWidth();
if (parentWidth != shadowWidth) {
recreatePinnedShadow();
}
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mPinnedSection != null) {
// prepare variables
int pLeft = getListPaddingLeft();
int pTop = getListPaddingTop();
View view = mPinnedSection.view;
// draw child
canvas.save();
int clipHeight = view.getHeight() +
(mShadowDrawable == null ? 0 : Math.min(mShadowHeight, mSectionsDistanceY));
canvas.clipRect(pLeft, pTop, pLeft + view.getWidth(), pTop + clipHeight);
canvas.translate(pLeft, pTop + mTranslateY);
drawChild(canvas, mPinnedSection.view, getDrawingTime());
if (mShadowDrawable != null && mSectionsDistanceY > 0) {
mShadowDrawable.setBounds(mPinnedSection.view.getLeft(),
mPinnedSection.view.getBottom(),
mPinnedSection.view.getRight(),
mPinnedSection.view.getBottom() + mShadowHeight);
mShadowDrawable.draw(canvas);
}
canvas.restore();
}
}
//-- touch handling methods
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
final int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN
&& mTouchTarget == null
&& mPinnedSection != null
&& isPinnedViewTouched(mPinnedSection.view, x, y)) { // create touch target
// user touched pinned view
mTouchTarget = mPinnedSection.view;
mTouchPoint.x = x;
mTouchPoint.y = y;
// copy down event for eventually be used later
mDownEvent = MotionEvent.obtain(ev);
}
if (mTouchTarget != null) {
if (isPinnedViewTouched(mTouchTarget, x, y)) { // forward event to pinned view
mTouchTarget.dispatchTouchEvent(ev);
}
if (action == MotionEvent.ACTION_UP) { // perform onClick on pinned view
super.dispatchTouchEvent(ev);
performPinnedItemClick();
clearTouchTarget();
} else if (action == MotionEvent.ACTION_CANCEL) { // cancel
clearTouchTarget();
} else if (action == MotionEvent.ACTION_MOVE) {
if (Math.abs(y - mTouchPoint.y) > mTouchSlop) {
// cancel sequence on touch target
MotionEvent event = MotionEvent.obtain(ev);
event.setAction(MotionEvent.ACTION_CANCEL);
mTouchTarget.dispatchTouchEvent(event);
event.recycle();
// provide correct sequence to super class for further handling
super.dispatchTouchEvent(mDownEvent);
super.dispatchTouchEvent(ev);
clearTouchTarget();
}
}
return true;
}
// call super if this was not our pinned view
return super.dispatchTouchEvent(ev);
}
private boolean isPinnedViewTouched(View view, float x, float y) {
view.getHitRect(mTouchRect);
// by taping top or bottom padding, the list performs on click on a border item.
// we don't add top padding here to keep behavior consistent.
mTouchRect.top += mTranslateY;
mTouchRect.bottom += mTranslateY + getPaddingTop();
mTouchRect.left += getPaddingLeft();
mTouchRect.right -= getPaddingRight();
return mTouchRect.contains((int)x, (int)y);
}
private void clearTouchTarget() {
mTouchTarget = null;
if (mDownEvent != null) {
mDownEvent.recycle();
mDownEvent = null;
}
}
private boolean performPinnedItemClick() {
if (mPinnedSection == null) return false;
OnItemClickListener listener = getOnItemClickListener();
if (listener != null && getAdapter().isEnabled(mPinnedSection.position)) {
View view = mPinnedSection.view;
playSoundEffect(SoundEffectConstants.CLICK);
if (view != null) {
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
}
listener.onItemClick(this, view, mPinnedSection.position, mPinnedSection.id);
return true;
}
return false;
}
public static boolean isItemViewTypePinned(ListAdapter adapter, int viewType) {
if (adapter instanceof HeaderViewListAdapter) {
adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
}
return ((PinnedSectionListAdapter) adapter).isItemViewTypePinned(viewType);
}
}
For second time get me error here :
SimpleAdapter adapt = new SimpleAdapter(_context, result, resultT, count, android.R.layout.simple_list_item_1, android.R.id.text1);
setListAdapter(adapt);
Your not using PinnedSectionListActivity current reference properly,If you wan to access your current public function inside any other class then you have to pass your current activity reference :
Declaration
private Context context;
Get current activity reference
public PinnedSectionListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView();
}
public PinnedSectionListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
initView();
}
Use current activity reference in onScroll() :
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if(firstVisibleItem+visibleItemCount == totalItemCount && totalItemCount!=0)
{
if(context!=null){
((PinnedSectionListActivity)context).cleans();
}
}else {
// get expected adapter or fail fast
ListAdapter adapter = getAdapter();
if (adapter == null || visibleItemCount == 0) return; // nothing to do
final boolean isFirstVisibleItemSection = isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));
if (isFirstVisibleItemSection) {
View sectionView = getChildAt(0);
if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
destroyPinnedShadow();
} else { // section doesn't stick to the top, make sure we have a pinned shadow
ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);
}
} else { // section is not at the first visible position
int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
if (sectionPosition > -1) { // we have section position
ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
} else { // there is no section for the first visible item, destroy shadow
destroyPinnedShadow();
}
}
}
};
PinnedSectionListView full activity code :
public class PinnedSectionListView extends ListView {
//-- inner classes
/** List adapter to be implemented for being used with PinnedSectionListView adapter. */
public static interface PinnedSectionListAdapter extends ListAdapter {
/** This method shall return 'true' if views of given type has to be pinned. */
boolean isItemViewTypePinned(int viewType);
}
/** Wrapper class for pinned section view and its position in the list. */
static class PinnedSection {
public View view;
public int position;
public long id;
}
//-- class fields
// fields used for handling touch events
private final Rect mTouchRect = new Rect();
private final PointF mTouchPoint = new PointF();
private int mTouchSlop;
private View mTouchTarget;
private MotionEvent mDownEvent;
// fields used for drawing shadow under a pinned section
private GradientDrawable mShadowDrawable;
private int mSectionsDistanceY;
private int mShadowHeight;
/** Delegating listener, can be null. */
OnScrollListener mDelegateOnScrollListener;
/** Shadow for being recycled, can be null. */
PinnedSection mRecycleSection;
/** shadow instance with a pinned view, can be null. */
PinnedSection mPinnedSection;
/** Pinned view Y-translation. We use it to stick pinned view to the next section. */
int mTranslateY;
private Context context;
/** Scroll listener which does the magic */
private final OnScrollListener mOnScrollListener = new OnScrollListener() {
#Override public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if(firstVisibleItem+visibleItemCount == totalItemCount && totalItemCount!=0)
{
if(context!=null){
((PinnedSectionListActivity)context).cleans();
}
}else {
// get expected adapter or fail fast
ListAdapter adapter = getAdapter();
if (adapter == null || visibleItemCount == 0) return; // nothing to do
final boolean isFirstVisibleItemSection = isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));
if (isFirstVisibleItemSection) {
View sectionView = getChildAt(0);
if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
destroyPinnedShadow();
} else { // section doesn't stick to the top, make sure we have a pinned shadow
ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);
}
} else { // section is not at the first visible position
int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
if (sectionPosition > -1) { // we have section position
ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
} else { // there is no section for the first visible item, destroy shadow
destroyPinnedShadow();
}
}
}
};
};
/** Default change observer. */
private final DataSetObserver mDataSetObserver = new DataSetObserver() {
#Override public void onChanged() {
recreatePinnedShadow();
};
#Override public void onInvalidated() {
recreatePinnedShadow();
}
};
//-- constructors
public PinnedSectionListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView();
}
public PinnedSectionListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
initView();
}
private void initView() {
setOnScrollListener(mOnScrollListener);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
initShadow(true);
}
//-- public API methods
public void setShadowVisible(boolean visible) {
initShadow(visible);
if (mPinnedSection != null) {
View v = mPinnedSection.view;
invalidate(v.getLeft(), v.getTop(), v.getRight(), v.getBottom() + mShadowHeight);
}
}
//-- pinned section drawing methods
public void initShadow(boolean visible) {
if (visible) {
if (mShadowDrawable == null) {
mShadowDrawable = new GradientDrawable(Orientation.TOP_BOTTOM,
new int[] { Color.parseColor("#ffa0a0a0"), Color.parseColor("#50a0a0a0"), Color.parseColor("#00a0a0a0")});
mShadowHeight = (int) (8 * getResources().getDisplayMetrics().density);
}
} else {
if (mShadowDrawable != null) {
mShadowDrawable = null;
mShadowHeight = 0;
}
}
}
/** Create shadow wrapper with a pinned view for a view at given position */
void createPinnedShadow(int position) {
// try to recycle shadow
PinnedSection pinnedShadow = mRecycleSection;
mRecycleSection = null;
// create new shadow, if needed
if (pinnedShadow == null) pinnedShadow = new PinnedSection();
// request new view using recycled view, if such
View pinnedView = getAdapter().getView(position, pinnedShadow.view, PinnedSectionListView.this);
// read layout parameters
LayoutParams layoutParams = (LayoutParams) pinnedView.getLayoutParams();
if (layoutParams == null) {
layoutParams = (LayoutParams) generateDefaultLayoutParams();
pinnedView.setLayoutParams(layoutParams);
}
int heightMode = MeasureSpec.getMode(layoutParams.height);
int heightSize = MeasureSpec.getSize(layoutParams.height);
if (heightMode == MeasureSpec.UNSPECIFIED) heightMode = MeasureSpec.EXACTLY;
int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();
if (heightSize > maxHeight) heightSize = maxHeight;
// measure & layout
int ws = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);
int hs = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
pinnedView.measure(ws, hs);
pinnedView.layout(0, 0, pinnedView.getMeasuredWidth(), pinnedView.getMeasuredHeight());
mTranslateY = 0;
// initialize pinned shadow
pinnedShadow.view = pinnedView;
pinnedShadow.position = position;
pinnedShadow.id = getAdapter().getItemId(position);
// store pinned shadow
mPinnedSection = pinnedShadow;
}
/** Destroy shadow wrapper for currently pinned view */
void destroyPinnedShadow() {
if (mPinnedSection != null) {
// keep shadow for being recycled later
mRecycleSection = mPinnedSection;
mPinnedSection = null;
}
}
/** Makes sure we have an actual pinned shadow for given position. */
void ensureShadowForPosition(int sectionPosition, int firstVisibleItem, int visibleItemCount) {
if (visibleItemCount < 2) { // no need for creating shadow at all, we have a single visible item
destroyPinnedShadow();
return;
}
if (mPinnedSection != null
&& mPinnedSection.position != sectionPosition) { // invalidate shadow, if required
destroyPinnedShadow();
}
if (mPinnedSection == null) { // create shadow, if empty
createPinnedShadow(sectionPosition);
}
// align shadow according to next section position, if needed
int nextPosition = sectionPosition + 1;
if (nextPosition < getCount()) {
int nextSectionPosition = findFirstVisibleSectionPosition(nextPosition,
visibleItemCount - (nextPosition - firstVisibleItem));
if (nextSectionPosition > -1) {
View nextSectionView = getChildAt(nextSectionPosition - firstVisibleItem);
final int bottom = mPinnedSection.view.getBottom() + getPaddingTop();
mSectionsDistanceY = nextSectionView.getTop() - bottom;
if (mSectionsDistanceY < 0) {
// next section overlaps pinned shadow, move it up
mTranslateY = mSectionsDistanceY;
} else {
// next section does not overlap with pinned, stick to top
mTranslateY = 0;
}
} else {
// no other sections are visible, stick to top
mTranslateY = 0;
mSectionsDistanceY = Integer.MAX_VALUE;
}
}
}
int findFirstVisibleSectionPosition(int firstVisibleItem, int visibleItemCount) {
ListAdapter adapter = getAdapter();
int adapterDataCount = adapter.getCount();
if (getLastVisiblePosition() >= adapterDataCount) return -1; // dataset has changed, no candidate
if (firstVisibleItem+visibleItemCount >= adapterDataCount){//added to prevent index Outofbound (in case)
visibleItemCount = adapterDataCount-firstVisibleItem;
}
for (int childIndex = 0; childIndex < visibleItemCount; childIndex++) {
int position = firstVisibleItem + childIndex;
int viewType = adapter.getItemViewType(position);
if (isItemViewTypePinned(adapter, viewType)) return position;
}
return -1;
}
int findCurrentSectionPosition(int fromPosition) {
ListAdapter adapter = getAdapter();
if (fromPosition >= adapter.getCount()) return -1; // dataset has changed, no candidate
if (adapter instanceof SectionIndexer) {
// try fast way by asking section indexer
SectionIndexer indexer = (SectionIndexer) adapter;
int sectionPosition = indexer.getSectionForPosition(fromPosition);
int itemPosition = indexer.getPositionForSection(sectionPosition);
int typeView = adapter.getItemViewType(itemPosition);
if (isItemViewTypePinned(adapter, typeView)) {
return itemPosition;
} // else, no luck
}
// try slow way by looking through to the next section item above
for (int position=fromPosition; position>=0; position--) {
int viewType = adapter.getItemViewType(position);
if (isItemViewTypePinned(adapter, viewType)) return position;
}
return -1; // no candidate found
}
void recreatePinnedShadow() {
destroyPinnedShadow();
ListAdapter adapter = getAdapter();
if (adapter != null && adapter.getCount() > 0) {
int firstVisiblePosition = getFirstVisiblePosition();
int sectionPosition = findCurrentSectionPosition(firstVisiblePosition);
if (sectionPosition == -1) return; // no views to pin, exit
ensureShadowForPosition(sectionPosition,
firstVisiblePosition, getLastVisiblePosition() - firstVisiblePosition);
}
}
#Override
public void setOnScrollListener(OnScrollListener listener) {
if (listener == mOnScrollListener) {
super.setOnScrollListener(listener);
} else {
mDelegateOnScrollListener = listener;
}
}
#Override
public void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
post(new Runnable() {
#Override public void run() { // restore pinned view after configuration change
recreatePinnedShadow();
}
});
}
#Override
public void setAdapter(ListAdapter adapter) {
// assert adapter in debug mode
if (BuildConfig.DEBUG && adapter != null) {
if (!(adapter instanceof PinnedSectionListAdapter))
throw new IllegalArgumentException("Does your adapter implement PinnedSectionListAdapter?");
if (adapter.getViewTypeCount() < 2)
throw new IllegalArgumentException("Does your adapter handle at least two types" +
" of views in getViewTypeCount() method: items and sections?");
}
// unregister observer at old adapter and register on new one
ListAdapter oldAdapter = getAdapter();
if (oldAdapter != null) oldAdapter.unregisterDataSetObserver(mDataSetObserver);
if (adapter != null) adapter.registerDataSetObserver(mDataSetObserver);
// destroy pinned shadow, if new adapter is not same as old one
if (oldAdapter != adapter) destroyPinnedShadow();
super.setAdapter(adapter);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mPinnedSection != null) {
int parentWidth = r - l - getPaddingLeft() - getPaddingRight();
int shadowWidth = mPinnedSection.view.getWidth();
if (parentWidth != shadowWidth) {
recreatePinnedShadow();
}
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mPinnedSection != null) {
// prepare variables
int pLeft = getListPaddingLeft();
int pTop = getListPaddingTop();
View view = mPinnedSection.view;
// draw child
canvas.save();
int clipHeight = view.getHeight() +
(mShadowDrawable == null ? 0 : Math.min(mShadowHeight, mSectionsDistanceY));
canvas.clipRect(pLeft, pTop, pLeft + view.getWidth(), pTop + clipHeight);
canvas.translate(pLeft, pTop + mTranslateY);
drawChild(canvas, mPinnedSection.view, getDrawingTime());
if (mShadowDrawable != null && mSectionsDistanceY > 0) {
mShadowDrawable.setBounds(mPinnedSection.view.getLeft(),
mPinnedSection.view.getBottom(),
mPinnedSection.view.getRight(),
mPinnedSection.view.getBottom() + mShadowHeight);
mShadowDrawable.draw(canvas);
}
canvas.restore();
}
}
//-- touch handling methods
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
final int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN
&& mTouchTarget == null
&& mPinnedSection != null
&& isPinnedViewTouched(mPinnedSection.view, x, y)) { // create touch target
// user touched pinned view
mTouchTarget = mPinnedSection.view;
mTouchPoint.x = x;
mTouchPoint.y = y;
// copy down event for eventually be used later
mDownEvent = MotionEvent.obtain(ev);
}
if (mTouchTarget != null) {
if (isPinnedViewTouched(mTouchTarget, x, y)) { // forward event to pinned view
mTouchTarget.dispatchTouchEvent(ev);
}
if (action == MotionEvent.ACTION_UP) { // perform onClick on pinned view
super.dispatchTouchEvent(ev);
performPinnedItemClick();
clearTouchTarget();
} else if (action == MotionEvent.ACTION_CANCEL) { // cancel
clearTouchTarget();
} else if (action == MotionEvent.ACTION_MOVE) {
if (Math.abs(y - mTouchPoint.y) > mTouchSlop) {
// cancel sequence on touch target
MotionEvent event = MotionEvent.obtain(ev);
event.setAction(MotionEvent.ACTION_CANCEL);
mTouchTarget.dispatchTouchEvent(event);
event.recycle();
// provide correct sequence to super class for further handling
super.dispatchTouchEvent(mDownEvent);
super.dispatchTouchEvent(ev);
clearTouchTarget();
}
}
return true;
}
// call super if this was not our pinned view
return super.dispatchTouchEvent(ev);
}
private boolean isPinnedViewTouched(View view, float x, float y) {
view.getHitRect(mTouchRect);
// by taping top or bottom padding, the list performs on click on a border item.
// we don't add top padding here to keep behavior consistent.
mTouchRect.top += mTranslateY;
mTouchRect.bottom += mTranslateY + getPaddingTop();
mTouchRect.left += getPaddingLeft();
mTouchRect.right -= getPaddingRight();
return mTouchRect.contains((int)x, (int)y);
}
private void clearTouchTarget() {
mTouchTarget = null;
if (mDownEvent != null) {
mDownEvent.recycle();
mDownEvent = null;
}
}
private boolean performPinnedItemClick() {
if (mPinnedSection == null) return false;
OnItemClickListener listener = getOnItemClickListener();
if (listener != null && getAdapter().isEnabled(mPinnedSection.position)) {
View view = mPinnedSection.view;
playSoundEffect(SoundEffectConstants.CLICK);
if (view != null) {
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
}
listener.onItemClick(this, view, mPinnedSection.position, mPinnedSection.id);
return true;
}
return false;
}
public static boolean isItemViewTypePinned(ListAdapter adapter, int viewType) {
if (adapter instanceof HeaderViewListAdapter) {
adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
}
return ((PinnedSectionListAdapter) adapter).isItemViewTypePinned(viewType);
}
}

RecyclerView scrolled UP/DOWN listener

How do we know if user scrolled down or up in RecyclerView ?
I tried with RecyclerView#OnScrollListener , it gives the amount of vertical scroll and the scroll state. How do we get the last scroll position when started to dragging and scrolled position when scroll state idle.
The accepted answer works fine, but #MaciejPigulski gave more clear and neat solution in the comment below. I just putting it as an answer here. Here's my working code.
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
// Scrolling up
} else {
// Scrolling down
}
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
// Do something
} else if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
// Do something
} else {
// Do something
}
}
});
Try this way:
private static int firstVisibleInListview;
firstVisibleInListview = yourLayoutManager.findFirstVisibleItemPosition();
In your scroll listener:
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
super.onScrolled(recyclerView, dx, dy);
int currentFirstVisible = yourLayoutManager.findFirstVisibleItemPosition();
if(currentFirstVisible > firstVisibleInListview)
Log.i("RecyclerView scrolled: ", "scroll up!");
else
Log.i("RecyclerView scrolled: ", "scroll down!");
firstVisibleInListview = currentFirstVisible;
}
I wanted to hide a layout if the recyclerview is scrolled down and then make it visible if the recyclerview is scrolled up. I did some thinking and came up with this logic.
Variable y is a global static int. Do not forget to declare y as static int y;
I hope it helps someone :)
mRecyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(lLayout) {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
// super.onScrolled(recyclerView, dx, dy);
y=dy;
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if(mRecyclerView.SCROLL_STATE_DRAGGING==newState){
//fragProductLl.setVisibility(View.GONE);
}
if(mRecyclerView.SCROLL_STATE_IDLE==newState){
// fragProductLl.setVisibility(View.VISIBLE);
if(y<=0){
fragProductLl.setVisibility(View.VISIBLE);
}
else{
y=0;
fragProductLl.setVisibility(View.GONE);
}
}
}
});
Another simple solution that can detect scroll direction with the help of your adapter:
class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyViewHolder> {
int lastItemPosition = -1;
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
if (position > lastItemPosition) {
// Scrolled Down
}
else {
// Scrolled Up
}
lastItemPosition = position;
}
}
^ Helpful when doing item animations upon scrolling.
PS: This will tell you directions discontinuously until further onBindViewHolder calls.
this code work for me
private var nearmeListScrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (recyclerView.scrollState == RecyclerView.SCROLL_STATE_SETTLING)
if (dy > 0) {
layout_vendros_list_header.hide()
layout_vendros_list_header.animate().translationY(1f)
} else if (dy < 0) {
layout_vendros_list_header.show()
layout_vendros_list_header.animate().translationY(0f)
}
}
}
```
There is my implementation of CustomRecyclerView with all type of scroll listeners
public class CustomRecyclerView extends RecyclerView
{
private boolean mIsScrolling;
private boolean mIsTouching;
private OnScrollListener mOnScrollListener;
private Runnable mScrollingRunnable;
private int y = 0;
public abstract static class OnScrollListener
{
void onScrollChanged(CustomRecyclerView RvView, int x, int y, int oldX, int oldY)
{
//if you need just override this method
}
void onEndScroll(CustomRecyclerView RvView)
{
//if you need just override this method
}
protected abstract void onGoUp();
protected abstract void onGoDown();
}
public CustomRecyclerView(final Context context)
{
super(context);
}
public CustomRecyclerView(final Context context, #Nullable final AttributeSet attrs)
{
super(context, attrs);
}
public CustomRecyclerView(final Context context, #Nullable final AttributeSet attrs, final int defStyle)
{
super(context, attrs, defStyle);
}
#Override
public boolean dispatchTouchEvent(MotionEvent iEv)
{
if (isEnabled())
{
processEvent(iEv);
super.dispatchTouchEvent(iEv);
return true; //to keep receive event that follow down event
}
return super.dispatchTouchEvent(iEv);
}
private void processEvent(final MotionEvent iEv)
{
switch (iEv.getAction())
{
case MotionEvent.ACTION_DOWN:
y = (int) iEv.getY();
break;
case MotionEvent.ACTION_UP:
y = (int) iEv.getY();
if (mIsTouching && !mIsScrolling && mOnScrollListener != null)
{
mOnScrollListener.onEndScroll(this);
}
mIsTouching = false;
break;
case MotionEvent.ACTION_MOVE:
mIsTouching = true;
mIsScrolling = true;
int newY = (int) iEv.getY();
int difY = y - newY;
int MAX_VALUE = 200;
int MIN_VALUE = -200;
if (difY > MAX_VALUE)
{
if (mOnScrollListener != null)
{
mOnScrollListener.onGoDown();
}
y = newY;
}
else if (difY < MIN_VALUE)
{
if (mOnScrollListener != null)
{
mOnScrollListener.onGoUp();
}
y = newY;
}
break;
}
}
#Override
protected void onScrollChanged(int iX, int iY, int iOldX, int iOldY)
{
super.onScrollChanged(iX, iY, iOldX, iOldY);
if (Math.abs(iOldX - iX) > 0)
{
if (mScrollingRunnable != null)
{
removeCallbacks(mScrollingRunnable);
}
mScrollingRunnable = () ->
{
if (mIsScrolling && !mIsTouching && mOnScrollListener != null)
{
mOnScrollListener.onEndScroll(CustomRecyclerView.this);
}
mIsScrolling = false;
mScrollingRunnable = null;
};
postDelayed(mScrollingRunnable, 200);
}
if (mOnScrollListener != null)
{
mOnScrollListener.onScrollChanged(this, iX, iY, iOldX, iOldY);
}
}
public void scrollToView(final View iV)
{
// Get deepChild Offset
Point childOffset = new Point();
getDeepChildOffset(CustomRecyclerView.this, iV.getParent(), iV, childOffset);
// Scroll to child.
CustomRecyclerView.this.scrollToY(childOffset.y);
}
private void getDeepChildOffset(final ViewGroup mainParent, final ViewParent parent, final View child, final Point accumulatedOffset)
{
ViewGroup parentGroup = (ViewGroup) parent;
accumulatedOffset.x += child.getLeft();
accumulatedOffset.y += child.getTop();
if (parentGroup.equals(mainParent))
{
return;
}
getDeepChildOffset(mainParent, parentGroup.getParent(), parentGroup, accumulatedOffset);
}
public void scrollToY(final int iY)
{
CustomRecyclerView.this.postDelayed(() ->
{
int x = 0;
int y = iY;
ObjectAnimator xTranslate = ObjectAnimator.ofInt(CustomRecyclerView.this, "scrollX", x);
ObjectAnimator yTranslate = ObjectAnimator.ofInt(CustomRecyclerView.this, "scrollY", y);
AnimatorSet animators = new AnimatorSet();
animators.setDuration(500L);
animators.playTogether(xTranslate, yTranslate);
animators.addListener(new Animator.AnimatorListener()
{
#Override
public void onAnimationStart(Animator arg0)
{
// noting
}
#Override
public void onAnimationRepeat(Animator arg0)
{
// noting
}
#Override
public void onAnimationEnd(Animator arg0)
{
// noting
}
#Override
public void onAnimationCancel(Animator arg0)
{
// noting
}
});
animators.start();
}, 300);
}
public void scrollToTop()
{
scrollToY(0);
}
public void setOnRvScrollListener(OnScrollListener mOnEndScrollListener)
{
this.mOnScrollListener = mOnEndScrollListener;
}
}
And there is sample of usage
private void setRecyclerViewSettings()
{
mRv.setLayoutManager(new LinearLayoutManager(getActivity()));
mRv.setOnRvScrollListener(mScrollListener);
}
private CustomRecyclerView.OnScrollListener mScrollListener = new CustomRecyclerView.OnScrollListener()
{
#Override
protected void onGoUp()
{
AppUtils.hideKeyboard(getContext(), mRvDynamicFormQuestions.getFocusedChild());
}
#Override
protected void onGoDown()
{
AppUtils.hideKeyboard(getContext(), mRvDynamicFormQuestions.getFocusedChild());
}
};

Categories