Add animation to custom SnapHelper in recycler view when items are scrolled - android

I'm using custom SnapHelper which extends LinearSnapHelper to automatically scroll in recycler view.Code can be found below. The scrolling however is way too fast and i want to add a slide or fade animation. Please guideLink to scrolling effect in which animation needs to be added
Auto scroll code(Fragment1.kt)-
private fun autoScrollRecyclerView() {
val snapHelper = GravitySnapHelper(Gravity.START)
snapHelper.attachToRecyclerView(binding.rcvSuccessMetric)
val timer = Timer()
timer.schedule(object : TimerTask() {
override fun run() {
if (layoutManager.findLastCompletelyVisibleItemPosition() < (adapter.itemCount) - 1)
layoutManager.smoothScrollToPosition(
binding.rcvSuccessMetric,
RecyclerView.State(),
layoutManager.findLastCompletelyVisibleItemPosition() + 2
)
else
layoutManager.smoothScrollToPosition(
binding.rcvSuccessMetric,
RecyclerView.State(),
0
)
}
}, 0, 6000)
}
GravitySnapHelper.java (Custom class)-
public class GravitySnapHelper extends LinearSnapHelper {
public static final int FLING_DISTANCE_DISABLE = -1;
public static final float FLING_SIZE_FRACTION_DISABLE = -1f;
private int gravity;
private boolean isRtl;
private boolean snapLastItem;
private int nextSnapPosition;
private boolean isScrolling = false;
private boolean snapToPadding = false;
private float scrollMsPerInch = 100f;
private int maxFlingDistance = FLING_DISTANCE_DISABLE;
private float maxFlingSizeFraction = FLING_SIZE_FRACTION_DISABLE;
private OrientationHelper verticalHelper;
private OrientationHelper horizontalHelper;
private GravitySnapHelper.SnapListener listener;
private RecyclerView recyclerView;
private RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
GravitySnapHelper.this.onScrollStateChanged(newState);
}
};
public GravitySnapHelper(int gravity) {
this(gravity, false, null);
}
public GravitySnapHelper(int gravity, #NonNull SnapListener snapListener) {
this(gravity, false, snapListener);
}
public GravitySnapHelper(int gravity, boolean enableSnapLastItem) {
this(gravity, enableSnapLastItem, null);
}
public GravitySnapHelper(int gravity, boolean enableSnapLastItem,
#Nullable SnapListener snapListener) {
if (gravity != Gravity.START
&& gravity != Gravity.END
&& gravity != Gravity.BOTTOM
&& gravity != Gravity.TOP
&& gravity != Gravity.CENTER) {
throw new IllegalArgumentException("Invalid gravity value. Use START " +
"| END | BOTTOM | TOP | CENTER constants");
}
this.snapLastItem = enableSnapLastItem;
this.gravity = gravity;
this.listener = snapListener;
}
#Override
public void attachToRecyclerView(#Nullable RecyclerView recyclerView)
throws IllegalStateException {
if (this.recyclerView != null) {
this.recyclerView.removeOnScrollListener(scrollListener);
}
if (recyclerView != null) {
recyclerView.setOnFlingListener(null);
if (gravity == Gravity.START || gravity == Gravity.END) {
isRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault())
== ViewCompat.LAYOUT_DIRECTION_RTL;
}
recyclerView.addOnScrollListener(scrollListener);
this.recyclerView = recyclerView;
} else {
this.recyclerView = null;
}
super.attachToRecyclerView(recyclerView);
}
#Override
#Nullable
public View findSnapView(#NonNull RecyclerView.LayoutManager lm) {
return findSnapView(lm, true);
}
#Nullable
public View findSnapView(#NonNull RecyclerView.LayoutManager lm, boolean checkEdgeOfList) {
View snapView = null;
switch (gravity) {
case Gravity.START:
snapView = findView(lm, getHorizontalHelper(lm), Gravity.START, checkEdgeOfList);
break;
case Gravity.END:
snapView = findView(lm, getHorizontalHelper(lm), Gravity.END, checkEdgeOfList);
break;
case Gravity.TOP:
snapView = findView(lm, getVerticalHelper(lm), Gravity.START, checkEdgeOfList);
break;
case Gravity.BOTTOM:
snapView = findView(lm, getVerticalHelper(lm), Gravity.END, checkEdgeOfList);
break;
case Gravity.CENTER:
if (lm.canScrollHorizontally()) {
snapView = findView(lm, getHorizontalHelper(lm), Gravity.CENTER,
checkEdgeOfList);
} else {
snapView = findView(lm, getVerticalHelper(lm), Gravity.CENTER,
checkEdgeOfList);
}
break;
}
if (snapView != null) {
nextSnapPosition = recyclerView.getChildAdapterPosition(snapView);
} else {
nextSnapPosition = RecyclerView.NO_POSITION;
}
return snapView;
}
#Override
#NonNull
public int[] calculateDistanceToFinalSnap(#NonNull RecyclerView.LayoutManager layoutManager,
#NonNull View targetView) {
if (gravity == Gravity.CENTER) {
//noinspection ConstantConditions
return super.calculateDistanceToFinalSnap(layoutManager, targetView);
}
int[] out = new int[2];
if (!(layoutManager instanceof LinearLayoutManager)) {
return out;
}
LinearLayoutManager lm = (LinearLayoutManager) layoutManager;
if (lm.canScrollHorizontally()) {
if ((isRtl && gravity == Gravity.END) || (!isRtl && gravity == Gravity.START)) {
out[0] = getDistanceToStart(targetView, getHorizontalHelper(lm));
} else {
out[0] = getDistanceToEnd(targetView, getHorizontalHelper(lm));
}
} else if (lm.canScrollVertically()) {
if (gravity == Gravity.TOP) {
out[1] = getDistanceToStart(targetView, getVerticalHelper(lm));
} else {
out[1] = getDistanceToEnd(targetView, getVerticalHelper(lm));
}
}
return out;
}
#Override
#NonNull
public int[] calculateScrollDistance(int velocityX, int velocityY) {
if (recyclerView == null
|| (verticalHelper == null && horizontalHelper == null)
|| (maxFlingDistance == FLING_DISTANCE_DISABLE
&& maxFlingSizeFraction == FLING_SIZE_FRACTION_DISABLE)) {
return super.calculateScrollDistance(velocityX, velocityY);
}
final int[] out = new int[2];
Scroller scroller = new Scroller(recyclerView.getContext(),
new DecelerateInterpolator());
int maxDistance = getFlingDistance();
scroller.fling(0, 0, velocityX, velocityY,
-maxDistance, maxDistance,
-maxDistance, maxDistance);
out[0] = scroller.getFinalX();
out[1] = scroller.getFinalY();
return out;
}
#Nullable
#Override
public RecyclerView.SmoothScroller createScroller(RecyclerView.LayoutManager layoutManager) {
if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)
|| recyclerView == null) {
return null;
}
return new LinearSmoothScroller(recyclerView.getContext()) {
#Override
protected void onTargetFound(View targetView,
RecyclerView.State state,
RecyclerView.SmoothScroller.Action action) {
if (recyclerView == null || recyclerView.getLayoutManager() == null) {
// The associated RecyclerView has been removed so there is no action to take.
return;
}
int[] snapDistances = calculateDistanceToFinalSnap(recyclerView.getLayoutManager(),
targetView);
final int dx = snapDistances[0];
final int dy = snapDistances[1];
final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy)));
if (time > 0) {
action.update(dx, dy, time, mDecelerateInterpolator);
}
}
#Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return scrollMsPerInch / displayMetrics.densityDpi;
}
};
}
/**
* Sets a {#link SnapListener} to listen for snap events
*
* #param listener a {#link SnapListener} that'll receive snap events or null to clear it
*/
public void setSnapListener(#Nullable SnapListener listener) {
this.listener = listener;
}
/**
* Changes the gravity of this {#link GravitySnapHelper}
* and dispatches a smooth scroll for the new snap position.
*
* #param newGravity one of the following: {#link Gravity#START}, {#link Gravity#TOP},
* {#link Gravity#END}, {#link Gravity#BOTTOM}, {#link Gravity#CENTER}
* #param smooth true if we should smooth scroll to new edge, false otherwise
*/
public void setGravity(int newGravity, Boolean smooth) {
if (this.gravity != newGravity) {
this.gravity = newGravity;
updateSnap(smooth, false);
}
}
/**
* Updates the current view to be snapped
*
* #param smooth true if we should smooth scroll, false otherwise
* #param checkEdgeOfList true if we should check if we're at an edge of the list
* and snap according to {#link GravitySnapHelper#getSnapLastItem()},
* or false to force snapping to the nearest view
*/
public void updateSnap(Boolean smooth, Boolean checkEdgeOfList) {
if (recyclerView == null || recyclerView.getLayoutManager() == null) {
return;
}
final RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();
View snapView = findSnapView(lm, checkEdgeOfList);
if (snapView != null) {
int[] out = calculateDistanceToFinalSnap(lm, snapView);
if (smooth) {
recyclerView.smoothScrollBy(out[0], out[1]);
} else {
recyclerView.scrollBy(out[0], out[1]);
}
}
}
/**
* This method will only work if there's a ViewHolder for the given position.
*
* #return true if scroll was successful, false otherwise
*/
public boolean scrollToPosition(int position) {
if (position == RecyclerView.NO_POSITION) {
return false;
}
return scrollTo(position, false);
}
/**
* Unlike {#link GravitySnapHelper#scrollToPosition(int)},
* this method will generally always find a snap view if the position is valid.
* <p>
* The smooth scroller from {#link GravitySnapHelper#createScroller(RecyclerView.LayoutManager)}
* will be used, and so will {#link GravitySnapHelper#scrollMsPerInch} for the scroll velocity
*
* #return true if scroll was successful, false otherwise
*/
public boolean smoothScrollToPosition(int position) {
if (position == RecyclerView.NO_POSITION) {
return false;
}
return scrollTo(position, true);
}
/**
* Get the current gravity being applied
*
* #return one of the following: {#link Gravity#START}, {#link Gravity#TOP}, {#link Gravity#END},
* {#link Gravity#BOTTOM}, {#link Gravity#CENTER}
*/
public int getGravity() {
return this.gravity;
}
/**
* Changes the gravity of this {#link GravitySnapHelper}
* and dispatches a smooth scroll for the new snap position.
*
* #param newGravity one of the following: {#link Gravity#START}, {#link Gravity#TOP},
* {#link Gravity#END}, {#link Gravity#BOTTOM}, {#link Gravity#CENTER}
*/
public void setGravity(int newGravity) {
setGravity(newGravity, true);
}
/**
* #return true if this SnapHelper should snap to the last item
*/
public boolean getSnapLastItem() {
return snapLastItem;
}
/**
* Enable snapping of the last item that's snappable.
* The default value is false, because you can't see the last item completely
* if this is enabled.
*
* #param snap true if you want to enable snapping of the last snappable item
*/
public void setSnapLastItem(boolean snap) {
snapLastItem = snap;
}
/**
* #return last distance set through {#link GravitySnapHelper#setMaxFlingDistance(int)}
* or {#link GravitySnapHelper#FLING_DISTANCE_DISABLE} if we're not limiting the fling distance
*/
public int getMaxFlingDistance() {
return maxFlingDistance;
}
/**
* Changes the max fling distance in absolute values.
*
* #param distance max fling distance in pixels
* or {#link GravitySnapHelper#FLING_DISTANCE_DISABLE}
* to disable fling limits
*/
public void setMaxFlingDistance(#Px int distance) {
maxFlingDistance = distance;
maxFlingSizeFraction = FLING_SIZE_FRACTION_DISABLE;
}
/**
* #return last distance set through {#link GravitySnapHelper#setMaxFlingSizeFraction(float)}
* or {#link GravitySnapHelper#FLING_SIZE_FRACTION_DISABLE}
* if we're not limiting the fling distance
*/
public float getMaxFlingSizeFraction() {
return maxFlingSizeFraction;
}
/**
* Changes the max fling distance depending on the available size of the RecyclerView.
* <p>
* Example: if you pass 0.5f and the RecyclerView measures 600dp,
* the max fling distance will be 300dp.
*
* #param fraction size fraction to be used for the max fling distance
* or {#link GravitySnapHelper#FLING_SIZE_FRACTION_DISABLE}
* to disable fling limits
*/
public void setMaxFlingSizeFraction(float fraction) {
maxFlingDistance = FLING_DISTANCE_DISABLE;
maxFlingSizeFraction = fraction;
}
/**
* #return last scroll speed set through {#link GravitySnapHelper#setScrollMsPerInch(float)}
* or 100f
*/
public float getScrollMsPerInch() {
return scrollMsPerInch;
}
/**
* Sets the scroll duration in ms per inch.
* <p>
* Default value is 100.0f
* <p>
* This value will be used in
* {#link GravitySnapHelper#createScroller(RecyclerView.LayoutManager)}
*
* #param ms scroll duration in ms per inch
*/
public void setScrollMsPerInch(float ms) {
scrollMsPerInch = ms;
}
/**
* #return true if this SnapHelper should snap to the padding. Defaults to false.
*/
public boolean getSnapToPadding() {
return snapToPadding;
}
/**
* If true, GravitySnapHelper will snap to the gravity edge
* plus any amount of padding that was set in the RecyclerView.
* <p>
* The default value is false.
*
* #param snapToPadding true if you want to snap to the padding
*/
public void setSnapToPadding(boolean snapToPadding) {
this.snapToPadding = snapToPadding;
}
/**
* #return the position of the current view that's snapped
* or {#link RecyclerView#NO_POSITION} in case there's none.
*/
public int getCurrentSnappedPosition() {
if (recyclerView != null && recyclerView.getLayoutManager() != null) {
View snappedView = findSnapView(recyclerView.getLayoutManager());
if (snappedView != null) {
return recyclerView.getChildAdapterPosition(snappedView);
}
}
return RecyclerView.NO_POSITION;
}
private int getFlingDistance() {
if (maxFlingSizeFraction != FLING_SIZE_FRACTION_DISABLE) {
if (verticalHelper != null) {
return (int) (recyclerView.getHeight() * maxFlingSizeFraction);
} else if (horizontalHelper != null) {
return (int) (recyclerView.getWidth() * maxFlingSizeFraction);
} else {
return Integer.MAX_VALUE;
}
} else if (maxFlingDistance != FLING_DISTANCE_DISABLE) {
return maxFlingDistance;
} else {
return Integer.MAX_VALUE;
}
}
/**
* #return true if the scroll will snap to a view, false otherwise
*/
private boolean scrollTo(int position, boolean smooth) {
if (recyclerView.getLayoutManager() != null) {
if (smooth) {
RecyclerView.SmoothScroller smoothScroller
= createScroller(recyclerView.getLayoutManager());
if (smoothScroller != null) {
smoothScroller.setTargetPosition(position);
recyclerView.getLayoutManager().startSmoothScroll(smoothScroller);
return true;
}
} else {
RecyclerView.ViewHolder viewHolder
= recyclerView.findViewHolderForAdapterPosition(position);
if (viewHolder != null) {
int[] distances = calculateDistanceToFinalSnap(recyclerView.getLayoutManager(),
viewHolder.itemView);
recyclerView.scrollBy(distances[0], distances[1]);
return true;
}
}
}
return false;
}
private int getDistanceToStart(View targetView, #NonNull OrientationHelper helper) {
int distance;
// If we don't care about padding, just snap to the start of the view
if (!snapToPadding) {
int childStart = helper.getDecoratedStart(targetView);
if (childStart >= helper.getStartAfterPadding() / 2) {
distance = childStart - helper.getStartAfterPadding();
} else {
distance = childStart;
}
} else {
distance = helper.getDecoratedStart(targetView) - helper.getStartAfterPadding();
}
return distance;
}
private int getDistanceToEnd(View targetView, #NonNull OrientationHelper helper) {
int distance;
if (!snapToPadding) {
int childEnd = helper.getDecoratedEnd(targetView);
if (childEnd >= helper.getEnd() - (helper.getEnd() - helper.getEndAfterPadding()) / 2) {
distance = helper.getDecoratedEnd(targetView) - helper.getEnd();
} else {
distance = childEnd - helper.getEndAfterPadding();
}
} else {
distance = helper.getDecoratedEnd(targetView) - helper.getEndAfterPadding();
}
return distance;
}
/**
* Returns the first view that we should snap to.
*
* #param layoutManager the RecyclerView's LayoutManager
* #param helper orientation helper to calculate view sizes
* #param gravity gravity to find the closest view
* #return the first view in the LayoutManager to snap to, or null if we shouldn't snap to any
*/
#Nullable
private View findView(#NonNull RecyclerView.LayoutManager layoutManager,
#NonNull OrientationHelper helper,
int gravity,
boolean checkEdgeOfList) {
if (layoutManager.getChildCount() == 0 || !(layoutManager instanceof LinearLayoutManager)) {
return null;
}
final LinearLayoutManager lm = (LinearLayoutManager) layoutManager;
// If we're at an edge of the list, we shouldn't snap
// to avoid having the last item not completely visible.
if (checkEdgeOfList && (isAtEdgeOfList(lm) && !snapLastItem)) {
return null;
}
View edgeView = null;
int distanceToTarget = Integer.MAX_VALUE;
final int center;
if (layoutManager.getClipToPadding()) {
center = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
} else {
center = helper.getEnd() / 2;
}
final boolean snapToStart = (gravity == Gravity.START && !isRtl)
|| (gravity == Gravity.END && isRtl);
final boolean snapToEnd = (gravity == Gravity.START && isRtl)
|| (gravity == Gravity.END && !isRtl);
for (int i = 0; i < lm.getChildCount(); i++) {
View currentView = lm.getChildAt(i);
int currentViewDistance;
if (snapToStart) {
if (!snapToPadding) {
currentViewDistance = Math.abs(helper.getDecoratedStart(currentView));
} else {
currentViewDistance = Math.abs(helper.getStartAfterPadding()
- helper.getDecoratedStart(currentView));
}
} else if (snapToEnd) {
if (!snapToPadding) {
currentViewDistance = Math.abs(helper.getDecoratedEnd(currentView)
- helper.getEnd());
} else {
currentViewDistance = Math.abs(helper.getEndAfterPadding()
- helper.getDecoratedEnd(currentView));
}
} else {
currentViewDistance = Math.abs(helper.getDecoratedStart(currentView)
+ (helper.getDecoratedMeasurement(currentView) / 2) - center);
}
if (currentViewDistance < distanceToTarget) {
distanceToTarget = currentViewDistance;
edgeView = currentView;
}
}
return edgeView;
}
private boolean isAtEdgeOfList(LinearLayoutManager lm) {
if ((!lm.getReverseLayout() && gravity == Gravity.START)
|| (lm.getReverseLayout() && gravity == Gravity.END)
|| (!lm.getReverseLayout() && gravity == Gravity.TOP)
|| (lm.getReverseLayout() && gravity == Gravity.BOTTOM)) {
return lm.findLastCompletelyVisibleItemPosition() == lm.getItemCount() - 1;
} else if (gravity == Gravity.CENTER) {
return lm.findFirstCompletelyVisibleItemPosition() == 0
|| lm.findLastCompletelyVisibleItemPosition() == lm.getItemCount() - 1;
} else {
return lm.findFirstCompletelyVisibleItemPosition() == 0;
}
}
/**
* Dispatches a {#link SnapListener#onSnap(int)} event if the snapped position
* is different than {#link RecyclerView#NO_POSITION}.
* <p>
* When {#link GravitySnapHelper#findSnapView(RecyclerView.LayoutManager)} returns null,
* {#link GravitySnapHelper#dispatchSnapChangeWhenPositionIsUnknown()} is called
*
* #param newState the new RecyclerView scroll state
*/
private void onScrollStateChanged(int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE && listener != null) {
if (isScrolling) {
if (nextSnapPosition != RecyclerView.NO_POSITION) {
listener.onSnap(nextSnapPosition);
} else {
dispatchSnapChangeWhenPositionIsUnknown();
}
}
}
isScrolling = newState != RecyclerView.SCROLL_STATE_IDLE;
}
/**
* Calls {#link GravitySnapHelper#findSnapView(RecyclerView.LayoutManager, boolean)}
* without the check for the edge of the list.
* <p>
* This makes sure that a position is reported in {#link SnapListener#onSnap(int)}
*/
private void dispatchSnapChangeWhenPositionIsUnknown() {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager == null) {
return;
}
View snapView = findSnapView(layoutManager, false);
if (snapView == null) {
return;
}
int snapPosition = recyclerView.getChildAdapterPosition(snapView);
if (snapPosition != RecyclerView.NO_POSITION) {
listener.onSnap(snapPosition);
}
}
private OrientationHelper getVerticalHelper(RecyclerView.LayoutManager layoutManager) {
if (verticalHelper == null || verticalHelper.getLayoutManager() != layoutManager) {
verticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
}
return verticalHelper;
}
private OrientationHelper getHorizontalHelper(RecyclerView.LayoutManager layoutManager) {
if (horizontalHelper == null || horizontalHelper.getLayoutManager() != layoutManager) {
horizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
}
return horizontalHelper;
}
/**
* A listener that's called when the {#link RecyclerView} used by {#link GravitySnapHelper}
* changes its scroll state to {#link RecyclerView#SCROLL_STATE_IDLE}
* and there's a valid snap position.
*/
public interface SnapListener {
/**
* #param position last position snapped to
*/
void onSnap(int position);
}
}

