All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.nhaarman.listviewanimations.itemmanipulation.dragdrop.DragAndDropHandler Maven / Gradle / Ivy

There is a newer version: 3.1.0
Show newest version
package com.nhaarman.listviewanimations.itemmanipulation.dragdrop;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListAdapter;
import android.widget.WrapperListAdapter;

import com.nhaarman.listviewanimations.itemmanipulation.DynamicListView;
import com.nhaarman.listviewanimations.itemmanipulation.TouchEventHandler;
import com.nhaarman.listviewanimations.util.Swappable;

/**
 * A class which handles drag and drop functionality for listview implementations backed up by a
 * {@link com.nhaarman.listviewanimations.util.Swappable} {@link ListAdapter}.
 * This class only works properly on API levels 14 and higher.
 * 

* Users of this class must call {@link #onTouchEvent(android.view.MotionEvent)} and {@link #dispatchDraw(android.graphics.Canvas)} on the right moments. */ @TargetApi(14) public class DragAndDropHandler implements TouchEventHandler { private static final int INVALID_ID = -1; @NonNull private final DragAndDropListViewWrapper mWrapper; /** * The {@link ScrollHandler} that handles scrolling when dragging an item. */ @NonNull private final ScrollHandler mScrollHandler; /** * The {@link SwitchViewAnimator} that is responsible for animating the switch views. */ @NonNull private final SwitchViewAnimator mSwitchViewAnimator; /** * The minimum distance in pixels that should be moved before starting vertical item movement. */ private final int mSlop; /** * The {@link android.widget.ListAdapter} that is assigned. Also implements {@link com.nhaarman.listviewanimations.util.Swappable}. */ @Nullable private ListAdapter mAdapter; /** * The Drawable that is drawn when the user is dragging an item. * This value is null if and only if the user is not dragging. */ @Nullable private HoverDrawable mHoverDrawable; /** * The View that is represented by {@link #mHoverDrawable}. * When this value is not null, the View should be invisible. * This value is null if and only if the user is not dragging. */ @Nullable private View mMobileView; /** * The id of the item view that is being dragged. * This value is {@value #INVALID_ID} if and only if the user is not dragging. */ private long mMobileItemId; /** * The y coordinate of the last non-final {@code MotionEvent}. */ private float mLastMotionEventY = -1; /** * The original position of the view that is being dragged. * This value is {@value android.widget.AdapterView#INVALID_POSITION} if and only if the user is not dragging. */ private int mOriginalMobileItemPosition = AdapterView.INVALID_POSITION; /** * The {@link DraggableManager} responsible for deciding if an item can be dragged. */ @NonNull private DraggableManager mDraggableManager; /** * The {@link OnItemMovedListener} that is notified of moved items. */ @Nullable private OnItemMovedListener mOnItemMovedListener; /** * The raw x coordinate of the down event. */ private float mDownX; /** * The raw y coordinate of the down event. */ private float mDownY; /** * Specifies whether or not the hover drawable is currently being animated as result of an up / cancel event. */ private boolean mIsSettlingHoverDrawable; /** * Creates a new {@code DragAndDropHandler} for given {@link com.nhaarman.listviewanimations.itemmanipulation.DynamicListView}. * * @param dynamicListView the {@code DynamicListView} to use. */ public DragAndDropHandler(@NonNull final DynamicListView dynamicListView) { this(new DynamicListViewWrapper(dynamicListView)); } /** * Creates a new {@code DragAndDropHandler} for the listview implementation * in given {@link com.nhaarman.listviewanimations.itemmanipulation.dragdrop.DragAndDropListViewWrapper} * * @param dragAndDropListViewWrapper the {@code DragAndDropListViewWrapper} which wraps the listview implementation to use. */ public DragAndDropHandler(@NonNull final DragAndDropListViewWrapper dragAndDropListViewWrapper) { mWrapper = dragAndDropListViewWrapper; if (mWrapper.getAdapter() != null) { setAdapterInternal(mWrapper.getAdapter()); } mScrollHandler = new ScrollHandler(); mWrapper.setOnScrollListener(mScrollHandler); mDraggableManager = new DefaultDraggableManager(); if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { mSwitchViewAnimator = new KitKatSwitchViewAnimator(); } else { mSwitchViewAnimator = new LSwitchViewAnimator(); } mMobileItemId = INVALID_ID; ViewConfiguration vc = ViewConfiguration.get(dragAndDropListViewWrapper.getListView().getContext()); mSlop = vc.getScaledTouchSlop(); } /** * @throws java.lang.IllegalStateException if the adapter does not have stable ids. * @throws java.lang.IllegalArgumentException if the adapter does not implement {@link com.nhaarman.listviewanimations.util.Swappable}. */ public void setAdapter(@NonNull final ListAdapter adapter) { setAdapterInternal(adapter); } /** * @throws java.lang.IllegalStateException if the adapter does not have stable ids. * @throws java.lang.IllegalArgumentException if the adapter does not implement {@link com.nhaarman.listviewanimations.util.Swappable}. */ private void setAdapterInternal(@NonNull final ListAdapter adapter) { ListAdapter actualAdapter = adapter; if (actualAdapter instanceof WrapperListAdapter) { actualAdapter = ((WrapperListAdapter) actualAdapter).getWrappedAdapter(); } if (!actualAdapter.hasStableIds()) { throw new IllegalStateException("Adapter doesn't have stable ids! Make sure your adapter has stable ids, and override hasStableIds() to return true."); } if (!(actualAdapter instanceof Swappable)) { throw new IllegalArgumentException("Adapter should implement Swappable!"); } mAdapter = actualAdapter; } /** * Sets the scroll speed when dragging an item. Defaults to {@code 1.0f}. * * @param speed {@code <1.0f} to slow down scrolling, {@code >1.0f} to speed up scrolling. */ public void setScrollSpeed(final float speed) { mScrollHandler.setScrollSpeed(speed); } /** * Starts dragging the item at given position. User must be touching this {@code DynamicListView}. * * @param position the position of the item in the adapter to start dragging. Be sure to subtract any header views. * * @throws java.lang.IllegalStateException if the user is not touching this {@code DynamicListView}, * or if there is no adapter set. */ public void startDragging(final int position) { if (mMobileItemId != INVALID_ID) { /* We are already dragging */ return; } if (mLastMotionEventY < 0) { throw new IllegalStateException("User must be touching the DynamicListView!"); } if (mAdapter == null) { throw new IllegalStateException("This DynamicListView has no adapter set!"); } if (position < 0 || position >= mAdapter.getCount()) { /* Out of bounds */ return; } mMobileView = mWrapper.getChildAt(position - mWrapper.getFirstVisiblePosition() + mWrapper.getHeaderViewsCount()); if (mMobileView != null) { mOriginalMobileItemPosition = position; mMobileItemId = mAdapter.getItemId(position); mHoverDrawable = new HoverDrawable(mMobileView, mLastMotionEventY); mMobileView.setVisibility(View.INVISIBLE); } } /** * Sets the {@link DraggableManager} to be used for determining whether an item should be dragged when the user issues a down {@code MotionEvent}. */ public void setDraggableManager(@NonNull final DraggableManager draggableManager) { mDraggableManager = draggableManager; } /** * Sets the {@link com.nhaarman.listviewanimations.itemmanipulation.dragdrop.OnItemMovedListener} that is notified when user has dropped a dragging item. */ public void setOnItemMovedListener(@Nullable final OnItemMovedListener onItemMovedListener) { mOnItemMovedListener = onItemMovedListener; } @Override public boolean isInteracting() { return mMobileItemId != INVALID_ID; } /** * Dispatches the {@link android.view.MotionEvent}s to their proper methods if applicable. * * @param event the {@code MotionEvent}. * * @return {@code true} if the event was handled, {@code false} otherwise. */ @Override public boolean onTouchEvent(@NonNull final MotionEvent event) { boolean handled = false; /* We are in the process of animating the hover drawable back, do not start a new drag yet. */ if (!mIsSettlingHoverDrawable) { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: mLastMotionEventY = event.getY(); handled = handleDownEvent(event); break; case MotionEvent.ACTION_MOVE: mLastMotionEventY = event.getY(); handled = handleMoveEvent(event); break; case MotionEvent.ACTION_UP: handled = handleUpEvent(); mLastMotionEventY = -1; break; case MotionEvent.ACTION_CANCEL: handled = handleCancelEvent(); mLastMotionEventY = -1; break; default: handled = false; break; } } return handled; } /** * Handles the down event. *

* Finds the position and {@code View} of the touch point and, if allowed by the {@link com.nhaarman.listviewanimations.itemmanipulation.dragdrop.DraggableManager}, * starts dragging the {@code View}. * * @param event the {@link android.view.MotionEvent} that was triggered. * * @return {@code true} if we have started dragging, {@code false} otherwise. */ private boolean handleDownEvent(@NonNull final MotionEvent event) { mDownX = event.getRawX(); mDownY = event.getRawY(); return true; } /** * Retrieves the position in the list corresponding to itemId. * * @return the position of the item in the list, or {@link android.widget.AdapterView#INVALID_POSITION} if the {@code View} corresponding to the id was not found. */ private int getPositionForId(final long itemId) { View v = getViewForId(itemId); if (v == null) { return AdapterView.INVALID_POSITION; } else { return mWrapper.getPositionForView(v); } } /** * Retrieves the {@code View} in the list corresponding to itemId. * * @return the {@code View}, or {@code null} if not found. */ @Nullable private View getViewForId(final long itemId) { ListAdapter adapter = mAdapter; if (itemId == INVALID_ID || adapter == null) { return null; } int firstVisiblePosition = mWrapper.getFirstVisiblePosition(); View result = null; for (int i = 0; i < mWrapper.getChildCount() && result == null; i++) { int position = firstVisiblePosition + i; if (position - mWrapper.getHeaderViewsCount() >= 0) { long id = adapter.getItemId(position - mWrapper.getHeaderViewsCount()); if (id == itemId) { result = mWrapper.getChildAt(i); } } } return result; } /** * Handles the move events. *

* Applies the {@link MotionEvent} to the hover drawable, and switches {@code View}s if necessary. * * @param event the {@code MotionEvent}. * * @return {@code true} if the event was handled, {@code false} otherwise. */ private boolean handleMoveEvent(@NonNull final MotionEvent event) { boolean handled = false; float deltaX = event.getRawX() - mDownX; float deltaY = event.getRawY() - mDownY; if (mHoverDrawable == null && Math.abs(deltaY) > mSlop && Math.abs(deltaY) > Math.abs(deltaX)) { int position = mWrapper.pointToPosition((int) event.getX(), (int) event.getY()); if (position != AdapterView.INVALID_POSITION) { View downView = mWrapper.getChildAt(position - mWrapper.getFirstVisiblePosition()); assert downView != null; if (mDraggableManager.isDraggable(downView, position - mWrapper.getHeaderViewsCount(), event.getX() - downView.getX(), event.getY() - downView.getY())) { startDragging(position - mWrapper.getHeaderViewsCount()); handled = true; } } } else if (mHoverDrawable != null) { mHoverDrawable.handleMoveEvent(event); switchIfNecessary(); mWrapper.getListView().invalidate(); handled = true; } return handled; } /** * Finds the {@code View} that is a candidate for switching, and executes the switch if necessary. */ private void switchIfNecessary() { if (mHoverDrawable == null || mAdapter == null) { return; } int position = getPositionForId(mMobileItemId); long aboveItemId = position - 1 - mWrapper.getHeaderViewsCount() >= 0 ? mAdapter.getItemId(position - 1 - mWrapper.getHeaderViewsCount()) : INVALID_ID; long belowItemId = position + 1 - mWrapper.getHeaderViewsCount() < mAdapter.getCount() ? mAdapter.getItemId(position + 1 - mWrapper.getHeaderViewsCount()) : INVALID_ID; final long switchId = mHoverDrawable.isMovingUpwards() ? aboveItemId : belowItemId; View switchView = getViewForId(switchId); final int deltaY = mHoverDrawable.getDeltaY(); if (switchView != null && Math.abs(deltaY) > mHoverDrawable.getIntrinsicHeight()) { switchViews(switchView, switchId, mHoverDrawable.getIntrinsicHeight() * (deltaY < 0 ? -1 : 1)); } mScrollHandler.handleMobileCellScroll(); mWrapper.getListView().invalidate(); } /** * Switches the item that is currently being dragged with the item belonging to given id, * by notifying the adapter to swap positions and that the data set has changed. * * @param switchView the {@code View} that should be animated towards the old position of the currently dragging item. * @param switchId the id of the item that will take the position of the currently dragging item. * @param translationY the distance in pixels the {@code switchView} should animate - i.e. the (positive or negative) height of the {@code View} corresponding to the currently * dragging item. */ private void switchViews(final View switchView, final long switchId, final float translationY) { assert mHoverDrawable != null; assert mAdapter != null; assert mMobileView != null; final int switchViewPosition = mWrapper.getPositionForView(switchView); int mobileViewPosition = mWrapper.getPositionForView(mMobileView); ((Swappable) mAdapter).swapItems(switchViewPosition - mWrapper.getHeaderViewsCount(), mobileViewPosition - mWrapper.getHeaderViewsCount()); ((BaseAdapter) mAdapter).notifyDataSetChanged(); mHoverDrawable.shift(switchView.getHeight()); mSwitchViewAnimator.animateSwitchView(switchId, translationY); } /** * Handles the up event. *

* Animates the hover drawable to its final position, and finalizes our drag properties when the animation has finished. * Will also notify the {@link com.nhaarman.listviewanimations.itemmanipulation.dragdrop.OnItemMovedListener} set if applicable. * * @return {@code true} if the event was handled, {@code false} otherwise. */ private boolean handleUpEvent() { if (mMobileView == null) { return false; } assert mHoverDrawable != null; ValueAnimator valueAnimator = ValueAnimator.ofInt(mHoverDrawable.getTop(), (int) mMobileView.getY()); SettleHoverDrawableAnimatorListener listener = new SettleHoverDrawableAnimatorListener(mHoverDrawable, mMobileView); valueAnimator.addUpdateListener(listener); valueAnimator.addListener(listener); valueAnimator.start(); int newPosition = getPositionForId(mMobileItemId) - mWrapper.getHeaderViewsCount(); if (mOriginalMobileItemPosition != newPosition && mOnItemMovedListener != null) { mOnItemMovedListener.onItemMoved(mOriginalMobileItemPosition, newPosition); } return true; } /** * Handles the cancel event. * * @return {@code true} if the event was handled, {@code false} otherwise. */ private boolean handleCancelEvent() { return handleUpEvent(); } public void dispatchDraw(@NonNull final Canvas canvas) { if (mHoverDrawable != null) { mHoverDrawable.draw(canvas); } } /** * An interface for animating the switch views. * A distinction is made between API levels because {@link android.widget.AbsListView.OnScrollListener#onScroll(android.widget.AbsListView, int, int, * int)} calling behavior differs. */ private interface SwitchViewAnimator { void animateSwitchView(final long switchId, final float translationY); } /** * By default, nothing is draggable. User should set a {@link com.nhaarman.listviewanimations.itemmanipulation.dragdrop.DraggableManager} manually, * or use {@link #startDragging(int)} if they want to start a drag (for example using a long click listener). */ private static class DefaultDraggableManager implements DraggableManager { @Override public boolean isDraggable(@NonNull final View view, final int position, final float x, final float y) { return false; } } /** * A {@link SwitchViewAnimator} for versions KitKat and below. * This class immediately updates {@link #mMobileView} to be the newly mobile view. */ private class KitKatSwitchViewAnimator implements SwitchViewAnimator { @Override public void animateSwitchView(final long switchId, final float translationY) { assert mMobileView != null; mWrapper.getListView().getViewTreeObserver().addOnPreDrawListener(new AnimateSwitchViewOnPreDrawListener(mMobileView, switchId, translationY)); mMobileView = getViewForId(mMobileItemId); } private class AnimateSwitchViewOnPreDrawListener implements ViewTreeObserver.OnPreDrawListener { private final View mPreviousMobileView; private final long mSwitchId; private final float mTranslationY; AnimateSwitchViewOnPreDrawListener(final View previousMobileView, final long switchId, final float translationY) { mPreviousMobileView = previousMobileView; mSwitchId = switchId; mTranslationY = translationY; } @Override public boolean onPreDraw() { mWrapper.getListView().getViewTreeObserver().removeOnPreDrawListener(this); View switchView = getViewForId(mSwitchId); if (switchView != null) { switchView.setTranslationY(mTranslationY); switchView.animate().translationY(0).start(); } mPreviousMobileView.setVisibility(View.VISIBLE); if (mMobileView != null) { mMobileView.setVisibility(View.INVISIBLE); } return true; } } } /** * A {@link SwitchViewAnimator} for versions L and above. * This class updates {@link #mMobileView} only after the next frame has been drawn. */ private class LSwitchViewAnimator implements SwitchViewAnimator { @Override public void animateSwitchView(final long switchId, final float translationY) { mWrapper.getListView().getViewTreeObserver().addOnPreDrawListener(new AnimateSwitchViewOnPreDrawListener(switchId, translationY)); } private class AnimateSwitchViewOnPreDrawListener implements ViewTreeObserver.OnPreDrawListener { private final long mSwitchId; private final float mTranslationY; AnimateSwitchViewOnPreDrawListener(final long switchId, final float translationY) { mSwitchId = switchId; mTranslationY = translationY; } @Override public boolean onPreDraw() { mWrapper.getListView().getViewTreeObserver().removeOnPreDrawListener(this); View switchView = getViewForId(mSwitchId); if (switchView != null) { switchView.setTranslationY(mTranslationY); switchView.animate().translationY(0).start(); } assert mMobileView != null; mMobileView.setVisibility(View.VISIBLE); mMobileView = getViewForId(mMobileItemId); assert mMobileView != null; mMobileView.setVisibility(View.INVISIBLE); return true; } } } /** * A class which handles scrolling for this {@code DynamicListView} when dragging an item. *

* The {@link #handleMobileCellScroll()} method initiates the scroll and should typically be called on a move {@code MotionEvent}. *

* The {@link #onScroll(android.widget.AbsListView, int, int, int)} method then takes over the functionality {@link #handleMoveEvent(android.view.MotionEvent)} provides. */ private class ScrollHandler implements AbsListView.OnScrollListener { private static final int SMOOTH_SCROLL_DP = 3; /** * The default scroll amount in pixels. */ private final int mSmoothScrollPx; /** * The factor to multiply {@link #mSmoothScrollPx} with for scrolling. */ private float mScrollSpeedFactor = 1.0f; /** * The previous first visible item before checking if we should switch. */ private int mPreviousFirstVisibleItem = -1; /** * The previous last visible item before checking if we should switch. */ private int mPreviousLastVisibleItem = -1; /** * The current first visible item. */ private int mCurrentFirstVisibleItem; /** * The current last visible item. */ private int mCurrentLastVisibleItem; ScrollHandler() { Resources r = mWrapper.getListView().getResources(); mSmoothScrollPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SMOOTH_SCROLL_DP, r.getDisplayMetrics()); } /** * Sets the scroll speed when dragging an item. Defaults to {@code 1.0f}. * * @param scrollSpeedFactor {@code <1.0f} to slow down scrolling, {@code >1.0f} to speed up scrolling. */ void setScrollSpeed(final float scrollSpeedFactor) { mScrollSpeedFactor = scrollSpeedFactor; } /** * Scrolls the {@code DynamicListView} if the hover drawable is above or below the bounds of the {@code ListView}. */ void handleMobileCellScroll() { if (mHoverDrawable == null || mIsSettlingHoverDrawable) { return; } Rect r = mHoverDrawable.getBounds(); int offset = mWrapper.computeVerticalScrollOffset(); int height = mWrapper.getListView().getHeight(); int extent = mWrapper.computeVerticalScrollExtent(); int range = mWrapper.computeVerticalScrollRange(); int hoverViewTop = r.top; int hoverHeight = r.height(); int scrollPx = (int) Math.max(1, mSmoothScrollPx * mScrollSpeedFactor); if (hoverViewTop <= 0 && offset > 0) { mWrapper.smoothScrollBy(-scrollPx, 0); } else if (hoverViewTop + hoverHeight >= height && offset + extent < range) { mWrapper.smoothScrollBy(scrollPx, 0); } } @Override public void onScroll(@NonNull final AbsListView view, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) { mCurrentFirstVisibleItem = firstVisibleItem; mCurrentLastVisibleItem = firstVisibleItem + visibleItemCount; mPreviousFirstVisibleItem = mPreviousFirstVisibleItem == -1 ? mCurrentFirstVisibleItem : mPreviousFirstVisibleItem; mPreviousLastVisibleItem = mPreviousLastVisibleItem == -1 ? mCurrentLastVisibleItem : mPreviousLastVisibleItem; if (mHoverDrawable != null) { assert mMobileView != null; float y = mMobileView.getY(); mHoverDrawable.onScroll(y); } if (!mIsSettlingHoverDrawable) { checkAndHandleFirstVisibleCellChange(); checkAndHandleLastVisibleCellChange(); } mPreviousFirstVisibleItem = mCurrentFirstVisibleItem; mPreviousLastVisibleItem = mCurrentLastVisibleItem; } @Override public void onScrollStateChanged(@NonNull final AbsListView view, final int scrollState) { if (scrollState == SCROLL_STATE_IDLE && mHoverDrawable != null) { handleMobileCellScroll(); } } /** * Determines if the listview scrolled up enough to reveal a new cell at the * top of the list. If so, switches the newly shown view with the mobile view. */ private void checkAndHandleFirstVisibleCellChange() { if (mHoverDrawable == null || mAdapter == null || mCurrentFirstVisibleItem >= mPreviousFirstVisibleItem) { return; } int position = getPositionForId(mMobileItemId); if (position == AdapterView.INVALID_POSITION) { return; } long switchItemId = position - 1 - mWrapper.getHeaderViewsCount() >= 0 ? mAdapter.getItemId(position - 1 - mWrapper.getHeaderViewsCount()) : INVALID_ID; View switchView = getViewForId(switchItemId); if (switchView != null) { switchViews(switchView, switchItemId, -switchView.getHeight()); } } /** * Determines if the listview scrolled down enough to reveal a new cell at the * bottom of the list. If so, switches the newly shown view with the mobile view. */ private void checkAndHandleLastVisibleCellChange() { if (mHoverDrawable == null || mAdapter == null || mCurrentLastVisibleItem <= mPreviousLastVisibleItem) { return; } int position = getPositionForId(mMobileItemId); if (position == AdapterView.INVALID_POSITION) { return; } long switchItemId = position + 1 - mWrapper.getHeaderViewsCount() < mAdapter.getCount() ? mAdapter.getItemId(position + 1 - mWrapper.getHeaderViewsCount()) : INVALID_ID; View switchView = getViewForId(switchItemId); if (switchView != null) { switchViews(switchView, switchItemId, switchView.getHeight()); } } } /** * Updates the hover drawable's bounds with the animated values. * When the animation has finished, it will reset all the drag properties. */ private class SettleHoverDrawableAnimatorListener extends AnimatorListenerAdapter implements ValueAnimator.AnimatorUpdateListener { @NonNull private final HoverDrawable mAnimatingHoverDrawable; @NonNull private final View mAnimatingMobileView; private SettleHoverDrawableAnimatorListener(@NonNull final HoverDrawable animatingHoverDrawable, @NonNull final View animatingMobileView) { mAnimatingHoverDrawable = animatingHoverDrawable; mAnimatingMobileView = animatingMobileView; } @Override public void onAnimationStart(final Animator animation) { mIsSettlingHoverDrawable = true; } @Override public void onAnimationUpdate(final ValueAnimator animation) { mAnimatingHoverDrawable.setTop((Integer) animation.getAnimatedValue()); mWrapper.getListView().postInvalidate(); } @Override public void onAnimationEnd(final Animator animation) { mAnimatingMobileView.setVisibility(View.VISIBLE); mHoverDrawable = null; mMobileView = null; mMobileItemId = INVALID_ID; mOriginalMobileItemPosition = AdapterView.INVALID_POSITION; mIsSettlingHoverDrawable = false; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy