I have a ListView, which contains more elements then I can display at one time.Now I want to get Index off all Elements, which are full visible ( -> excluding those that are only partially visible).
At this moment I use getFirstVisiblePosition() & getLastVisiblePosition() into an for-loop to iterate them, but these method is not accurate as I want to.
Is there any better solution?
A ListView keeps its rows organized in a top-down list, which you can access with getChildAt(). So what you want is quite simple. Let's get the first and last Views, then check if they are completely visible or not:
// getTop() and getBottom() are relative to the ListView,
// so if getTop() is negative, it is not fully visible
int first = 0;
if(listView.getChildAt(first).getTop() < 0)
first++;
int last = listView.getChildCount() - 1;
if(listView.getChildAt(last).getBottom() > listView.getHeight())
last--;
// Now loop through your rows
for( ; first <= last; first++) {
// Do something
View row = listView.getChildAt(first);
}
Addition
Now I want to get Index off all Elements, which are full visible
I'm not certain what that sentence means. If the code above isn't the index you wanted you can use:
int first = listView.getFirstVisiblePosition();
if(listView.getChildAt(0).getTop() < 0)
first++;
To have an index that is relative to your adapter (i.e. adapter.getItem(first).)
The way I would do this is I would extend whatever view your are passing in getView of the ListView adapter and override the methods onAttachedToWindow and onDetachedToWindow to keep track of the indexes that are visible.
Try onScrollListner and you can able to use getFirstVisiblePosition and getLastVisiblePosition.
This this link, it contain similar type of problem. I suppose you got your answer there..,.
The above code is somewhat correct. If you need to find the completely visible location of view use below code
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
View v = null;
if (scrollState == 0) {
int first =0;
if (view.getChildAt(first).getTop() < 0)
first++;
int last = list.getChildCount() - 1;
if (list.getChildAt(last).getBottom() > list
.getHeight())
last--;
// Now loop through your rows
for ( ; first <= last; first++) {
// Do something
View row = view.getChildAt(first);
// postion for your row............
int i=list.getPositionForView(row);
}
}
// set the margin.
}
Related
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.
how can I access a single imageview which is located inside a row of a listview?
I have a listview that contains many rows custom xml. each row contains a imageview and a TextView. Once inserted these lines in listview how can I change the bitmap of an imageview to a specific row?
Assuming you know which position you want to update and you're on the UI thread, you can use
public View getViewForPosition(int position){
int relativePos = position - listview.getFirstVisiblePosition();
if( relativePos < 0 || relativePos > listview.getChildCount()){
return null;
}
return listview.getChildAt(relativePos);
}
A return of null from this function means that position is offscreen. A non null will return the view from getView, and you can then do a findViewById on it to find the child you want.
If calling from a non-UI thread, you can be off by 1 due to calculating as the view mapping is changing. There is a hack to fix this I came up with once, but I'd suggest just not doing it.
Edit: here's the thread safe hack.
public View getViewForPosition(int position){
int relativePos = position - listview.getFirstVisiblePosition();
//Hack for allowing us to get a view for a position that is currently being created
if( (relativePos == listview.getChildCount() || relativePos == -1) && currentProcessingView != null){
return currentProcessingView;
}
if( relativePos < 0 || relativePos >= listview.getChildCount()){
return null;
}
return listview.getChildAt(relativePos);
}
currentProcessingView should be set in getView to the current position's view at the very beginning, and to null at the end of getView. If the position is not currently on screen it will return null, you need to be able to handle that.
You could uniquely tag each of the listview items and then use findViewWithTag on it.
I try to animate a single item in my ListView, I've figured out that this is kinda hard. But, after some googling I came up with some codesnippets that I combined, but when I try to call my ListView's getChildAt in order to return the View, this returns null.
public void animateSingleItemInListView() {
final Animation animBounce = AnimationUtils.loadAnimation(context, R.anim.bounce);
int totalItemsInListView = lastCases.getCount();
int wantedPosition = 1; // Whatever position you're looking for
int firstPosition = lastCases.getFirstVisiblePosition() - lastCases.getHeaderViewsCount(); // This is the same as child #0
int wantedChild = wantedPosition - firstPosition;
// Say, first visible position is 8, you want position 10, wantedChild will now be 2
// So that means your view is child #2 in the ViewGroup:
int childs = lastCases.getChildCount();
if (wantedChild < 0) {
Log.w("UPDATEUI", "Unable to get view for desired position, because it's not being displayed on screen.");
return;
}
// Could also check if wantedPosition is between listView.getFirstVisiblePosition() and listView.getLastVisiblePosition() instead.
View wantedView = lastCases.getChildAt(firstPosition);
wantedView.setAnimation(animBounce);
}
From the code:
The totalItemsInListView returns 10, which is correct number of rows in my ListView. The integer firstPosition is of course 0, but the getChildAt() method only returns null. How come? How can I get the first View from my ListView?
try to use
lastCases.getItemAtPosition(firstPosition)
I did get the drag and drop working and the TouchListView class works great. However in my case I have rows of various height due to my adapter which contains an EditText that can have multiple lines. Therefore after I drop, all my rows convert to the tlv:normal_height which in my case is 74dip. This causes many rows to cut off all my text in the EditTexts. I tried re initializing my adapter (mylistview.setAdapter= myadapter), setting the ListView to GONE then VISIBLE and invalidateViews() but nothing seems to reset the ListView back to before I dragged, short of leaving the activity and coming back. What can be done here? -Thx
tlv:normal_height="74dip"
tlv:expanded_height="128dip"
There's little question that the original AOSP code was designed for uniform row heights, and the whole expanded_height construct was there to provide space for the user to visualize where the drop would occur.
One starting point would probably be to create a TouchListAdapter mixin interface (akin to SpinnerAdapter) where the normal_height and expanded_height would be retrieved dynamically from the adapter based on position as opposed to being fixed values declared in the layout. Whether that alone would be sufficient or more work would need to be done, I can't say.
If you come up with a solution, patches are welcome. Otherwise, I'll probably take a look at this sometime, but not very soon.
My apologies for not having a near-term silver bullet.
I edited the unExpandViews() method - called getAdapter() and for every item in my adapter set the height to 0 and then all the rows were set back to original. I also bypassed the delete part of the method since it did not apply to me.
private void unExpandViews(boolean deletion) {
int height_saved = 0;
CheckBoxifiedTextListAdapter cbla = (CheckBoxifiedTextListAdapter)getAdapter();
for (int i = 0;i < cbla.getCount(); i++)
{
//View v = getChildAt(i);
View v = cbla.getView(i, null, null);
//if (v == null)
//{
/*
if (deletion)
{
// HACK force update of mItemCount
int position = getFirstVisiblePosition();
int y = getChildAt(0).getTop();
setAdapter(getAdapter());
setSelectionFromTop(position, y);
// end hack
}
layoutChildren(); // force children to be recreated where needed
v = getChildAt(i);
if (v == null)
{
break;
}
height_saved = v.getHeight();
*/
//}
//else
//height_saved = v.getHeight();
if (isDraggableRow(v))
{
ViewGroup.LayoutParams params = v.getLayoutParams();
params.height = 0;
v.setLayoutParams(params);
v.setVisibility(View.VISIBLE);
}
}
}
I am using it, but it always returns 0, even though I have scrolled till the end of the list.
getScrollY() is actually a method on View, not ListView. It is referring to the scroll amount of the entire view, so it will almost always be 0.
If you want to know how far the ListView's contents are scrolled, you can use listView.getFirstVisiblePosition();
It does work, it returns the top part of the scrolled portion of the view in pixels from the top of the visible view. See the getScrollY() documentation. Basically if your list is taking up the full view then you will always get 0, because the top of the scrolled portion of the list is always at the top of the screen.
What you want to do to see if you are at the end of a list is something like this:
public void onCreate(final Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.main);
// The list defined as field elswhere
this.view = (ListView) findViewById(R.id.searchResults);
this.view.setOnScrollListener(new OnScrollListener() {
private int priorFirst = -1;
#Override
public void onScroll(final AbsListView view, final int first, final int visible, final int total) {
// detect if last item is visible
if (visible < total && (first + visible == total)) {
// see if we have more results
if (first != priorFirst) {
priorFirst = first;
//Do stuff here, last item is displayed, end of list reached
}
}
}
});
}
The reason for the priorFirst counter is that sometimes scroll events can be generated multiple times, so you only need to react to the first time the end of the list is reached.
If you are trying to do an auto-growing list, I'd suggest this tutorial.
You need two things to precisely define the scroll position of a listView:
To get current position:
int firstVisiblePosition = listView.getFirstVisiblePosition();
int topEdge=listView.getChildAt(0).getTop(); //This gives how much the top view has been scrolled.
To set the position:
listView.setSelectionFromTop(firstVisiblePosition,0);
// Note the '-' sign for scrollTo..
listView.scrollTo(0,-topEdge);