I have two list views. I need to scroll one list view automatically
when the other list view is scrolled. both list views should have this ability
i implemented the onScrollListner in both listviews
for listview 1
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (l1.getChildAt(0) != null) {
Rect r = new Rect();
l1.getChildVisibleRect(l1.getChildAt(0), r, null);
l2.setSelectionFromTop(l1.getFirstVisiblePosition(), r.top);
}
}
for listview 2
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (l2.getChildAt(0) != null) {
Rect r = new Rect();
l2.getChildVisibleRect(l2.getChildAt(0), r, null);
l1.setSelectionFromTop(l2.getFirstVisiblePosition(), r.top);
}
}
I have 2 problems regarding this
1 - the lists do not scroll smoothly. (not like normall listview)
2 - I can only scroll both listviews using one list view.(when i scroll using l2
both get scrolled. but it does not work when i scroll using l1. both stay fixed)
Thanks in advance
It happened becoz you put listview inside listview :)
You can achieve you problem using this.
suppose L1 is inside L2 then
L1.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View arg0, MotionEvent arg1) {
if(arg1.getAction() == MotionEvent.ACTION_DOWN || arg1.getAction() == MotionEvent.ACTION_MOVE)
{
L2.requestDisallowInterceptTouchEvent(true);
}
return false;
}
});
It's your application so you could do whatever you want but I am confused with what you want to achieve. The sole idea of having two list views is to have separate representation of two different categories of data so that they can be accessed(scrolled or selected) separately.
If you have requirement of representing two categories of data that need to be displayed side by side as a list then you can have a list row with two columns.
something like this will work:
You can populate these two rows with different kind of data and this can be scrolled together.
I found out a different way to get the same result. I remove my two list views and replaced it
with a grid view with two columns. this way i can scroll both without implementing onScrollListner and i can even use the custom adapter i used for the two listviews.
Related
TL;DR - ViewPager as ListView Header creating some issues.
my activity has a ListView that presents several types of data..
I have a HeaderView, a sticky view, and the rest of the data is a "normal" list item.
I am using this Library -> https://github.com/LarsWerkman/QuickReturnListView
for my list view.
In my HeaderView i have a view pager for 2 profile images of my users.
2 problems :
scrolling the pager right to the next image doesn't open the image to the full screen width..
(but if scrolling down in the list view, scrolling back up to the top of the screen "redraw" the header view and then if you scroll the pager again it's fixed!)
sometimes the images do not load to my view pager.
you can see that bug in those images, first image is of the state from trying to scroll the pager to the right, and the second image is the bug
image one
image two
anyone encountered such a problem before?
I've read in some places that using a view pager inside a list view is not exactly optimal, is there a different way to achieve my goal?
I had that same problem,
try removing that third party library of the list view,
back in the day i was using that same one and it's really buggy.
use a normal listview instead.
A few months ago i did a similar trick as you want to do. I tried almost all libraries in web about this quick return and all of them has bugs and not suitable for me. Also not suitable for a header which contains a viewpager in.
Later i implemented my own scroll listener. This is not exactly a quick return header pattern but you can add animations in if you have time.
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#SuppressLint("NewApi")
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int scrollOffset = 0;
float transitionY;
if (firstVisibleItem > 0) {
scrollOffset += headerHeight;
if (firstVisibleItem > 1) {
scrollOffset += (firstVisibleItem - 1) * cellHeight;
}
}
if (listView.getChildCount() > 0) {
scrollOffset += -listView.getChildAt(0).getTop();
scrollOffset = -scrollOffset;
}
float scrollDelta = scrollOffset - prevOffset;
float nextY = mQuickReturnView.getY() + scrollDelta;
if (nextY < minRawY) {
transitionY = minRawY;
}
else if (nextY > qReturnDelta) {
transitionY = qReturnDelta;
}
else {
transitionY = nextY;
}
mQuickReturnView.setY(transitionY);
prevOffset = scrollOffset;
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
});
And i used a view pager and a pageradapter in header. In my solution you have to use fixed size for listview items and declare it as CellHeight and a fixed size for header. It's QuickReturnHeight
It's a bit hard to implement this pattern with ListView + Header + ViewPager.
I hope this'll help you.
I want to find out the position or ids related to a ListView's items: only those ones which are completely visible on the screen.
Using listview.getFirstVisibleposition and listview.getLastVisibleposition takes partial list items into account.
I followed a little bit similar approach as suggested by Rich, to suit my requirement which was to fetch completely visible items on screen when List View is scrolled every time.
This is what i did
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//Loop to get tids of all completely visible List View's item scrolled on screen
for (int listItemIndex = 0; listItemIndex <= getListView().getLastVisiblePosition() - getListView().getFirstVisiblePosition(); listItemIndex++) {
View listItem = getListView().getChildAt(listItemIndex);
TextView tvNewPostLabel = (TextView) listItem.findViewById(R.id.tvNewPostLabel);
if (tvNewPostLabel != null && tvNewPostLabel.getVisibility() == View.VISIBLE) {
int listTid = (int) tvNewPostLabel.getTag();
if (listItem.getBottom() < getListView().getHeight()) {//If List View's item is not partially visible
listItemTids.add(listTid);
}
}
}
}
I have not tried this, but here are the pieces of the framework that I believe will get you to what you're looking for (at least this is what I'd try first)
As you've stated, you should get the last visible position from the list view using ListView.getLastVisiblePosition()
You can then access the View representing this position using ListView.getChildAt(position)
You now have a reference to the view, which you can call a combination of View.getLocationOnScreen(location) and View.getHeight()
Also call View.getLocationOnScreen(location) and View.getHeight() on the ListView. y + height of the View should be less than or equal to y + height of the ListView if it is fully visible.
I'm having an issue with the SwipeRefreshLayout. I have a list view within the layout and every time I scroll upward in the list view the swipe to refresh layout is tiggered and you can never scroll back to the top of the layout. Anyone have any ideas?
I found an answer to my question. I am manually enabling the swipeRefreshLayout when the first child in the listview is at the top of the list.
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView listView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int topRowVerticalPosition = (listView == null || listView.getChildCount() == 0) ?
0 : expandableListview.getChildAt(0).getTop();
refresh.setEnabled((topRowVerticalPosition >= 0));
}
});
https://developer.android.com/reference/android/support/v4/widget/SwipeRefreshLayout.html
According to Android docs at the above link,
This layout should be made the parent of the view that will be refreshed as a result of the gesture and can only support one direct child.
If the ListView is the 1st direct child of the SwipeRefreshLayout, refresh won't be triggered before scrolling back to the top of the ListView. You don't need to do anything in onScroll().
Consider I have two Horizontal ListView as shown below:
list1
list2
I want to know if it is possible to make the second list (list2) view scroll horizontally simultaneously along with the scroll of first list (list1).....
That is when I scroll list1 (horizontal), even list2 must scroll by the same offset...
Is this possible, if yes, please do help...
![link to image]: https://picasaweb.google.com/109389839906668906213/January132012#5697019272538269218
You can do that - just create such layout and use scroll events:
list1.setOnScrollListener(new OnScrollListener() {
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
}
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
list2.setSelectionFromTop(firstVisibleItem, list1.getChildAt(0).getTop());
}
});
Some explanation:
Better use list.setSelectionFromTop() than list.scrollTo() - because first visible item of first list can be shown partially.
list1.getChildAt(0).getTop() - is construction for getting value of X coordinate of first visible item.
As There is no HorizontalListView in Android, you must having another adapter view, anyway implement following:
list1.setOnItemSelectedListener(new OnItemSelectedListener()
{
public void onItemSelected(AdapterView adapterView adapterView, View view, int position, long id){
list2.setSelection(position);
}
});
I'm creating a list of pictures using a ListView and the photos are of a size that would fit 2 to 3 photos on the screen.
The problem that I'm having is that I would like to when the user stops scrolling that the first item of the visible list would snap to the top of screen, for example, if the scroll ends and small part of the first picture displayed, we scroll the list down so the picture is always fully displayed, if mostly of the picture is displayed, we scroll the list up so the next picture is fully visible.
Is there a way to achieve this in android with the listview?
I've found a way to do this just listening to scroll and change the position when the scroll ended by implementing ListView.OnScrollListener
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_IDLE:
if (scrolling){
// get first visible item
View itemView = view.getChildAt(0);
int top = Math.abs(itemView.getTop()); // top is a negative value
int bottom = Math.abs(itemView.getBottom());
if (top >= bottom){
((ListView)view).setSelectionFromTop(view.getFirstVisiblePosition()+1, 0);
} else {
((ListView)view).setSelectionFromTop(view.getFirstVisiblePosition(), 0);
}
}
scrolling = false;
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
case OnScrollListener.SCROLL_STATE_FLING:
Log.i("TEST", "SCROLLING");
scrolling = true;
break;
}
}
The change is not so smooth but it works.
Utilizing a couple ideas from #nininho's solution, I got my listview to snap to the item with a smooth scroll instead of abruptly going to it. One caveat is that I've only tested this solution on a Moto X in a basic ListView with text, but it works very well on the device. Nevertheless, I'm confident about this solution, and encourage you to provide feedback.
listview.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
if (scrollState == SCROLL_STATE_IDLE) {
View itemView = view.getChildAt(0);
int top = Math.abs(itemView.getTop());
int bottom = Math.abs(itemView.getBottom());
int scrollBy = top >= bottom ? bottom : -top;
if (scrollBy == 0) {
return;
}
smoothScrollDeferred(scrollBy, (ListView)view);
}
}
private void smoothScrollDeferred(final int scrollByF,
final ListView viewF) {
final Handler h = new Handler();
h.post(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
viewF.smoothScrollBy(scrollByF, 200);
}
});
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
});
The reason I defer the smooth scrolling is because in my testing, directly calling the smoothScrollBy method in the state changed callback had problems actually scrolling. Also, I don't foresee a fully-tested, robust solution holding very much state, and in my solution below, I hold no state at all. This solution is not yet in the Google Play Store, but should serve as a good starting point.
Using #nininho 's solution,
In the onScrollStateChanged when the state changes to SCROLL_STATE_IDLE, remember the position to snap and raise a flag:
snapTo = view.getFirstVisiblePosition();
shouldSnap = true;
Then, override the computeScroll() method:
#Override
public void computeScroll() {
super.computeScroll();
if(shouldSnap){
this.smoothScrollToPositionFromTop(snapTo, 0);
shouldSnap = false;
}
}
You can do a much more smooth scrolling if you use RecyclerView. The OnScrollListener is way better.
I have made an example here: https://github.com/plattysoft/SnappingList
Well.. I know 10 years have past since this question was asked, but now we can use LinearSnapHelper:
new LinearSnapHelper().attachToRecyclerView(recyclerView);
Source:
https://proandroiddev.com/android-recyclerview-snaphelper-19eaa9598da6
Apart from trying the code above one thing you should make sure of is that your listView have a height that can fit exact number of items you want to be displayed.
e.g
If you want 4 items to be displayed after snap effect and your row height (defined in its layout) should be 1/4 of the total height of the list.
Note that after the smoothScrollBy() call, getFirstVisiblePosition() may point to the list item ABOVE the topmost one in the listview. This is especially true when view.getChildAt(0).getBottom() == 0. I had to call view.setSelection(view.getFirstVisiblePosition() + 1) to remedy this odd behavior.