Related

AutoScroll Horizontally (Right to Left) in Android Recyclerview using UniformSpeedInterpolator OR AutoScroll RecyclerView

I have a RecyclerView with a HORIZONTAL orientation. I want to scroll RecyclerView automatically from Right to Left.
also, I need below options
should scroll in an infinite loop
support touch while auto-scroll
support reverse auto-scroll (Left to Right)
should work in all Layout manager (LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager)
Finally, I found a solution to the above question with all the options.
For start Autoscrolling
recyclerView.startAutoScroll();
For infinite loop
recyclerView.setLoopEnabled(true);
For touch
recyclerView.setCanTouch(true);
For pause Autoscrolling
recyclerView.pauseAutoScroll(true);
It will work with all Layout manager (LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager)
AutoScrollRecyclerView
public class AutoScrollRecyclerView extends RecyclerView {
private static final String TAG = AutoScrollRecyclerView.class.getSimpleName();
private static final int SPEED = 10;
/**
* Sliding estimator
*/
private UniformSpeedInterpolator mInterpolator;
/**
* Dx and dy between units
*/
private int mSpeedDx, mSpeedDy;
/**
* Sliding speed, default 100
*/
private int mCurrentSpeed = SPEED;
/**
* Whether to display the list infinitely
*/
private boolean mLoopEnabled;
/**
* Whether to slide backwards
*/
private boolean mReverse;
/**
* Whether to turn on automatic sliding
*/
private boolean mIsOpenAuto;
/**
* Whether the user can manually slide the screen
*/
private boolean mCanTouch = true;
/**
* Whether the user clicks on the screen
*/
private boolean mPointTouch;
/**
* Are you ready for data?
*/
private boolean mReady;
/**
* Whether initialization is complete
*/
private boolean mInflate;
/**
* Whether to stop scroll
*/
private boolean isStopAutoScroll = false;
public AutoScrollRecyclerView(Context context) {
this(context, null);
}
public AutoScrollRecyclerView(Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoScrollRecyclerView(Context context, #Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mInterpolator = new UniformSpeedInterpolator();
mReady = false;
}
/**
* Start sliding
*/
public void startAutoScroll() {
isStopAutoScroll = false;
openAutoScroll(mCurrentSpeed, false);
}
/**
* Start sliding
*
* #param speed Sliding distance (determining the sliding speed)
* #param reverse Whether to slide backwards
*/
public void openAutoScroll(int speed, boolean reverse) {
mReverse = reverse;
mCurrentSpeed = speed;
mIsOpenAuto = true;
notifyLayoutManager();
startScroll();
}
/**
* Is it possible to manually slide when swiping automatically?
*/
public void setCanTouch(boolean b) {
mCanTouch = b;
}
public boolean canTouch() {
return mCanTouch;
}
/**
* Set whether to display the list infinitely
*/
public void setLoopEnabled(boolean loopEnabled) {
this.mLoopEnabled = loopEnabled;
if (getAdapter() != null) {
getAdapter().notifyDataSetChanged();
startScroll();
}
}
/**
* Whether to slide infinitely
*/
public boolean isLoopEnabled() {
return mLoopEnabled;
}
/**
* Set whether to reverse
*/
public void setReverse(boolean reverse) {
mReverse = reverse;
notifyLayoutManager();
startScroll();
}
/**
* #param isStopAutoScroll
*/
public void pauseAutoScroll(boolean isStopAutoScroll) {
this.isStopAutoScroll = isStopAutoScroll;
}
public boolean getReverse() {
return mReverse;
}
/**
* Start scrolling
*/
private void startScroll() {
if (!mIsOpenAuto)
return;
if (getScrollState() == SCROLL_STATE_SETTLING)
return;
if (mInflate && mReady) {
mSpeedDx = mSpeedDy = 0;
smoothScroll();
}
}
private void smoothScroll() {
if (!isStopAutoScroll) {
int absSpeed = Math.abs(mCurrentSpeed);
int d = mReverse ? -absSpeed : absSpeed;
smoothScrollBy(d, d, mInterpolator);
}
}
private void notifyLayoutManager() {
LayoutManager layoutManager = getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager linearLayoutManager = ((LinearLayoutManager) layoutManager);
if (linearLayoutManager != null) {
linearLayoutManager.setReverseLayout(mReverse);
}
} else {
StaggeredGridLayoutManager staggeredGridLayoutManager = ((StaggeredGridLayoutManager) layoutManager);
if (staggeredGridLayoutManager != null) {
staggeredGridLayoutManager.setReverseLayout(mReverse);
}
}
}
#Override
public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
super.swapAdapter(generateAdapter(adapter), removeAndRecycleExistingViews);
mReady = true;
}
#Override
public void setAdapter(Adapter adapter) {
super.setAdapter(generateAdapter(adapter));
mReady = true;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent e) {
if (mCanTouch) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
mPointTouch = true;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (mIsOpenAuto) {
return true;
}
}
return super.onInterceptTouchEvent(e);
} else return true;
}
#Override
public boolean onTouchEvent(MotionEvent e) {
if (mCanTouch) {
switch (e.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mIsOpenAuto) {
mPointTouch = false;
smoothScroll();
return true;
}
}
return super.onTouchEvent(e);
} else return true;
}
#Override
public boolean performClick() {
return super.performClick();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
startScroll();
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
mInflate = true;
}
#Override
public void onScrolled(int dx, int dy) {
if (mPointTouch) {
mSpeedDx = 0;
mSpeedDy = 0;
return;
}
boolean vertical;
if (dx == 0) {//Vertical scrolling
mSpeedDy += dy;
vertical = true;
} else {//Horizontal scrolling
mSpeedDx += dx;
vertical = false;
}
if (vertical) {
if (Math.abs(mSpeedDy) >= Math.abs(mCurrentSpeed)) {
mSpeedDy = 0;
smoothScroll();
}
} else {
if (Math.abs(mSpeedDx) >= Math.abs(mCurrentSpeed)) {
mSpeedDx = 0;
smoothScroll();
}
}
}
#NonNull
#SuppressWarnings("unchecked")
private NestingRecyclerViewAdapter generateAdapter(Adapter adapter) {
return new NestingRecyclerViewAdapter(this, adapter);
}
/**
* Custom estimator
* Swipe the list at a constant speed
*/
private static class UniformSpeedInterpolator implements Interpolator {
#Override
public float getInterpolation(float input) {
return input;
}
}
/**
* Customize the Adapter container so that the list can be displayed in an infinite loop
*/
private static class NestingRecyclerViewAdapter<VH extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH> {
private AutoScrollRecyclerView mRecyclerView;
RecyclerView.Adapter<VH> mAdapter;
NestingRecyclerViewAdapter(AutoScrollRecyclerView recyclerView, RecyclerView.Adapter<VH> adapter) {
mAdapter = adapter;
mRecyclerView = recyclerView;
}
#NonNull
#Override
public VH onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return mAdapter.onCreateViewHolder(parent, viewType);
}
#Override
public void registerAdapterDataObserver(#NonNull RecyclerView.AdapterDataObserver observer) {
super.registerAdapterDataObserver(observer);
mAdapter.registerAdapterDataObserver(observer);
}
#Override
public void unregisterAdapterDataObserver(#NonNull RecyclerView.AdapterDataObserver observer) {
super.unregisterAdapterDataObserver(observer);
mAdapter.unregisterAdapterDataObserver(observer);
}
#Override
public void onBindViewHolder(#NonNull VH holder, int position) {
mAdapter.onBindViewHolder(holder, generatePosition(position));
}
#Override
public void setHasStableIds(boolean hasStableIds) {
super.setHasStableIds(hasStableIds);
mAdapter.setHasStableIds(hasStableIds);
}
#Override
public int getItemCount() {
//If it is an infinite scroll mode, set an unlimited number of items
return getLoopEnable() ? Integer.MAX_VALUE : mAdapter.getItemCount();
}
#Override
public int getItemViewType(int position) {
return mAdapter.getItemViewType(generatePosition(position));
}
#Override
public long getItemId(int position) {
return mAdapter.getItemId(generatePosition(position));
}
/**
* Returns the corresponding position according to the current scroll mode
*/
private int generatePosition(int position) {
if (getLoopEnable()) {
return getActualPosition(position);
} else {
return position;
}
}
/**
* Returns the actual position of the item
*
* #param position The position after starting to scroll will grow indefinitely
* #return Item actual location
*/
private int getActualPosition(int position) {
int itemCount = mAdapter.getItemCount();
return position >= itemCount ? position % itemCount : position;
}
private boolean getLoopEnable() {
return mRecyclerView.mLoopEnabled;
}
public boolean getReverse() {
return mRecyclerView.mReverse;
}
}
}

