我正在开发一个应用程序,我需要一个像我的三星Galaxy S的conctact ListView一样的ListView:
当我向右滑动手指时,我可以向此联系人发送消息。
当我向右滑动手指时,我可以打电话给我的联系人。
我有我的ListView,只需要这个功能......
提前致谢。
PD: 我搜索了很多,但没有找到任何东西。最相似的: Resource for Android Slight Left/Right Slide action on listview
答案 0 :(得分:28)
在其他帖子中,有一个指向此Google代码的链接:https://gist.github.com/2980593 这篇Google+帖子来自https://plus.google.com/u/0/113735310430199015092/posts/Fgo1p5uWZLu。 这是一种Swipe-To-Dismiss功能。
通过此,您可以提供自己的Swipe-To-Action代码。所以这是我的版本,我可以个性化左右动作,你可以触发Dismiss动画(这只是对Roman Nuric代码的修改)。
您必须在项目中包含此类:
public class SwipeListViewTouchListener implements View.OnTouchListener {
// Cached ViewConfiguration and system-wide constant values
private int mSlop;
private int mMinFlingVelocity;
private int mMaxFlingVelocity;
private long mAnimationTime;
// Fixed properties
private ListView mListView;
private OnSwipeCallback mCallback;
private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero
private boolean dismissLeft = true;
private boolean dismissRight = true;
// Transient properties
private List < PendingSwipeData > mPendingSwipes = new ArrayList < PendingSwipeData > ();
private int mDismissAnimationRefCount = 0;
private float mDownX;
private boolean mSwiping;
private VelocityTracker mVelocityTracker;
private int mDownPosition;
private View mDownView;
private boolean mPaused;
/**
* The callback interface used by {@link SwipeListViewTouchListener} to inform its client
* about a successful swipe of one or more list item positions.
*/
public interface OnSwipeCallback {
/**
* Called when the user has swiped the list item to the left.
*
* @param listView The originating {@link ListView}.
* @param reverseSortedPositions An array of positions to dismiss, sorted in descending
* order for convenience.
*/
void onSwipeLeft(ListView listView, int[] reverseSortedPositions);
void onSwipeRight(ListView listView, int[] reverseSortedPositions);
}
/**
* Constructs a new swipe-to-action touch listener for the given list view.
*
* @param listView The list view whose items should be dismissable.
* @param callback The callback to trigger when the user has indicated that she would like to
* dismiss one or more list items.
*/
public SwipeListViewTouchListener(ListView listView, OnSwipeCallback callback) {
ViewConfiguration vc = ViewConfiguration.get(listView.getContext());
mSlop = vc.getScaledTouchSlop();
mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
mAnimationTime = listView.getContext().getResources().getInteger(
android.R.integer.config_shortAnimTime);
mListView = listView;
mCallback = callback;
}
/**
* Constructs a new swipe-to-action touch listener for the given list view.
*
* @param listView The list view whose items should be dismissable.
* @param callback The callback to trigger when the user has indicated that she would like to
* dismiss one or more list items.
* @param dismissLeft set if the dismiss animation is up when the user swipe to the left
* @param dismissRight set if the dismiss animation is up when the user swipe to the right
* @see #SwipeListViewTouchListener(ListView, OnSwipeCallback, boolean, boolean)
*/
public SwipeListViewTouchListener(ListView listView, OnSwipeCallback callback, boolean dismissLeft, boolean dismissRight) {
this(listView, callback);
this.dismissLeft = dismissLeft;
this.dismissRight = dismissRight;
}
/**
* Enables or disables (pauses or resumes) watching for swipe-to-dismiss gestures.
*
* @param enabled Whether or not to watch for gestures.
*/
public void setEnabled(boolean enabled) {
mPaused = !enabled;
}
/**
* Returns an {@link android.widget.AbsListView.OnScrollListener} to be added to the
* {@link ListView} using
* {@link ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener)}.
* If a scroll listener is already assigned, the caller should still pass scroll changes
* through to this listener. This will ensure that this
* {@link SwipeListViewTouchListener} is paused during list view scrolling.</p>
*
* @see {@link SwipeListViewTouchListener}
*/
public AbsListView.OnScrollListener makeScrollListener() {
return new AbsListView.OnScrollListener() {@
Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
}
@
Override
public void onScroll(AbsListView absListView, int i, int i1, int i2) {}
};
}
@
Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (mViewWidth < 2) {
mViewWidth = mListView.getWidth();
}
switch (motionEvent.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
{
if (mPaused) {
return false;
}
// TODO: ensure this is a finger, and set a flag
// Find the child view that was touched (perform a hit test)
Rect rect = new Rect();
int childCount = mListView.getChildCount();
int[] listViewCoords = new int[2];
mListView.getLocationOnScreen(listViewCoords);
int x = (int) motionEvent.getRawX() - listViewCoords[0];
int y = (int) motionEvent.getRawY() - listViewCoords[1];
View child;
for (int i = 0; i < childCount; i++) {
child = mListView.getChildAt(i);
child.getHitRect(rect);
if (rect.contains(x, y)) {
mDownView = child;
break;
}
}
if (mDownView != null) {
mDownX = motionEvent.getRawX();
mDownPosition = mListView.getPositionForView(mDownView);
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(motionEvent);
}
view.onTouchEvent(motionEvent);
return true;
}
case MotionEvent.ACTION_UP:
{
if (mVelocityTracker == null) {
break;
}
float deltaX = motionEvent.getRawX() - mDownX;
mVelocityTracker.addMovement(motionEvent);
mVelocityTracker.computeCurrentVelocity(500); // 1000 by defaut but it was too much
float velocityX = Math.abs(mVelocityTracker.getXVelocity());
float velocityY = Math.abs(mVelocityTracker.getYVelocity());
boolean swipe = false;
boolean swipeRight = false;
if (Math.abs(deltaX) > mViewWidth / 2) {
swipe = true;
swipeRight = deltaX > 0;
} else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity && velocityY < velocityX) {
swipe = true;
swipeRight = mVelocityTracker.getXVelocity() > 0;
}
if (swipe) {
// sufficent swipe value
final View downView = mDownView; // mDownView gets null'd before animation ends
final int downPosition = mDownPosition;
final boolean toTheRight = swipeRight;
++mDismissAnimationRefCount;
mDownView.animate()
.translationX(swipeRight ? mViewWidth : -mViewWidth)
.alpha(0)
.setDuration(mAnimationTime)
.setListener(new AnimatorListenerAdapter() {@
Override
public void onAnimationEnd(Animator animation) {
performSwipeAction(downView, downPosition, toTheRight, toTheRight ? dismissRight : dismissLeft);
}
});
} else {
// cancel
mDownView.animate()
.translationX(0)
.alpha(1)
.setDuration(mAnimationTime)
.setListener(null);
}
mVelocityTracker = null;
mDownX = 0;
mDownView = null;
mDownPosition = ListView.INVALID_POSITION;
mSwiping = false;
break;
}
case MotionEvent.ACTION_MOVE:
{
if (mVelocityTracker == null || mPaused) {
break;
}
mVelocityTracker.addMovement(motionEvent);
float deltaX = motionEvent.getRawX() - mDownX;
if (Math.abs(deltaX) > mSlop) {
mSwiping = true;
mListView.requestDisallowInterceptTouchEvent(true);
// Cancel ListView's touch (un-highlighting the item)
MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
(motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
mListView.onTouchEvent(cancelEvent);
}
if (mSwiping) {
mDownView.setTranslationX(deltaX);
mDownView.setAlpha(Math.max(0f, Math.min(1f,
1f - 2f * Math.abs(deltaX) / mViewWidth)));
return true;
}
break;
}
}
return false;
}
class PendingSwipeData implements Comparable < PendingSwipeData > {
public int position;
public View view;
public PendingSwipeData(int position, View view) {
this.position = position;
this.view = view;
}
@
Override
public int compareTo(PendingSwipeData other) {
// Sort by descending position
return other.position - position;
}
}
private void performSwipeAction(final View swipeView, final int swipePosition, boolean toTheRight, boolean dismiss) {
// Animate the dismissed list item to zero-height and fire the dismiss callback when
// all dismissed list item animations have completed. This triggers layout on each animation
// frame; in the future we may want to do something smarter and more performant.
final ViewGroup.LayoutParams lp = swipeView.getLayoutParams();
final int originalHeight = swipeView.getHeight();
final boolean swipeRight = toTheRight;
ValueAnimator animator;
if (dismiss)
animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime);
else
animator = ValueAnimator.ofInt(originalHeight, originalHeight - 1).setDuration(mAnimationTime);
animator.addListener(new AnimatorListenerAdapter() {@
Override
public void onAnimationEnd(Animator animation) {
--mDismissAnimationRefCount;
if (mDismissAnimationRefCount == 0) {
// No active animations, process all pending dismisses.
// Sort by descending position
Collections.sort(mPendingSwipes);
int[] swipePositions = new int[mPendingSwipes.size()];
for (int i = mPendingSwipes.size() - 1; i >= 0; i--) {
swipePositions[i] = mPendingSwipes.get(i).position;
}
if (swipeRight)
mCallback.onSwipeRight(mListView, swipePositions);
else
mCallback.onSwipeLeft(mListView, swipePositions);
ViewGroup.LayoutParams lp;
for (PendingSwipeData pendingDismiss: mPendingSwipes) {
// Reset view presentation
pendingDismiss.view.setAlpha(1f);
pendingDismiss.view.setTranslationX(0);
lp = pendingDismiss.view.getLayoutParams();
lp.height = originalHeight;
pendingDismiss.view.setLayoutParams(lp);
}
mPendingSwipes.clear();
}
}
});
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@
Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
lp.height = (Integer) valueAnimator.getAnimatedValue();
swipeView.setLayoutParams(lp);
}
});
mPendingSwipes.add(new PendingSwipeData(swipePosition, swipeView));
animator.start();
}
}
从那里,您可以使用ListView将以下代码添加到Activity中的onCreate:
// Create a ListView-specific touch listener. ListViews are given special treatment because
// by default they handle touches for their list items... i.e. they're in charge of drawing
// the pressed state (the list selector), handling list item clicks, etc.
SwipeListViewTouchListener touchListener =
new SwipeListViewTouchListener(
listView,
new SwipeListViewTouchListener.OnSwipeCallback() {
@Override
public void onSwipeLeft(ListView listView, int [] reverseSortedPositions) {
// Log.i(this.getClass().getName(), "swipe left : pos="+reverseSortedPositions[0]);
// TODO : YOUR CODE HERE FOR LEFT ACTION
}
@Override
public void onSwipeRight(ListView listView, int [] reverseSortedPositions) {
// Log.i(ProfileMenuActivity.class.getClass().getName(), "swipe right : pos="+reverseSortedPositions[0]);
// TODO : YOUR CODE HERE FOR RIGHT ACTION
}
},
true, // example : left action = dismiss
false); // example : right action without dismiss animation
listView.setOnTouchListener(touchListener);
// Setting this scroll listener is required to ensure that during ListView scrolling,
// we don't look for swipes.
listView.setOnScrollListener(touchListener.makeScrollListener());
编辑:
要在滑动时添加颜色修改,您的代码必须位于case MotionEvent.ACTION_MOVE
附近mDownView.setAlpha
。
答案 1 :(得分:18)
你可能在这里做什么是创建一个新的视图,尤其是列表视图(称之为ListViewFlinger或其他东西)。然后在此视图中,覆盖其onTouchEvent方法并在其中放置一些代码以确定幻灯片手势。一旦你有了滑动手势,就可以激活一个onSlideComplete事件(你必须让那个监听器)成为一个voialla,你就是一个带有幻灯片激活内容的ListView。
float historicX = Float.NaN, historicY = Float.NaN;
static final TRIGGER_DELTA = 50; // Number of pixels to travel till trigger
@Override public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
historicX = e.getX();
historicY = e.getY();
break;
case MotionEvent.ACTION_UP:
if (e.getX() - historicX > -TRIGGER_DELTA) {
onSlideComplete(Direction.LEFT);
return true;
}
else if (e.getX() - historicX > TRIGGER_DELTA) {
onSlideComplete(Direction.RIGHT);
return true;
} break;
default:
return super.onTouchEvent(e);
}
}
enum Direction {
LEFT, RIGHT;
}
interface OnSlideCompleteListener {
void onSlideComplete(Direction dir);
}
答案 2 :(得分:1)
它是一个非常棒的库,允许在两个方向上使用基础Layout
或Color
进行滑动,并在完成滑动/滑动手势时执行所需的操作。您可以将其配置为显示/更改布局。
〜我还没有使用过Samsung Contacts应用程序,但听起来这就是你想要的
从某种意义上说,它更像是iOS中可以滑动的TableView。