Skip to content

Scrollfix for short list #43

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: scrollfix_for_short_list
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions pulltorefresh/default.properties
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "build.properties", and override values to adapt the script to your
# project structure.

# Project target.
target=android-10
android.library=true
# Project target.
target=android-14
12 changes: 12 additions & 0 deletions pulltorefresh/project.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "ant.properties", and override values to adapt the script to your
# project structure.

android.library=true
# Project target.
target=android-10
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class PullToRefreshListView extends ListView implements OnScrollListener
private int mRefreshState = PULL_TO_REFRESH;

private OnRefreshListener mOnRefreshListener;
private OnEndOfListReachedListener mOnEndOfListListener;

/**
* Listener that will receive notifications every time the list scrolls.
Expand All @@ -52,6 +53,7 @@ public class PullToRefreshListView extends ListView implements OnScrollListener
private int mRefreshOriginalTopPadding;
private int mLastMotionY;
private int mHeight = -1;
private int mScrollPriorLast = -1;

private boolean mBounceHack;
private TextView mFooterView;
Expand Down Expand Up @@ -149,46 +151,89 @@ protected void onDraw(Canvas canvas) {
adaptFooterHeight();
}
}

/**
* Adapts the height of the footer view.
* Sets list view selection to first element in adapter unless refreshing where it will set it to the refresh view element
*/
private void adaptFooterHeight() {
int itemHeight = getTotalItemHeight();
int footerAndHeaderSize = mFooterView.getHeight()
+ (mRefreshViewHeight - mRefreshOriginalTopPadding);
int actualItemsSize = itemHeight - footerAndHeaderSize;
if (mHeight < actualItemsSize) {
mFooterView.setHeight(0);
} else {
int h = mHeight - actualItemsSize;
mFooterView.setHeight(h);
setSelection(1);
}
}

/**
* Calculates the combined height of all items in the adapter.
*
* Modified from http://iserveandroid.blogspot.com/2011/06/how-to-calculate-lsitviews-total.html
*
* @return
*/
private int getTotalItemHeight() {
ListAdapter adapter = getAdapter();
int listviewElementsheight = 0;
for(int i =0; i < adapter.getCount(); i++) {
View mView = adapter.getView(i, null, this);
mView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
listviewElementsheight+= mView.getMeasuredHeight();
}
return listviewElementsheight;
}
private void setSelectionToFirst() {
if (getAdapter() != null && mRefreshState != REFRESHING) {
// This allows for the divider to push the selection down from the top so the fading edge doesn't obscure the first view
setSelectionFromTop(1, getDividerHeight());
} else {
// Refreshing or no adapter, display the refresh view
super.setSelection(0);
}
}

@Override
public void setSelection(int position) {
// If the 0th or 1st element do special behavior
if (position <= 1) {
setSelectionToFirst();
} else {
// Force to index 0 while refreshing, allow other indices while not
super.setSelection((mRefreshState == REFRESHING) ? 0 : position);
}
}

/**
* Adapts the height of the footer view.
*/
private void adaptFooterHeight() {
if (mHeight == -1) {
return;
}
// We can fill up to the total height - header padding to get it off screen
int spaceToFill = mHeight - mRefreshOriginalTopPadding;
int itemHeight = getTotalItemHeight(spaceToFill);
spaceToFill -= itemHeight;

if (spaceToFill <= 0) {
mFooterView.setHeight(0);
} else {
mFooterView.setHeight(spaceToFill);
setSelectionToFirst();
}
}

/**
* Calculates the combined height of all items in the adapter. Does not look at header and footer
*
* Modified from
* http://iserveandroid.blogspot.com/2011/06/how-to-calculate-lsitviews-total.html
*
* @param spaceToFill
* the maximum item height we care about
* @return total item height, capped to spaceToFill
*/
private int getTotalItemHeight(int spaceToFill) {
ListAdapter adapter = getAdapter();
// If no adapter there is no item height
if (adapter == null) {
return 0;
}
int listviewElementsheight = 0;
// Need to constrain width for lists with variable height items or items with wrapping text
int desiredWidth = MeasureSpec.makeMeasureSpec(getWidth(),
MeasureSpec.AT_MOST);
// Skip header and footer
for (int i = 1; i < adapter.getCount() - 1
&& listviewElementsheight < spaceToFill; i++) {
View mView = adapter.getView(i, null, this);
mView.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);
listviewElementsheight += mView.getMeasuredHeight();
}
// Add in dividers
listviewElementsheight += getDividerHeight() * (adapter.getCount() - 1);
if (listviewElementsheight > spaceToFill) {
listviewElementsheight = spaceToFill;
}
return listviewElementsheight;
}

@Override
protected void onAttachedToWindow() {
setSelection(1);
setSelectionToFirst();
}

@Override
Expand All @@ -198,8 +243,11 @@ public void setAdapter(ListAdapter adapter) {
}