How to enable a PagerSnapHelper to snap in both directions in a RecyclerView?

I've attached a PagerSnapHelper to a RecyclerView with a Horizontal LinearLayoutManager using below code.
SnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(myRecyclerView);
However, when I perform scroll, snapping happens only in one direction. i.e. it snaps to next item on a right swipe but doesn't snap to previous item on a left swipe.
Is this the default behavior? Could I override this to enable snapping to previous item as well on a left swipe?
Any pointers here would be of great help. Thanks!
SnapHelper sometimes has issues in some cases, like item count etc.
You can use this GravitySnapHelper class which is extended from LinearSnapHelper.
import android.view.Gravity;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearSnapHelper;
import androidx.recyclerview.widget.OrientationHelper;
import androidx.recyclerview.widget.RecyclerView;
public class GravitySnapHelper extends LinearSnapHelper {
private OrientationHelper mVerticalHelper;
private OrientationHelper mHorizontalHelper;
private int mGravity;
private boolean mIsRtlHorizontal;
private boolean mSnapLastItemEnabled;
SnapListener mSnapListener;
boolean mSnapping;
private RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_SETTLING) {
mSnapping = false;
}
if (newState == RecyclerView.SCROLL_STATE_IDLE && mSnapping && mSnapListener != null) {
int position = getSnappedPosition(recyclerView);
if (position != RecyclerView.NO_POSITION) {
mSnapListener.onSnap(position);
}
mSnapping = false;
}
}
};
public GravitySnapHelper(int gravity) {
this(gravity, false, null);
}
public GravitySnapHelper(int gravity, boolean enableSnapLastItem) {
this(gravity, enableSnapLastItem, null);
}
public GravitySnapHelper(int gravity, boolean enableSnapLastItem, SnapListener snapListener) {
if (gravity != Gravity.START && gravity != Gravity.END
&& gravity != Gravity.BOTTOM && gravity != Gravity.TOP) {
throw new IllegalArgumentException("Invalid gravity value. Use START " +
"| END | BOTTOM | TOP constants");
}
mSnapListener = snapListener;
mGravity = gravity;
mSnapLastItemEnabled = enableSnapLastItem;
}
#Override
public void attachToRecyclerView(#Nullable RecyclerView recyclerView)
throws IllegalStateException {
if (recyclerView != null) {
if (mGravity == Gravity.START || mGravity == Gravity.END) {
mIsRtlHorizontal
= false;
}
if (mSnapListener != null) {
recyclerView.addOnScrollListener(mScrollListener);
}
}
super.attachToRecyclerView(recyclerView);
}
#Override
public int[] calculateDistanceToFinalSnap(#NonNull RecyclerView.LayoutManager layoutManager,
#NonNull View targetView) {
int[] out = new int[2];
if (layoutManager.canScrollHorizontally()) {
if (mGravity == Gravity.START) {
out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager), false);
} else { // END
out[0] = distanceToEnd(targetView, getHorizontalHelper(layoutManager), false);
}
} else {
out[0] = 0;
}
if (layoutManager.canScrollVertically()) {
if (mGravity == Gravity.TOP) {
out[1] = distanceToStart(targetView, getVerticalHelper(layoutManager), false);
} else { // BOTTOM
out[1] = distanceToEnd(targetView, getVerticalHelper(layoutManager), false);
}
} else {
out[1] = 0;
}
return out;
}
#Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
View snapView = null;
if (layoutManager instanceof LinearLayoutManager) {
switch (mGravity) {
case Gravity.START:
snapView = findStartView(layoutManager, getHorizontalHelper(layoutManager));
break;
case Gravity.END:
snapView = findEndView(layoutManager, getHorizontalHelper(layoutManager));
break;
case Gravity.TOP:
snapView = findStartView(layoutManager, getVerticalHelper(layoutManager));
break;
case Gravity.BOTTOM:
snapView = findEndView(layoutManager, getVerticalHelper(layoutManager));
break;
}
}
mSnapping = snapView != null;
return snapView;
}
/**
* Enable snapping of the last item that's snappable.
* The default value is false, because you can't see the last item completely
* if this is enabled.
*
* #param snap true if you want to enable snapping of the last snappable item
*/
public void enableLastItemSnap(boolean snap) {
mSnapLastItemEnabled = snap;
}
private int distanceToStart(View targetView, OrientationHelper helper, boolean fromEnd) {
if (mIsRtlHorizontal && !fromEnd) {
return distanceToEnd(targetView, helper, true);
}
return helper.getDecoratedStart(targetView) - helper.getStartAfterPadding();
}
private int distanceToEnd(View targetView, OrientationHelper helper, boolean fromStart) {
if (mIsRtlHorizontal && !fromStart) {
return distanceToStart(targetView, helper, true);
}
return helper.getDecoratedEnd(targetView) - helper.getEndAfterPadding();
}
/**
* Returns the first view that we should snap to.
*
* #param layoutManager the recyclerview's layout manager
* #param helper orientation helper to calculate view sizes
* #return the first view in the LayoutManager to snap to
*/
private View findStartView(RecyclerView.LayoutManager layoutManager,
OrientationHelper helper) {
if (layoutManager instanceof LinearLayoutManager) {
int firstChild = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
if (firstChild == RecyclerView.NO_POSITION) {
return null;
}
View child = layoutManager.findViewByPosition(firstChild);
float visibleWidth;
// We should return the child if it's visible width
// is greater than 0.5 of it's total width.
// In a RTL configuration, we need to check the start point and in LTR the end point
if (mIsRtlHorizontal) {
visibleWidth = (float) (helper.getTotalSpace() - helper.getDecoratedStart(child))
/ helper.getDecoratedMeasurement(child);
} else {
visibleWidth = (float) helper.getDecoratedEnd(child)
/ helper.getDecoratedMeasurement(child);
}
// If we're at the end of the list, we shouldn't snap
// to avoid having the last item not completely visible.
boolean endOfList = ((LinearLayoutManager) layoutManager)
.findLastCompletelyVisibleItemPosition()
== layoutManager.getItemCount() - 1;
if (visibleWidth > 0.5f && !endOfList) {
return child;
} else if (mSnapLastItemEnabled && endOfList) {
return child;
} else if (endOfList) {
return null;
} else {
// If the child wasn't returned, we need to return
// the next view close to the start.
return layoutManager.findViewByPosition(firstChild + 1);
}
}
return null;
}
private View findEndView(RecyclerView.LayoutManager layoutManager,
OrientationHelper helper) {
if (layoutManager instanceof LinearLayoutManager) {
int lastChild = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
if (lastChild == RecyclerView.NO_POSITION) {
return null;
}
View child = layoutManager.findViewByPosition(lastChild);
float visibleWidth;
if (mIsRtlHorizontal) {
visibleWidth = (float) helper.getDecoratedEnd(child)
/ helper.getDecoratedMeasurement(child);
} else {
visibleWidth = (float) (helper.getTotalSpace() - helper.getDecoratedStart(child))
/ helper.getDecoratedMeasurement(child);
}
// If we're at the start of the list, we shouldn't snap
// to avoid having the first item not completely visible.
boolean startOfList = ((LinearLayoutManager) layoutManager)
.findFirstCompletelyVisibleItemPosition() == 0;
if (visibleWidth > 0.5f && !startOfList) {
return child;
} else if (mSnapLastItemEnabled && startOfList) {
return child;
} else if (startOfList) {
return null;
} else {
// If the child wasn't returned, we need to return the previous view
return layoutManager.findViewByPosition(lastChild - 1);
}
}
return null;
}
int getSnappedPosition(RecyclerView recyclerView) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
if (mGravity == Gravity.START || mGravity == Gravity.TOP) {
return ((LinearLayoutManager) layoutManager).findFirstCompletelyVisibleItemPosition();
} else if (mGravity == Gravity.END || mGravity == Gravity.BOTTOM) {
return ((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition();
}
}
return RecyclerView.NO_POSITION;
}
private OrientationHelper getVerticalHelper(RecyclerView.LayoutManager layoutManager) {
if (mVerticalHelper == null) {
mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
}
return mVerticalHelper;
}
private OrientationHelper getHorizontalHelper(RecyclerView.LayoutManager layoutManager) {
if (mHorizontalHelper == null) {
mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
}
return mHorizontalHelper;
}
public interface SnapListener {
void onSnap(int position);
}
}
Usage is simple:
SnapHelper snapHelper = new GravitySnapHelper(Gravity.START);
snapHelper.attachToRecyclerView(recyclerView);

How to add setSelection method logic to custom listview in Android

I have developed a custom 3D ListView by following this tutorial. I am able to create the listview successfully. However, now I wish to scroll down and up in the listview by using Button events. I have created following functions for scrolling down and up in the listview.
//scroll list down
public void scrollListDown(){
int count = 20;
int currentSelected = mListView.getSelectedItemPosition();
if (currentSelected != (count -1) && currentSelected < (count - 1)) {
int count1 = currentSelected + 1;
mListView.setSelection(count1);
}
}
However, in this example, they have not written any implementation for the setSelection method. Due to this the scrolling does not work. I wish to move the selection from one item to another when a button is clicked. My function works on a normal ListView in Android
Currently, I have written a logic for the setSelection method as follows:
#Override
public void setSelection(int position) {
if (mAdapter == null) {
return;
}
if (!isInTouchMode()) {
position = lookForSelectablePosition(position, true);
if (position >= 0) {
setNextSelectedPositionInt(position);
}
} else {
mResurrectToPosition = position;
}
if (position >= 0) {
mLayoutMode = LAYOUT_SPECIFIC;
mSpecificTop = 10;
if (mNeedSync) {
mSyncPosition = position;
mSyncRowId = mAdapter.getItemId(position);
}
requestLayout();
}
}
This is not working as expected. Can anyone suggest how I should use the setSelection method in this case?
My Adapter class
/**
* A simple list view that displays the items as 3D blocks
*/
public class MyListView extends AdapterView<ListAdapter> {
/** Width of the items compared to the width of the list */
private static final float ITEM_WIDTH = 0.85f;
/** Space occupied by the item relative to the height of the item */
private static final float ITEM_VERTICAL_SPACE = 1.45f;
/** Ambient light intensity */
private static final int AMBIENT_LIGHT = 55;
/** Diffuse light intensity */
private static final int DIFFUSE_LIGHT = 200;
/** Specular light intensity */
private static final float SPECULAR_LIGHT = 70;
/** Shininess constant */
private static final float SHININESS = 200;
/** The max intensity of the light */
private static final int MAX_INTENSITY = 0xFF;
/** Amount of down scaling */
private static final float SCALE_DOWN_FACTOR = 0.15f;
/** Amount to rotate during one screen length */
private static final int DEGREES_PER_SCREEN = 270;
/** Represents an invalid child index */
private static final int INVALID_INDEX = -1;
/** Distance to drag before we intercept touch events */
private static final int TOUCH_SCROLL_THRESHOLD = 10;
/** Children added with this layout mode will be added below the last child */
private static final int LAYOUT_MODE_BELOW = 0;
/** Children added with this layout mode will be added above the first child */
private static final int LAYOUT_MODE_ABOVE = 1;
/** User is not touching the list */
private static final int TOUCH_STATE_RESTING = 0;
/** User is touching the list and right now it's still a "click" */
private static final int TOUCH_STATE_CLICK = 1;
/** User is scrolling the list */
private static final int TOUCH_STATE_SCROLL = 2;
/** The adapter with all the data */
private ListAdapter mAdapter;
/** Current touch state */
private int mTouchState = TOUCH_STATE_RESTING;
/** X-coordinate of the down event */
private int mTouchStartX;
/** Y-coordinate of the down event */
private int mTouchStartY;
/**
* The top of the first item when the touch down event was received
*/
private int mListTopStart;
/** The current top of the first item */
private int mListTop;
/**
* The offset from the top of the currently first visible item to the top of
* the first item
*/
private int mListTopOffset;
/** Current rotation */
private int mListRotation;
/** The adaptor position of the first visible item */
private int mFirstItemPosition;
/** The adaptor position of the last visible item */
private int mLastItemPosition;
/** A list of cached (re-usable) item views */
private final LinkedList<View> mCachedItemViews = new LinkedList<View>();
/** Used to check for long press actions */
private Runnable mLongPressRunnable;
/** Reusable rect */
private Rect mRect;
/** Camera used for 3D transformations */
private Camera mCamera;
/** Re-usable matrix for canvas transformations */
private Matrix mMatrix;
/** Paint object to draw with */
private Paint mPaint;
/** true if rotation of the items is enabled */
private boolean mRotationEnabled = true;
/** true if lighting of the items is enabled */
private boolean mLightEnabled = true;
int mSyncPosition;
long mSyncRowId = INVALID_ROW_ID;
long mNextSelectedRowId;
boolean mNeedSync = false;
int mLayoutHeight;
private boolean mAreAllItemsSelectable = true;
private int mResurrectToPosition;
private int mLayoutMode, mSpecificTop,mNextSelectedPosition;
static final int SYNC_SELECTED_POSITION = 0;
int mSyncMode;
static final int LAYOUT_SPECIFIC = 4;
/**
* Constructor
*
* #param context The context
* #param attrs Attributes
*/
public MyListView(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
#Override
public void setAdapter(ListAdapter adapter) {
mAdapter = adapter;
removeAllViewsInLayout();
requestLayout();
}
#Override
public ListAdapter getAdapter() {
return mAdapter;
}
#Override
public void setSelection(int position) {
//throw new UnsupportedOperationException("Not supported");
if (mAdapter == null) {
return;
}
if (!isInTouchMode()) {
position = lookForSelectablePosition(position, true);
if (position >= 0) {
setNextSelectedPositionInt(position);
}
} else {
mResurrectToPosition = position;
}
if (position >= 0) {
mLayoutMode = LAYOUT_SPECIFIC;
mSpecificTop = 10;
if (mNeedSync) {
mSyncPosition = position;
mSyncRowId = mAdapter.getItemId(position);
}
requestLayout();
}
}
public void setNextSelectedPositionInt(int position) {
mNextSelectedPosition = position;
mNextSelectedRowId = getItemIdAtPosition(position);
// If we are trying to sync to the selection, update that too
if (mNeedSync && mSyncMode == SYNC_SELECTED_POSITION && position >= 0) {
mSyncPosition = position;
mSyncRowId = mNextSelectedRowId;
}
}
/**
* Find a position that can be selected (i.e., is not a separator).
*
* #param position The starting position to look at.
* #param lookDown Whether to look down for other positions.
* #return The next selectable position starting at position and then searching either up or
* down. Returns {#link #INVALID_POSITION} if nothing can be found.
*/
public int lookForSelectablePosition(int position, boolean lookDown) {
final ListAdapter adapter = mAdapter;
if (adapter == null || isInTouchMode()) {
return INVALID_POSITION;
}
final int count = adapter.getCount();
if (!mAreAllItemsSelectable) {
if (lookDown) {
position = Math.max(0, position);
while (position < count && !adapter.isEnabled(position)) {
position++;
}
} else {
position = Math.min(position, count - 1);
while (position >= 0 && !adapter.isEnabled(position)) {
position--;
}
}
if (position < 0 || position >= count) {
return INVALID_POSITION;
}
return position;
} else {
if (position < 0 || position >= count) {
return INVALID_POSITION;
}
return position;
}
}
/* #Override
public View getSelectedView() {
throw new UnsupportedOperationException("Not supported");
}*/
/**
* Enables and disables individual rotation of the items.
*
* #param enable If rotation should be enabled or not
*/
public void enableRotation(final boolean enable) {
mRotationEnabled = enable;
if (!mRotationEnabled) {
mListRotation = 0;
}
invalidate();
}
/**
* Checks whether rotation is enabled
*
* #return true if rotation is enabled
*/
public boolean isRotationEnabled() {
return mRotationEnabled;
}
/**
* Enables and disables lighting of the items.
*
* #param enable If lighting should be enabled or not
*/
public void enableLight(final boolean enable) {
mLightEnabled = enable;
if (!mLightEnabled) {
mPaint.setColorFilter(null);
} else {
mPaint.setAlpha(0xFF);
}
invalidate();
}
/**
* Checks whether lighting is enabled
*
* #return true if rotation is enabled
*/
public boolean isLightEnabled() {
return mLightEnabled;
}
#Override
public boolean onInterceptTouchEvent(final MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouch(event);
return false;
case MotionEvent.ACTION_MOVE:
return startScrollIfNeeded(event);
default:
endTouch();
return false;
}
}
#Override
public boolean onTouchEvent(final MotionEvent event) {
if (getChildCount() == 0) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouch(event);
break;
case MotionEvent.ACTION_MOVE:
if (mTouchState == TOUCH_STATE_CLICK) {
startScrollIfNeeded(event);
}
if (mTouchState == TOUCH_STATE_SCROLL) {
scrollList((int)event.getY() - mTouchStartY);
}
break;
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_CLICK) {
clickChildAt((int)event.getX(), (int)event.getY());
}
endTouch();
break;
default:
endTouch();
break;
}
return true;
}
#Override
protected void onLayout(final boolean changed, final int left, final int top, final int right,
final int bottom) {
super.onLayout(changed, left, top, right, bottom);
// if we don't have an adapter, we don't need to do anything
if (mAdapter == null) {
return;
}
if (getChildCount() == 0) {
mLastItemPosition = -1;
fillListDown(mListTop, 0);
} else {
final int offset = mListTop + mListTopOffset - getChildTop(getChildAt(0));
removeNonVisibleViews(offset);
fillList(offset);
}
positionItems();
invalidate();
}
#Override
protected boolean drawChild(final Canvas canvas, final View child, final long drawingTime) {
// get the bitmap
final Bitmap bitmap = child.getDrawingCache();
if (bitmap == null) {
// if the is null for some reason, default to the standard
// drawChild implementation
return super.drawChild(canvas, child, drawingTime);
}
// get top left coordinates
final int top = child.getTop();
final int left = child.getLeft();
// get centerX and centerY
final int childWidth = child.getWidth();
final int childHeight = child.getHeight();
final int centerX = childWidth / 2;
final int centerY = childHeight / 2;
// get scale
final float halfHeight = getHeight() / 2;
final float distFromCenter = (top + centerY - halfHeight) / halfHeight;
final float scale = (float)(1 - SCALE_DOWN_FACTOR * (1 - Math.cos(distFromCenter)));
// get rotation
float childRotation = mListRotation - 20 * distFromCenter;
childRotation %= 90;
if (childRotation < 0) {
childRotation += 90;
}
// draw the item
if (childRotation < 45) {
drawFace(canvas, bitmap, top, left, centerX, centerY, scale, childRotation - 90);
drawFace(canvas, bitmap, top, left, centerX, centerY, scale, childRotation);
} else {
drawFace(canvas, bitmap, top, left, centerX, centerY, scale, childRotation);
drawFace(canvas, bitmap, top, left, centerX, centerY, scale, childRotation - 90);
}
return false;
}
/**
* Draws a face of the 3D block
*
* #param canvas The canvas to draw on
* #param view A bitmap of the view to draw
* #param top Top placement of the view
* #param left Left placement of the view
* #param centerX Center x-coordinate of the view
* #param centerY Center y-coordinate of the view
* #param scale The scale to draw the view in
* #param rotation The rotation of the view
*/
private void drawFace(final Canvas canvas, final Bitmap view, final int top, final int left,
final int centerX, final int centerY, final float scale, final float rotation) {
// create the camera if we haven't before
if (mCamera == null) {
mCamera = new Camera();
}
// save the camera state
mCamera.save();
// translate and then rotate the camera
mCamera.translate(0, 0, centerY);
mCamera.rotateX(rotation);
mCamera.translate(0, 0, -centerY);
// create the matrix if we haven't before
if (mMatrix == null) {
mMatrix = new Matrix();
}
// get the matrix from the camera and then restore the camera
mCamera.getMatrix(mMatrix);
mCamera.restore();
// translate and scale the matrix
mMatrix.preTranslate(-centerX, -centerY);
mMatrix.postScale(scale, scale);
mMatrix.postTranslate(left + centerX, top + centerY);
// create and initialize the paint object
if (mPaint == null) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setFilterBitmap(true);
}
// set the light
if (mLightEnabled) {
mPaint.setColorFilter(calculateLight(rotation));
} else {
mPaint.setAlpha(0xFF - (int)(2 * Math.abs(rotation)));
}
// draw the bitmap
canvas.drawBitmap(view, mMatrix, mPaint);
}
/**
* Calculates the lighting of the item based on rotation.
*
* #param rotation The rotation of the item
* #return A color filter to use
*/
private LightingColorFilter calculateLight(final float rotation) {
final double cosRotation = Math.cos(Math.PI * rotation / 180);
int intensity = AMBIENT_LIGHT + (int)(DIFFUSE_LIGHT * cosRotation);
int highlightIntensity = (int)(SPECULAR_LIGHT * Math.pow(cosRotation, SHININESS));
if (intensity > MAX_INTENSITY) {
intensity = MAX_INTENSITY;
}
if (highlightIntensity > MAX_INTENSITY) {
highlightIntensity = MAX_INTENSITY;
}
final int light = Color.rgb(intensity, intensity, intensity);
final int highlight = Color.rgb(highlightIntensity, highlightIntensity, highlightIntensity);
return new LightingColorFilter(light, highlight);
}
/**
* Sets and initializes all things that need to when we start a touch
* gesture.
*
* #param event The down event
*/
private void startTouch(final MotionEvent event) {
// save the start place
mTouchStartX = (int)event.getX();
mTouchStartY = (int)event.getY();
mListTopStart = getChildTop(getChildAt(0)) - mListTopOffset;
// start checking for a long press
startLongPressCheck();
// we don't know if it's a click or a scroll yet, but until we know
// assume it's a click
mTouchState = TOUCH_STATE_CLICK;
}
/**
* Resets and recycles all things that need to when we end a touch gesture
*/
private void endTouch() {
// remove any existing check for longpress
removeCallbacks(mLongPressRunnable);
// reset touch state
mTouchState = TOUCH_STATE_RESTING;
}
/**
* Scrolls the list. Takes care of updating rotation (if enabled) and
* snapping
*
* #param scrolledDistance The distance to scroll
*/
public void scrollList(final int scrolledDistance) {
mListTop = mListTopStart + scrolledDistance;
//mRotationEnabled = false;
if (mRotationEnabled) {
mListRotation = -(DEGREES_PER_SCREEN * mListTop) / getHeight();
}
requestLayout();
}
public void scrollListViewUp(final int scrolledDistance) {
mListTop = mListTopStart + scrolledDistance;
//mRotationEnabled = false;
if (mRotationEnabled) {
mListRotation = -(DEGREES_PER_SCREEN * mListTop) / getHeight();
}
requestLayout();
}
public void scrollListViewDown(final int scrolledDistance) {
mListTop = mListTopStart + scrolledDistance;
//mRotationEnabled = false;
if (mRotationEnabled) {
mListRotation = -(DEGREES_PER_SCREEN * mListTop) / getHeight();
}
requestLayout();
}
/**
* Posts (and creates if necessary) a runnable that will when executed call
* the long click listener
*/
private void startLongPressCheck() {
// create the runnable if we haven't already
if (mLongPressRunnable == null) {
mLongPressRunnable = new Runnable() {
public void run() {
if (mTouchState == TOUCH_STATE_CLICK) {
final int index = getContainingChildIndex(mTouchStartX, mTouchStartY);
if (index != INVALID_INDEX) {
longClickChild(index);
}
}
}
};
}
// then post it with a delay
postDelayed(mLongPressRunnable, ViewConfiguration.getLongPressTimeout());
}
/**
* Checks if the user has moved far enough for this to be a scroll and if
* so, sets the list in scroll mode
*
* #param event The (move) event
* #return true if scroll was started, false otherwise
*/
public boolean startScrollIfNeeded(final MotionEvent event) {
final int xPos = (int)event.getX();
final int yPos = (int)event.getY();
if (xPos < mTouchStartX - TOUCH_SCROLL_THRESHOLD
|| xPos > mTouchStartX + TOUCH_SCROLL_THRESHOLD
|| yPos < mTouchStartY - TOUCH_SCROLL_THRESHOLD
|| yPos > mTouchStartY + TOUCH_SCROLL_THRESHOLD) {
// we've moved far enough for this to be a scroll
removeCallbacks(mLongPressRunnable);
mTouchState = TOUCH_STATE_SCROLL;
return true;
}
return false;
}
/**
* Returns the index of the child that contains the coordinates given.
*
* #param x X-coordinate
* #param y Y-coordinate
* #return The index of the child that contains the coordinates. If no child
* is found then it returns INVALID_INDEX
*/
private int getContainingChildIndex(final int x, final int y) {
if (mRect == null) {
mRect = new Rect();
}
for (int index = 0; index < getChildCount(); index++) {
getChildAt(index).getHitRect(mRect);
if (mRect.contains(x, y)) {
return index;
}
}
return INVALID_INDEX;
}
/**
* Calls the item click listener for the child with at the specified
* coordinates
*
* #param x The x-coordinate
* #param y The y-coordinate
*/
private void clickChildAt(final int x, final int y) {
final int index = getContainingChildIndex(x, y);
if (index != INVALID_INDEX) {
final View itemView = getChildAt(index);
final int position = mFirstItemPosition + index;
final long id = mAdapter.getItemId(position);
performItemClick(itemView, position, id);
}
}
/**
* Calls the item long click listener for the child with the specified index
*
* #param index Child index
*/
private void longClickChild(final int index) {
final View itemView = getChildAt(index);
final int position = mFirstItemPosition + index;
final long id = mAdapter.getItemId(position);
final OnItemLongClickListener listener = getOnItemLongClickListener();
if (listener != null) {
listener.onItemLongClick(this, itemView, position, id);
}
}
/**
* Removes view that are outside of the visible part of the list. Will not
* remove all views.
*
* #param offset Offset of the visible area
*/
private void removeNonVisibleViews(final int offset) {
// We need to keep close track of the child count in this function. We
// should never remove all the views, because if we do, we loose track
// of were we are.
int childCount = getChildCount();
// if we are not at the bottom of the list and have more than one child
if (mLastItemPosition != mAdapter.getCount() - 1 && childCount > 1) {
// check if we should remove any views in the top
View firstChild = getChildAt(0);
while (firstChild != null && getChildBottom(firstChild) + offset < 0) {
// remove the top view
removeViewInLayout(firstChild);
childCount--;
mCachedItemViews.addLast(firstChild);
mFirstItemPosition++;
// update the list offset (since we've removed the top child)
mListTopOffset += getChildHeight(firstChild);
// Continue to check the next child only if we have more than
// one child left
if (childCount > 1) {
firstChild = getChildAt(0);
} else {
firstChild = null;
}
}
}
// if we are not at the top of the list and have more than one child
if (mFirstItemPosition != 0 && childCount > 1) {
// check if we should remove any views in the bottom
View lastChild = getChildAt(childCount - 1);
while (lastChild != null && getChildTop(lastChild) + offset > getHeight()) {
// remove the bottom view
removeViewInLayout(lastChild);
childCount--;
mCachedItemViews.addLast(lastChild);
mLastItemPosition--;
// Continue to check the next child only if we have more than
// one child left
if (childCount > 1) {
lastChild = getChildAt(childCount - 1);
} else {
lastChild = null;
}
}
}
}
/**
* Fills the list with child-views
*
* #param offset Offset of the visible area
*/
private void fillList(final int offset) {
final int bottomEdge = getChildBottom(getChildAt(getChildCount() - 1));
fillListDown(bottomEdge, offset);
final int topEdge = getChildTop(getChildAt(0));
fillListUp(topEdge, offset);
}
/**
* Starts at the bottom and adds children until we've passed the list bottom
*
* #param bottomEdge The bottom edge of the currently last child
* #param offset Offset of the visible area
*/
private void fillListDown(int bottomEdge, final int offset) {
while (bottomEdge + offset < getHeight() && mLastItemPosition < mAdapter.getCount() - 1) {
mLastItemPosition++;
final View newBottomchild = mAdapter.getView(mLastItemPosition, getCachedView(), this);
addAndMeasureChild(newBottomchild, LAYOUT_MODE_BELOW);
bottomEdge += getChildHeight(newBottomchild);
}
}
/**
* Starts at the top and adds children until we've passed the list top
*
* #param topEdge The top edge of the currently first child
* #param offset Offset of the visible area
*/
private void fillListUp(int topEdge, final int offset) {
while (topEdge + offset > 0 && mFirstItemPosition > 0) {
mFirstItemPosition--;
final View newTopCild = mAdapter.getView(mFirstItemPosition, getCachedView(), this);
addAndMeasureChild(newTopCild, LAYOUT_MODE_ABOVE);
final int childHeight = getChildHeight(newTopCild);
topEdge -= childHeight;
// update the list offset (since we added a view at the top)
mListTopOffset -= childHeight;
}
}
/**
* Adds a view as a child view and takes care of measuring it
*
* #param child The view to add
* #param layoutMode Either LAYOUT_MODE_ABOVE or LAYOUT_MODE_BELOW
*/
private void addAndMeasureChild(final View child, final int layoutMode) {
LayoutParams params = child.getLayoutParams();
if (params == null) {
params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
final int index = layoutMode == LAYOUT_MODE_ABOVE ? 0 : -1;
child.setDrawingCacheEnabled(true);
addViewInLayout(child, index, params, true);
final int itemWidth = (int)(getWidth() * ITEM_WIDTH);
child.measure(MeasureSpec.EXACTLY | itemWidth, MeasureSpec.UNSPECIFIED);
}
/**
* Positions the children at the "correct" positions
*/
private void positionItems() {
int top = mListTop + mListTopOffset;
for (int index = 0; index < getChildCount(); index++) {
final View child = getChildAt(index);
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
final int left = (getWidth() - width) / 2;
final int margin = getChildMargin(child);
final int childTop = top + margin;
child.layout(left, childTop, left + width, childTop + height);
top += height + 2 * margin;
}
}
/**
* Checks if there is a cached view that can be used
*
* #return A cached view or, if none was found, null
*/
private View getCachedView() {
if (mCachedItemViews.size() != 0) {
return mCachedItemViews.removeFirst();
}
return null;
}
/**
* Returns the margin of the child view taking into account the
* ITEM_VERTICAL_SPACE
*
* #param child The child view
* #return The margin of the child view
*/
private int getChildMargin(final View child) {
return (int)(child.getMeasuredHeight() * (ITEM_VERTICAL_SPACE - 1) / 2);
}
/**
* Returns the top placement of the child view taking into account the
* ITEM_VERTICAL_SPACE
*
* #param child The child view
* #return The top placement of the child view
*/
private int getChildTop(final View child) {
return child.getTop() - getChildMargin(child);
}
/**
* Returns the bottom placement of the child view taking into account the
* ITEM_VERTICAL_SPACE
*
* #param child The child view
* #return The bottom placement of the child view
*/
private int getChildBottom(final View child) {
return child.getBottom() + getChildMargin(child);
}
/**
* Returns the height of the child view taking into account the
* ITEM_VERTICAL_SPACE
*
* #param child The child view
* #return The height of the child view
*/
private int getChildHeight(final View child) {
return child.getMeasuredHeight() + 2 * getChildMargin(child);
}
#Override
public View getSelectedView() {
// TODO Auto-generated method stub
return null;
}

Tap to refresh is visible all the time and gap is still visible on list view

I am using pull to refresh in my application. Pull to refresh is working fine when the list size is crossing screen. But when the size is one or two there is a gap between the header and the listview saying tap to refresh.
Here is my code
public class PullToRefreshListView extends ListView implements OnScrollListener {
private static final int TAP_TO_REFRESH = 1;
private static final int PULL_TO_REFRESH = 2;
private static final int RELEASE_TO_REFRESH = 3;
private static final int REFRESHING = 4;
private static final String TAG = "PullToRefreshListView";
private OnRefreshListener mOnRefreshListener;
/**
* Listener that will receive notifications every time the list scrolls.
*/
private OnScrollListener mOnScrollListener;
private LayoutInflater mInflater;
private RelativeLayout mRefreshView;
private TextView mRefreshViewText;
private ImageView mRefreshViewImage;
private ProgressBar mRefreshViewProgress;
private TextView mRefreshViewLastUpdated;
private int mCurrentScrollState;
private int mRefreshState;
private RotateAnimation mFlipAnimation;
private RotateAnimation mReverseFlipAnimation;
private int mRefreshViewHeight;
private int mRefreshOriginalTopPadding;
private int mLastMotionY;
private boolean mBounceHack;
public PullToRefreshListView(Context context) {
super(context);
init(context);
}
public PullToRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public PullToRefreshListView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
// Load all of the animations we need in code rather than through XML
mFlipAnimation = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mFlipAnimation.setInterpolator(new LinearInterpolator());
mFlipAnimation.setDuration(250);
mFlipAnimation.setFillAfter(true);
mReverseFlipAnimation = new RotateAnimation(-180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
mReverseFlipAnimation.setDuration(250);
mReverseFlipAnimation.setFillAfter(true);
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mRefreshView = (RelativeLayout) mInflater.inflate(
R.layout.pull_to_refresh_header, this, false);
mRefreshViewText = (TextView) mRefreshView
.findViewById(R.id.pull_to_refresh_text);
mRefreshViewImage = (ImageView) mRefreshView
.findViewById(R.id.pull_to_refresh_image);
mRefreshViewProgress = (ProgressBar) mRefreshView
.findViewById(R.id.pull_to_refresh_progress);
mRefreshViewLastUpdated = (TextView) mRefreshView
.findViewById(R.id.pull_to_refresh_updated_at);
mRefreshViewImage.setMinimumHeight(50);
mRefreshView.setOnClickListener(new OnClickRefreshListener());
mRefreshOriginalTopPadding = mRefreshView.getPaddingTop();
mRefreshState = TAP_TO_REFRESH;
addHeaderView(mRefreshView);
super.setOnScrollListener(this);
measureView(mRefreshView);
mRefreshViewHeight = mRefreshView.getMeasuredHeight();
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setSelection(1);
}
#Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
setSelection(1);
}
/**
* Set the listener that will receive notifications every time the list
* scrolls.
*
* #param l
* The scroll listener.
*/
#Override
public void setOnScrollListener(AbsListView.OnScrollListener l) {
mOnScrollListener = l;
}
/**
* Register a callback to be invoked when this list should be refreshed.
*
* #param onRefreshListener
* The callback to run.
*/
public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
mOnRefreshListener = onRefreshListener;
}
/**
* Set a text to represent when the list was last updated.
*
* #param lastUpdated
* Last updated at.
*/
public void setLastUpdated(CharSequence lastUpdated) {
if (lastUpdated != null) {
mRefreshViewLastUpdated.setVisibility(View.VISIBLE);
mRefreshViewLastUpdated.setText(lastUpdated);
} else {
mRefreshViewLastUpdated.setVisibility(View.GONE);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
final int y = (int) event.getY();
mBounceHack = false;
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
if (!isVerticalScrollBarEnabled()) {
setVerticalScrollBarEnabled(true);
}
if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {
if ((mRefreshView.getBottom() >= mRefreshViewHeight || mRefreshView
.getTop() >= 0) && mRefreshState == RELEASE_TO_REFRESH) {
// Initiate the refresh
mRefreshState = REFRESHING;
prepareForRefresh();
onRefresh();
} else if (mRefreshView.getBottom() < mRefreshViewHeight
|| mRefreshView.getTop() <= 0) {
// Abort refresh and scroll down below the refresh view
resetHeader();
setSelection(1);
}
}
break;
case MotionEvent.ACTION_DOWN:
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
applyHeaderPadding(event);
break;
}
return super.onTouchEvent(event);
}
private void applyHeaderPadding(MotionEvent ev) {
// getHistorySize has been available since API 1
int pointerCount = ev.getHistorySize();
for (int p = 0; p < pointerCount; p++) {
if (mRefreshState == RELEASE_TO_REFRESH) {
if (isVerticalFadingEdgeEnabled()) {
setVerticalScrollBarEnabled(false);
}
int historicalY = (int) ev.getHistoricalY(p);
// Calculate the padding to apply, we divide by 1.7 to
// simulate a more resistant effect during pull.
int topPadding = (int) (((historicalY - mLastMotionY) - mRefreshViewHeight) / 1.7);
mRefreshView.setPadding(mRefreshView.getPaddingLeft(),
topPadding, mRefreshView.getPaddingRight(),
mRefreshView.getPaddingBottom());
}
}
}
/**
* Sets the header padding back to original size.
*/
private void resetHeaderPadding() {
mRefreshView.setPadding(mRefreshView.getPaddingLeft(),
mRefreshOriginalTopPadding, mRefreshView.getPaddingRight(),
mRefreshView.getPaddingBottom());
}
/**
* Resets the header to the original state.
*/
private void resetHeader() {
if (mRefreshState != TAP_TO_REFRESH) {
mRefreshState = TAP_TO_REFRESH;
resetHeaderPadding();
// Set refresh view text to the pull label
mRefreshViewText.setText(R.string.pull_to_refresh_tap_label);
// Replace refresh drawable with arrow drawable
mRefreshViewImage
.setImageResource(R.drawable.ic_pulltorefresh_arrow);
// Clear the full rotation animation
mRefreshViewImage.clearAnimation();
// Hide progress bar and arrow.
mRefreshViewImage.setVisibility(View.GONE);
mRefreshViewProgress.setVisibility(View.GONE);
}
}
private void measureView(View child) {
ViewGroup.LayoutParams p = child.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0,
MeasureSpec.UNSPECIFIED);
}
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
// "Release to refresh..." and flip the arrow drawable.
if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL
&& mRefreshState != REFRESHING) {
if (firstVisibleItem == 0) {
mRefreshViewImage.setVisibility(View.VISIBLE);
if ((mRefreshView.getBottom() >= mRefreshViewHeight + 20 || mRefreshView
.getTop() >= 0) && mRefreshState != RELEASE_TO_REFRESH) {
mRefreshViewText
.setText(R.string.pull_to_refresh_release_label);
mRefreshViewImage.clearAnimation();
mRefreshViewImage.startAnimation(mFlipAnimation);
mRefreshState = RELEASE_TO_REFRESH;
} else if (mRefreshView.getBottom() < mRefreshViewHeight + 20
&& mRefreshState != PULL_TO_REFRESH) {
mRefreshViewText
.setText(R.string.pull_to_refresh_pull_label);
if (mRefreshState != TAP_TO_REFRESH) {
mRefreshViewImage.clearAnimation();
mRefreshViewImage.startAnimation(mReverseFlipAnimation);
}
mRefreshState = PULL_TO_REFRESH;
}
} else {
mRefreshViewImage.setVisibility(View.GONE);
resetHeader();
}
} else if (mCurrentScrollState == SCROLL_STATE_FLING
&& firstVisibleItem == 0 && mRefreshState != REFRESHING) {
setSelection(1);
mBounceHack = true;
} else if (mBounceHack && mCurrentScrollState == SCROLL_STATE_FLING) {
setSelection(1);
}
if (mOnScrollListener != null) {
mOnScrollListener.onScroll(view, firstVisibleItem,
visibleItemCount, totalItemCount);
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
mCurrentScrollState = scrollState;
if (mCurrentScrollState == SCROLL_STATE_IDLE) {
mBounceHack = false;
}
if (mOnScrollListener != null) {
mOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
public void prepareForRefresh() {
resetHeaderPadding();
mRefreshViewImage.setVisibility(View.GONE);
// We need this hack, otherwise it will keep the previous drawable.
mRefreshViewImage.setImageDrawable(null);
mRefreshViewProgress.setVisibility(View.VISIBLE);
// Set refresh view text to the refreshing label
mRefreshViewText.setText(R.string.pull_to_refresh_refreshing_label);
mRefreshState = REFRESHING;
}
public void onRefresh() {
Log.d(TAG, "onRefresh");
if (mOnRefreshListener != null) {
mOnRefreshListener.onRefresh();
}
}
/**
* Resets the list to a normal state after a refresh.
*
* #param lastUpdated
* Last updated at.
*/
public void onRefreshComplete(CharSequence lastUpdated) {
setLastUpdated(lastUpdated);
onRefreshComplete();
}
/**
* Resets the list to a normal state after a refresh.
*/
public void onRefreshComplete() {
Log.d(TAG, "onRefreshComplete");
resetHeader();
// If refresh view is visible when loading completes, scroll down to
// the next item.
if (mRefreshView.getBottom() > 0) {
invalidateViews();
setSelection(1);
}
}
/**
* Invoked when the refresh view is clicked on. This is mainly used when
* there's only a few items in the list and it's not possible to drag the
* list.
*/
private class OnClickRefreshListener implements OnClickListener {
#Override
public void onClick(View v) {
if (mRefreshState != REFRESHING) {
prepareForRefresh();
onRefresh();
}
}
}
/**
* Interface definition for a callback to be invoked when list should be
* refreshed.
*/
public interface OnRefreshListener {
/**
* Called when the list should be refreshed.
* <p>
* A call to {#link PullToRefreshListView #onRefreshComplete()} is
* expected to indicate that the refresh has completed.
*/
public void onRefresh();
}
}
Here is my xml code
<com.k2b.kluebook.pulltorefresh.PullToRefreshListView
android:id="#+id/list_pulltorefresh"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:divider="#null"
android:dividerHeight="0dp" >
</com.k2b.kluebook.pulltorefresh.PullToRefreshListView>
Here is my class file code
listview.setOnRefreshListener(new OnRefreshListener() {
#Override
public void onRefresh() {
// Do work to refresh the list here.
}
});
How to get rid of the GAP and "Tap to Refresh".
Use this code instead
public class PullToRefreshListView extends ListView implements OnScrollListener {
// private static final int TAP_TO_REFRESH = 1;
private static final int PULL_TO_REFRESH = 2;
private static final int RELEASE_TO_REFRESH = 3;
protected static final int REFRESHING = 4;
protected static final String TAG = "PullToRefreshListView";
private OnRefreshListener mOnRefreshListener;
/**
* Listener that will receive notifications every time the list scrolls.
*/
private OnScrollListener mOnScrollListener;
protected LayoutInflater mInflater;
// header
private RelativeLayout mRefreshView;
private TextView mRefreshViewText;
private ImageView mRefreshViewImage;
private ProgressBar mRefreshViewProgress;
private TextView mRefreshViewLastUpdated;
protected int mCurrentScrollState;
protected int mRefreshState;
private RotateAnimation mFlipAnimation;
private RotateAnimation mReverseFlipAnimation;
private int mRefreshViewHeight;
private int mRefreshOriginalTopPadding;
private int mLastMotionY;
private boolean mBounceHack;
public PullToRefreshListView(Context context) {
super(context);
init(context);
}
public PullToRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public PullToRefreshListView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
init(context);
}
protected void init(Context context) {
// Load all of the animations we need in code rather than through XML
mFlipAnimation = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mFlipAnimation.setInterpolator(new LinearInterpolator());
mFlipAnimation.setDuration(250);
mFlipAnimation.setFillAfter(true);
mReverseFlipAnimation = new RotateAnimation(-180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
mReverseFlipAnimation.setDuration(250);
mReverseFlipAnimation.setFillAfter(true);
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// header
mRefreshView = (RelativeLayout) mInflater.inflate(
R.layout.pull_to_refresh_header, this, false);
mRefreshViewText = (TextView) mRefreshView
.findViewById(R.id.pull_to_refresh_text);
mRefreshViewImage = (ImageView) mRefreshView
.findViewById(R.id.pull_to_refresh_image);
mRefreshViewProgress = (ProgressBar) mRefreshView
.findViewById(R.id.pull_to_refresh_progress);
mRefreshViewLastUpdated = (TextView) mRefreshView
.findViewById(R.id.pull_to_refresh_updated_at);
mRefreshViewImage.setMinimumHeight(50);
mRefreshView.setOnClickListener(new OnClickRefreshListener());
mRefreshOriginalTopPadding = mRefreshView.getPaddingTop();
mRefreshState = PULL_TO_REFRESH;
addHeaderView(mRefreshView);
super.setOnScrollListener(this);
measureView(mRefreshView);
mRefreshViewHeight = mRefreshView.getMeasuredHeight();
}
#Override
protected void onAttachedToWindow() {
//have to ask super to attach to window, otherwise it won't scroll in jelly bean.
super.onAttachedToWindow();
setSelection(1);
}
#Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
setSelection(1);
}
/**
* Set the listener that will receive notifications every time the list
* scrolls.
*
* #param l
* The scroll listener.
*/
#Override
public void setOnScrollListener(AbsListView.OnScrollListener l) {
mOnScrollListener = l;
}
/**
* Register a callback to be invoked when this list should be refreshed.
*
* #param onRefreshListener
* The callback to run.
*/
public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
mOnRefreshListener = onRefreshListener;
}
/**
* Set a text to represent when the list was last updated.
*
* #param lastUpdated
* Last updated at.
*/
public void setLastUpdated(CharSequence lastUpdated) {
if (lastUpdated != null) {
mRefreshViewLastUpdated.setVisibility(View.VISIBLE);
mRefreshViewLastUpdated.setText(lastUpdated);
} else {
mRefreshViewLastUpdated.setVisibility(View.GONE);
}
}
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent(MotionEvent event) {
final int y = (int) event.getY();
mBounceHack = false;
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
if (!isVerticalScrollBarEnabled()) {
setVerticalScrollBarEnabled(true);
}
if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {
if ((mRefreshView.getBottom() >= mRefreshViewHeight || mRefreshView
.getTop() >= 0) && mRefreshState == RELEASE_TO_REFRESH) {
// Initiate the refresh
mRefreshState = REFRESHING;
prepareForRefresh();
onRefresh();
} else if (mRefreshView.getBottom() < mRefreshViewHeight
|| mRefreshView.getTop() <= 0) {
// Abort refresh and scroll down below the refresh view
resetHeader();
setSelection(1);
}
}
break;
case MotionEvent.ACTION_DOWN:
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
applyHeaderPadding(event);
break;
}
return super.onTouchEvent(event);
}
private void applyHeaderPadding(MotionEvent ev) {
// getHistorySize has been available since API 1
int pointerCount = ev.getHistorySize();
for (int p = 0; p < pointerCount; p++) {
// if (mRefreshState == RELEASE_TO_REFRESH) {
if (isVerticalFadingEdgeEnabled()) {
setVerticalScrollBarEnabled(false);
}
int historicalY = (int) ev.getHistoricalY(p);
// Calculate the padding to apply, we divide by 1.7 to
// simulate a more resistant effect during pull.
int topPadding = (int) (((historicalY - mLastMotionY) - mRefreshViewHeight) / 1.7);
mRefreshView.setPadding(mRefreshView.getPaddingLeft(),
topPadding, mRefreshView.getPaddingRight(),
mRefreshView.getPaddingBottom());
}
// }
}
/**
* Sets the header padding back to original size.
*/
private void resetHeaderPadding() {
mLastMotionY = 0;
mRefreshView.setPadding(mRefreshView.getPaddingLeft(),
mRefreshOriginalTopPadding, mRefreshView.getPaddingRight(),
mRefreshView.getPaddingBottom());
}
/**
* Resets the header to the original state.
*/
private void resetHeader() {
// if (mRefreshState != TAP_TO_REFRESH) {
mRefreshState = PULL_TO_REFRESH;
resetHeaderPadding();
// Set refresh view text to the pull label
mRefreshViewText.setText(R.string.pull_to_refresh_tap_label);
// Replace refresh drawable with arrow drawable
mRefreshViewImage
.setImageResource(R.drawable.ic_pulltorefresh_arrow);
// Clear the full rotation animation
mRefreshViewImage.clearAnimation();
// Hide progress bar and arrow.
mRefreshViewImage.setVisibility(View.GONE);
mRefreshViewProgress.setVisibility(View.GONE);
// }
}
private void measureView(View child) {
ViewGroup.LayoutParams p = child.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0,
MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthSpec, childHeightSpec);
}
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// When the refresh view is completely visible, change the text to say
// "Release to refresh..." and flip the arrow drawable.
if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL
&& mRefreshState != REFRESHING) {
if (firstVisibleItem == 0) {
mRefreshViewImage.setVisibility(View.VISIBLE);
if ((mRefreshView.getBottom() >= mRefreshViewHeight + 20 || mRefreshView
.getTop() >= 0) && mRefreshState != RELEASE_TO_REFRESH) {
mRefreshViewText
.setText(R.string.pull_to_refresh_release_label);
mRefreshViewImage.clearAnimation();
mRefreshViewImage.startAnimation(mFlipAnimation);
mRefreshState = RELEASE_TO_REFRESH;
} else if (mRefreshView.getBottom() < mRefreshViewHeight + 20
&& mRefreshState != PULL_TO_REFRESH) {
mRefreshViewText
.setText(R.string.pull_to_refresh_pull_label);
// if (mRefreshState != TAP_TO_REFRESH) {
mRefreshViewImage.clearAnimation();
mRefreshViewImage.startAnimation(mReverseFlipAnimation);
// }
mRefreshState = PULL_TO_REFRESH;
}
} else {
mRefreshViewImage.setVisibility(View.GONE);
resetHeader();
}
} else if (mCurrentScrollState == SCROLL_STATE_FLING
&& firstVisibleItem == 0 && mRefreshState != REFRESHING) {
setSelection(1);
mBounceHack = true;
} else if (mBounceHack && mCurrentScrollState == SCROLL_STATE_FLING) {
setSelection(1);
}
if (mOnScrollListener != null) {
mOnScrollListener.onScroll(view, firstVisibleItem,
visibleItemCount, totalItemCount);
}
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
mCurrentScrollState = scrollState;
if (mCurrentScrollState == SCROLL_STATE_IDLE) {
mBounceHack = false;
}
if (mOnScrollListener != null) {
mOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
public void prepareForRefresh() {
resetHeaderPadding();
mRefreshViewImage.setVisibility(View.GONE);
// We need this hack, otherwise it will keep the previous drawable.
mRefreshViewImage.setImageDrawable(null);
mRefreshViewProgress.setVisibility(View.VISIBLE);
// Set refresh view text to the refreshing label
mRefreshViewText.setText(R.string.pull_to_refresh_refreshing_label);
mRefreshState = REFRESHING;
}
public void onRefresh() {
Log.d(TAG, "onRefresh");
if (mOnRefreshListener != null) {
mOnRefreshListener.onRefresh();
}
}
/**
* Resets the list to a normal state after a refresh.
*
* #param lastUpdated
* Last updated at.
*/
public void onRefreshComplete(CharSequence lastUpdated) {
setLastUpdated(lastUpdated);
onRefreshComplete();
}
/**
* Resets the list to a normal state after a refresh.
*/
public void onRefreshComplete() {
Log.d(TAG, "onRefreshComplete");
resetHeader();
// If refresh view is visible when loading completes, scroll down to
// the next item.
if (mRefreshView.getBottom() > 0) {
invalidateViews();
setSelection(1);
}
}
/**
* Invoked when the refresh view is clicked on. This is mainly used when
* there's only a few items in the list and it's not possible to drag the
* list.
*/
private class OnClickRefreshListener implements OnClickListener {
public void onClick(View v) {
if (mRefreshState != REFRESHING) {
prepareForRefresh();
onRefresh();
}
}
}
/**
* Interface definition for a callback to be invoked when list should be
* refreshed.
*/
public interface OnRefreshListener {
/**
* Called when the list should be refreshed.
* <p>
* A call to {#link PullToRefreshListView #onRefreshComplete()} is
* expected to indicate that the refresh has completed.
*/
public void onRefresh();
}
}
also set the visibility to gone in the pull_to_refresh_header.xml in your library layout if you have it (android:id="#+id/pull_to_refresh_text")
<TextView
android:id="#+id/pull_to_refresh_text"
android:text="#string/pull_to_refresh_pull_label"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
android:paddingTop="5dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:visibility="gone"
/>
enjoy!