super.setAdapter(adapter);

setSelection(1);
// Ensure with new data the view is reinitialized to beginning
setSelectionToFirst();
// With different data we may need to adjust footer height
adaptFooterHeight();
mScrollPriorLast = -1;
}

/**
Expand All @@ -221,6 +269,15 @@ public void setOnScrollListener(AbsListView.OnScrollListener l) {
public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
mOnRefreshListener = onRefreshListener;
}

/**
* Register a callback to be invoked when the end of the list is reached.
*
* @param onEndOfListListener The callback to run.
*/
public void setOnEndOfListReachedListener(OnEndOfListReachedListener onEndOfListListener) {
mOnEndOfListListener = onEndOfListListener;
}

/**
* Set a text to represent when the list was last updated.
Expand Down Expand Up @@ -256,7 +313,7 @@ public boolean onTouchEvent(MotionEvent event) {
|| mRefreshView.getTop() <= 0) {
// Abort refresh and scroll down below the refresh view
resetHeader();
setSelection(1);
setSelectionToFirst();
}
}
break;
Expand Down Expand Up @@ -346,7 +403,6 @@ private void measureView(View child) {
child.measure(childWidthSpec, childHeightSpec);
}

@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// When the refresh view is completely visible, change the text to say
Expand Down Expand Up @@ -376,10 +432,26 @@ public void onScroll(AbsListView view, int firstVisibleItem,
} else if (mCurrentScrollState == SCROLL_STATE_FLING
&& firstVisibleItem == 0
&& mRefreshState != REFRESHING) {
setSelection(1);
setSelectionToFirst();
mBounceHack = true;
} else if (mBounceHack && mCurrentScrollState == SCROLL_STATE_FLING) {
setSelection(1);
setSelectionToFirst();
}

if(mOnEndOfListListener != null)
{
// what is the bottom item that is visible
int lastInScreen = firstVisibleItem + visibleItemCount;

// is the bottom item visible
if (lastInScreen == totalItemCount) {
// Only do callback 1x when we reach the bottom
if (mScrollPriorLast != lastInScreen) {
mScrollPriorLast = lastInScreen;
// Do end of list reached callback
mOnEndOfListListener.onEndOfListReached();
}
}
}

if (mOnScrollListener != null) {
Expand All @@ -388,7 +460,6 @@ public void onScroll(AbsListView view, int firstVisibleItem,
}
}

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
mCurrentScrollState = scrollState;

Expand Down Expand Up @@ -440,7 +511,7 @@ public void onRefreshComplete() {
// the next item.
if (mRefreshView.getBottom() > 0) {
invalidateViews();
setSelection(1);
setSelectionToFirst();
}
}

Expand All @@ -450,8 +521,6 @@ public void onRefreshComplete() {
* list.
*/
private class OnClickRefreshListener implements OnClickListener {

@Override
public void onClick(View v) {
if (mRefreshState != REFRESHING) {
prepareForRefresh();
Expand All @@ -474,4 +543,15 @@ public interface OnRefreshListener {
*/
public void onRefresh();
}

/**
* Interface definition for a callback to be invoked when end of list is reached.
*/
public interface OnEndOfListReachedListener {
/**
* Called 1x when the end of list is reached. This might be used to implement an endless list which auto-loads more data as users scroll.
* It will only be called again if the adapter changes or the list grows/shrinks
*/
public void onEndOfListReached();
}
}
13 changes: 2 additions & 11 deletions pulltorefreshexample/default.properties
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "build.properties", and override values to adapt the script to your
# project structure.

# Project target.
target=android-10
android.library.reference.1=../pulltorefresh/
# Project target.
target=android-14
12 changes: 12 additions & 0 deletions pulltorefreshexample/project.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "ant.properties", and override values to adapt the script to your
# project structure.

android.library.reference.1=../pulltorefresh/
# Project target.
target=android-10
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import com.markupartist.android.widget.PullToRefreshListView;
import com.markupartist.android.widget.PullToRefreshListView.OnEndOfListReachedListener;
import com.markupartist.android.widget.PullToRefreshListView.OnRefreshListener;

public class PullToRefreshActivity extends ListActivity {
Expand All @@ -25,12 +27,17 @@ public void onCreate(Bundle savedInstanceState) {
// Set a listener to be invoked when the list should be refreshed.
PullToRefreshListView listView = (PullToRefreshListView) getListView();
listView.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh() {
// Do work to refresh the list here.
new GetRobotTalkTask().execute();
}
});
listView.setOnEndOfListReachedListener(new OnEndOfListReachedListener() {
public void onEndOfListReached() {
// Post a toast, could load more data here to extend the list
Toast.makeText(getApplicationContext(), "End of list reached", Toast.LENGTH_SHORT).show();
}
});

mAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
Expand Down