How to make listview empty in android

I am using pulltorefresh in my application.In onRefresh(), I tried to remove all the items by keeping listView.setAdapter(null);
But still my listView.getCount() is returning 1.
listView.setOnRefreshListener(new OnRefreshListener() {
// #Override
public void onRefresh() {
// Your code to refresh the list contents goes here
scroll=true;
pic.clear();
id.clear();
name.clear();
msg.clear();
img.clear();
profimg.clear();
objid.clear();
comment.clear();
weburl.clear();
adapter.clear();
likes.clear();
like_or_unlike.clear();
previousTotal = 0;
adapter.clear();
j=0;
loading = true;
webserv="https://graph.facebook.com/me/home?access_token="+accesstoken;
// listView.setAdapter(null);
System.out.println(listView.getCount());
// doInBack dob=new doInBack();
// dob.execute();
System.out.println(listView.getCount());
doback(webserv);
Log.e("hi","doback called");
}
});
My pulltorefresh class is
package com.beerbro.utils;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.*;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.*;
import android.view.animation.Animation.AnimationListener;
import android.widget.*;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* A generic, customizable Android ListView implementation that has 'Pull to Refresh' functionality.
* <p/>
* This ListView can be used in place of the normal Android android.widget.ListView class.
* <p/>
* Users of this class should implement OnRefreshListener and call setOnRefreshListener(..)
* to get notified on refresh events. The using class should call onRefreshComplete() when
* refreshing is finished.
* <p/>
* The using class can call setRefreshing() to set the state explicitly to refreshing. This
* is useful when you want to show the spinner and 'Refreshing' text when the
* refresh was not triggered by 'Pull to Refresh', for example on start.
* <p/>
* For more information, visit the project page:
* https://github.com/erikwt/PullToRefresh-ListView
*
* #author Erik Wallentinsen <dev+ptr#erikw.eu>
* #version 1.0.0
*/
public class PullToRefreshListView extends ListView{
private static final float PULL_RESISTANCE = 1.7f;
private static final int BOUNCE_ANIMATION_DURATION = 700;
private static final int BOUNCE_ANIMATION_DELAY = 100;
private static final float BOUNCE_OVERSHOOT_TENSION = 1.4f;
private static final int ROTATE_ARROW_ANIMATION_DURATION = 250;
private static enum State{
PULL_TO_REFRESH,
RELEASE_TO_REFRESH,
REFRESHING
}
/**
* Interface to implement when you want to get notified of 'pull to refresh'
* events.
* Call setOnRefreshListener(..) to activate an OnRefreshListener.
*/
public interface OnRefreshListener{
/**
* Method to be called when a refresh is requested
*/
public void onRefresh();
}
private static int measuredHeaderHeight;
private boolean scrollbarEnabled;
private boolean bounceBackHeader;
private boolean lockScrollWhileRefreshing;
private boolean showLastUpdatedText;
private String pullToRefreshText;
private String releaseToRefreshText;
private String refreshingText;
private String lastUpdatedText;
private SimpleDateFormat lastUpdatedDateFormat = new SimpleDateFormat("dd/MM HH:mm");
private float previousY;
private int headerPadding;
private boolean hasResetHeader;
private long lastUpdated = -1;
private State state;
private LinearLayout headerContainer;
private RelativeLayout header;
private RotateAnimation flipAnimation;
private RotateAnimation reverseFlipAnimation;
private ImageView image;
private ProgressBar spinner;
private TextView text;
private TextView lastUpdatedTextView;
private OnItemClickListener onItemClickListener;
private OnItemLongClickListener onItemLongClickListener;
private OnRefreshListener onRefreshListener;
public PullToRefreshListView(Context context){
super(context);
init();
}
public PullToRefreshListView(Context context, AttributeSet attrs){
super(context, attrs);
init();
}
public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
init();
}
#Override
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
this.onItemClickListener = onItemClickListener;
}
#Override
public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener){
this.onItemLongClickListener = onItemLongClickListener;
}
/**
* Activate an OnRefreshListener to get notified on 'pull to refresh'
* events.
*
* #param onRefreshListener The OnRefreshListener to get notified
*/
public void setOnRefreshListener(OnRefreshListener onRefreshListener){
this.onRefreshListener = onRefreshListener;
}
/**
* #return If the list is in 'Refreshing' state
*/
public boolean isRefreshing(){
return state == State.REFRESHING;
}
/**
* Default is false. When lockScrollWhileRefreshing is set to true, the list
* cannot scroll when in 'refreshing' mode. It's 'locked' on refreshing.
*
* #param lockScrollWhileRefreshing
*/
public void setLockScrollWhileRefreshing(boolean lockScrollWhileRefreshing){
this.lockScrollWhileRefreshing = lockScrollWhileRefreshing;
}
/**
* Default is false. Show the last-updated date/time in the 'Pull ro Refresh'
* header. See 'setLastUpdatedDateFormat' to set the date/time formatting.
*
* #param showLastUpdatedText
*/
public void setShowLastUpdatedText(boolean showLastUpdatedText){
this.showLastUpdatedText = showLastUpdatedText;
if(!showLastUpdatedText) lastUpdatedTextView.setVisibility(View.GONE);
}
/**
* Default: "dd/MM HH:mm". Set the format in which the last-updated
* date/time is shown. Meaningless if 'showLastUpdatedText == false (default)'.
* See 'setShowLastUpdatedText'.
*
* #param lastUpdatedDateFormat
*/
public void setLastUpdatedDateFormat(SimpleDateFormat lastUpdatedDateFormat){
this.lastUpdatedDateFormat = lastUpdatedDateFormat;
}
/**
* Explicitly set the state to refreshing. This
* is useful when you want to show the spinner and 'Refreshing' text when
* the refresh was not triggered by 'pull to refresh', for example on start.
*/
public void setRefreshing(){
state = State.REFRESHING;
scrollTo(0, 0);
setUiRefreshing();
setHeaderPadding(0);
}
/**
* Set the state back to 'pull to refresh'. Call this method when refreshing
* the data is finished.
*/
public void onRefreshComplete(){
state = State.PULL_TO_REFRESH;
resetHeader();
lastUpdated = System.currentTimeMillis();
}
/**
* Change the label text on state 'Pull to Refresh'
*
* #param pullToRefreshText Text
*/
public void setTextPullToRefresh(String pullToRefreshText){
this.pullToRefreshText = pullToRefreshText;
if(state == State.PULL_TO_REFRESH){
text.setText(pullToRefreshText);
}
}
/**
* Change the label text on state 'Release to Refresh'
*
* #param releaseToRefreshText Text
*/
public void setTextReleaseToRefresh(String releaseToRefreshText){
this.releaseToRefreshText = releaseToRefreshText;
if(state == State.RELEASE_TO_REFRESH){
text.setText(releaseToRefreshText);
}
}
/**
* Change the label text on state 'Refreshing'
*
* #param refreshingText Text
*/
public void setTextRefreshing(String refreshingText){
this.refreshingText = refreshingText;
if(state == State.REFRESHING){
text.setText(refreshingText);
}
}
private void init(){
setVerticalFadingEdgeEnabled(false);
headerContainer = (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.ptr_header, null);
header = (RelativeLayout) headerContainer.findViewById(R.id.ptr_id_header);
text = (TextView) header.findViewById(R.id.ptr_id_text);
lastUpdatedTextView = (TextView) header.findViewById(R.id.ptr_id_last_updated);
image = (ImageView) header.findViewById(R.id.ptr_id_image);
spinner = (ProgressBar) header.findViewById(R.id.ptr_id_spinner);
pullToRefreshText = getContext().getString(R.string.ptr_pull_to_refresh);
releaseToRefreshText = getContext().getString(R.string.ptr_release_to_refresh);
refreshingText = getContext().getString(R.string.ptr_refreshing);
lastUpdatedText = getContext().getString(R.string.ptr_last_updated);
flipAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
flipAnimation.setInterpolator(new LinearInterpolator());
flipAnimation.setDuration(ROTATE_ARROW_ANIMATION_DURATION);
flipAnimation.setFillAfter(true);
reverseFlipAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
reverseFlipAnimation.setInterpolator(new LinearInterpolator());
reverseFlipAnimation.setDuration(ROTATE_ARROW_ANIMATION_DURATION);
reverseFlipAnimation.setFillAfter(true);
addHeaderView(headerContainer);
setState(State.PULL_TO_REFRESH);
scrollbarEnabled = isVerticalScrollBarEnabled();
ViewTreeObserver vto = header.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new PTROnGlobalLayoutListener());
super.setOnItemClickListener(new PTROnItemClickListener());
super.setOnItemLongClickListener(new PTROnItemLongClickListener());
}
private void setHeaderPadding(int padding){
headerPadding = padding;
MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) header.getLayoutParams();
mlp.setMargins(0, Math.round(padding), 0, 0);
header.setLayoutParams(mlp);
}
#Override
public boolean onTouchEvent(MotionEvent event){
if(lockScrollWhileRefreshing
&& (state == State.REFRESHING || getAnimation() != null && !getAnimation().hasEnded())){
return true;
}
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
if(getFirstVisiblePosition() == 0) previousY = event.getY();
else previousY = -1;
break;
case MotionEvent.ACTION_UP:
if(previousY != -1 && (state == State.RELEASE_TO_REFRESH || getFirstVisiblePosition() == 0)){
switch(state){
case RELEASE_TO_REFRESH:
setState(State.REFRESHING);
bounceBackHeader();
break;
case PULL_TO_REFRESH:
resetHeader();
break;
}
}
break;
case MotionEvent.ACTION_MOVE:
if(previousY != -1){
float y = event.getY();
float diff = y - previousY;
if(diff > 0) diff /= PULL_RESISTANCE;
previousY = y;
int newHeaderPadding = Math.max(Math.round(headerPadding + diff), -header.getHeight());
if(newHeaderPadding != headerPadding && state != State.REFRESHING){
setHeaderPadding(newHeaderPadding);
if(state == State.PULL_TO_REFRESH && headerPadding > 0){
setState(State.RELEASE_TO_REFRESH);
image.clearAnimation();
image.startAnimation(flipAnimation);
}else if(state == State.RELEASE_TO_REFRESH && headerPadding < 0){
setState(State.PULL_TO_REFRESH);
image.clearAnimation();
image.startAnimation(reverseFlipAnimation);
}
return true;
}
}
break;
}
return super.onTouchEvent(event);
}
private void bounceBackHeader(){
int yTranslate = state == State.REFRESHING ?
header.getHeight() - headerContainer.getHeight() :
-headerContainer.getHeight() - headerContainer.getTop();
TranslateAnimation bounceAnimation = new TranslateAnimation(
TranslateAnimation.ABSOLUTE, 0,
TranslateAnimation.ABSOLUTE, 0,
TranslateAnimation.ABSOLUTE, 0,
TranslateAnimation.ABSOLUTE, yTranslate);
bounceAnimation.setDuration(BOUNCE_ANIMATION_DURATION);
bounceAnimation.setFillEnabled(true);
bounceAnimation.setFillAfter(false);
bounceAnimation.setFillBefore(true);
bounceAnimation.setInterpolator(new OvershootInterpolator(BOUNCE_OVERSHOOT_TENSION));
bounceAnimation.setAnimationListener(new HeaderAnimationListener(yTranslate));
startAnimation(bounceAnimation);
}
private void resetHeader(){
if(getFirstVisiblePosition() > 0){
setHeaderPadding(-header.getHeight());
setState(State.PULL_TO_REFRESH);
return;
}
if(getAnimation() != null && !getAnimation().hasEnded()){
bounceBackHeader = true;
}else{
bounceBackHeader();
}
}
private void setUiRefreshing(){
spinner.setVisibility(View.VISIBLE);
image.clearAnimation();
image.setVisibility(View.INVISIBLE);
text.setText(refreshingText);
}
private void setState(State state){
this.state = state;
switch(state){
case PULL_TO_REFRESH:
spinner.setVisibility(View.INVISIBLE);
image.setVisibility(View.VISIBLE);
text.setText(pullToRefreshText);
if(showLastUpdatedText && lastUpdated != -1){
lastUpdatedTextView.setVisibility(View.VISIBLE);
lastUpdatedTextView.setText(String.format(lastUpdatedText, lastUpdatedDateFormat.format(new Date(lastUpdated))));
}
break;
case RELEASE_TO_REFRESH:
spinner.setVisibility(View.INVISIBLE);
image.setVisibility(View.VISIBLE);
text.setText(releaseToRefreshText);
break;
case REFRESHING:
setUiRefreshing();
lastUpdated = System.currentTimeMillis();
if(onRefreshListener == null){
setState(State.PULL_TO_REFRESH);
}else{
onRefreshListener.onRefresh();
}
break;
}
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt){
super.onScrollChanged(l, t, oldl, oldt);
if(!hasResetHeader){
if(measuredHeaderHeight > 0 && state != State.REFRESHING){
setHeaderPadding(-measuredHeaderHeight);
}
hasResetHeader = true;
}
}
private class HeaderAnimationListener implements AnimationListener{
private int height, translation;
private State stateAtAnimationStart;
public HeaderAnimationListener(int translation){
this.translation = translation;
}
public void onAnimationStart(Animation animation){
stateAtAnimationStart = state;
android.view.ViewGroup.LayoutParams lp = getLayoutParams();
height = lp.height;
lp.height = getHeight() - translation;
setLayoutParams(lp);
if(scrollbarEnabled){
setVerticalScrollBarEnabled(false);
}
}
// #Override
public void onAnimationEnd(Animation animation){
setHeaderPadding(stateAtAnimationStart == State.REFRESHING ? 0 : -measuredHeaderHeight - headerContainer.getTop());
setSelection(0);
android.view.ViewGroup.LayoutParams lp = getLayoutParams();
lp.height = height;
setLayoutParams(lp);
if(scrollbarEnabled){
setVerticalScrollBarEnabled(true);
}
if(bounceBackHeader){
bounceBackHeader = false;
postDelayed(new Runnable(){
// #Override
public void run(){
resetHeader();
}
}, BOUNCE_ANIMATION_DELAY);
}else if(stateAtAnimationStart != State.REFRESHING){
setState(State.PULL_TO_REFRESH);
}
}
// #Override
public void onAnimationRepeat(Animation animation){}
}
private class PTROnGlobalLayoutListener implements OnGlobalLayoutListener{
// #Override
public void onGlobalLayout(){
int initialHeaderHeight = header.getHeight();
if(initialHeaderHeight > 0){
measuredHeaderHeight = initialHeaderHeight;
if(measuredHeaderHeight > 0 && state != State.REFRESHING){
setHeaderPadding(-measuredHeaderHeight);
requestLayout();
}
}
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
private class PTROnItemClickListener implements OnItemClickListener{
//
// #Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id){
hasResetHeader = false;
if(onItemClickListener != null && state == State.PULL_TO_REFRESH){
// Passing up onItemClick. Correct position with the number of header views
onItemClickListener.onItemClick(adapterView, view, position - getHeaderViewsCount(), id);
}
}
}
private class PTROnItemLongClickListener implements OnItemLongClickListener{
// #Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int position, long id){
hasResetHeader = false;
if(onItemLongClickListener != null && state == State.PULL_TO_REFRESH){
// Passing up onItemLongClick. Correct position with the number of header views
return onItemLongClickListener.onItemLongClick(adapterView, view, position - getHeaderViewsCount(), id);
}
return false;
}
}
}
I really didnt understand why it is returning 1 instead of 0.
Have you tried to clear the list and the notify the adapter?
I mean, If I use an String array to make the list:
ArrayList<String> names=new ArrayList<String>();
ArrayAdapter<String> adapter=new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, names);
ListView listView = (ListView) findViewById(android.R.id.list);
listView.setAdapter(adapter);
If I want to clear it, I make:
names.clear();
adapter.notifyDataSetChanged();

Categories

Resources