diff --git a/AndroidManifest.xml b/AndroidManifest.xml new file mode 100644 index 0000000..e01273c --- /dev/null +++ b/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/default.properties b/default.properties new file mode 100644 index 0000000..ecd5d12 --- /dev/null +++ b/default.properties @@ -0,0 +1,21 @@ +# +# Copyright (C) 2013 47 Degrees, LLC +# http://47deg.com +# hello@47deg.com +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# File used by Eclipse to determine the target system +# Project target. +target=android-16 \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5462a88 --- /dev/null +++ b/pom.xml @@ -0,0 +1,188 @@ + + + + + 4.0.0 + com.fortysevendeg.android + swipelistview + 1.0-SNAPSHOT + apklib + android-swipelistview-lib + An Android List View implementation with support for drawable cells and many other swipe related features + https://github.com/47deg/${project.name-github} + 2013 + + + 4.1.1.4 + android-swipelistview + master + /Applications/android-sdk-macosx + + + + https://github.com/47deg/${project.name-github}/tree/${scm.branch} + scm:git:git://github.com/47deg/${project.name-github}.git + scm:git:ssh://git@github.com/47deg/${project.name-github}.git + HEAD + + + + Github Issue Tracking + https://github.com/47deg/${project.name-github}/issues + + + + + sonatype-nexus-snapshots + Sonatype Nexus snapshot repository + https://oss.sonatype.org/content/repositories/snapshots + + + sonatype-nexus-staging + Sonatype Nexus release repository + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + Apache 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + + + + + + com.google.android + android + ${platform.version} + provided + + + com.google.android + support-v13 + r8 + + + com.nineoldandroids + nineoldandroids + 2.4.0 + + + + + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + 3.5.0 + + ${project.basedir}/AndroidManifest.xml + ${project.basedir}/assets + ${project.basedir}/res + ${project.basedir}/src/main/native + + 16 + + true + + true + + + + maven-compiler-plugin + 2.5.1 + + 1.6 + 1.6 + + + + + com.github.github + downloads-maven-plugin + 0.5 + + ${project.version} release of ${project.name} + true + true + + + *.jar + + true + + + + + org.apache.maven.plugins + maven-release-plugin + 2.2.2 + + -Dgpg.passphrase=${gpg.passphrase} + + + + maven-assembly-plugin + + + jar-with-dependencies + + + + + + + + + + + release-sign-artifacts + + + performRelease + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.4 + + ${gpg.passphrase} + + + + sign-artifacts + verify + + sign + + + + + + + + + + diff --git a/res/values/attrs.xml b/res/values/attrs.xml new file mode 100644 index 0000000..475b407 --- /dev/null +++ b/res/values/attrs.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/fortysevendeg/android/swipelistview/SwipeListView.java b/src/main/java/com/fortysevendeg/android/swipelistview/SwipeListView.java new file mode 100644 index 0000000..8768aec --- /dev/null +++ b/src/main/java/com/fortysevendeg/android/swipelistview/SwipeListView.java @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2013 47 Degrees, LLC + * http://47deg.com + * hello@47deg.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.fortysevendeg.android.swipelistview; + +import android.content.Context; +import android.content.res.TypedArray; +import android.database.DataSetObserver; +import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.ViewConfigurationCompat; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.ViewConfiguration; +import android.widget.ListAdapter; +import android.widget.ListView; + +public class SwipeListView extends ListView { + + public final static int SWIPE_MODE_NONE = 0; + public final static int SWIPE_MODE_BOTH = 1; + public final static int SWIPE_MODE_RIGHT = 2; + public final static int SWIPE_MODE_LEFT = 3; + + public final static int SWIPE_ACTION_REVEAL = 0; + public final static int SWIPE_ACTION_DISMISS = 1; + public final static int SWIPE_ACTION_CHECK = 2; + public final static int SWIPE_ACTION_NONE = 3; + + private final static int TOUCH_STATE_REST = 0; + private final static int TOUCH_STATE_SCROLLING_X = 1; + private final static int TOUCH_STATE_SCROLLING_Y = 2; + + private int touchState = TOUCH_STATE_REST; + + private float lastMotionX; + private float lastMotionY; + private int touchSlop; + + private SwipeListViewListener swipeListViewListener; + + private SwipeListViewTouchListener touchListener; + + public SwipeListView(Context context) { + super(context); + init(null); + } + + public SwipeListView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public SwipeListView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(attrs); + } + + private void init(AttributeSet attrs) { + + int swipeMode = SWIPE_MODE_BOTH; + boolean swipeOpenOnLongPress = true; + boolean swipeCloseAllItemsWhenMoveList = true; + int swipeFrontView = 0; + int swipeBackView = 0; + long swipeAnimationTime = 0; + float swipeOffsetLeft = 0; + float swipeOffsetRight = 0; + + int swipeActionLeft = SWIPE_ACTION_REVEAL; + int swipeActionRight = SWIPE_ACTION_REVEAL; + + if (attrs != null) { + TypedArray styled = getContext().obtainStyledAttributes(attrs, R.styleable.SwipeListView); + swipeMode = styled.getInt(R.styleable.SwipeListView_swipeMode, SWIPE_MODE_BOTH); + swipeActionLeft = styled.getInt(R.styleable.SwipeListView_swipeActionLeft, SWIPE_ACTION_REVEAL); + swipeActionRight = styled.getInt(R.styleable.SwipeListView_swipeActionRight, SWIPE_ACTION_REVEAL); + swipeOffsetLeft = styled.getDimension(R.styleable.SwipeListView_swipeOffsetLeft, 0); + swipeOffsetRight = styled.getDimension(R.styleable.SwipeListView_swipeOffsetRight, 0); + swipeOpenOnLongPress = styled.getBoolean(R.styleable.SwipeListView_swipeOpenOnLongPress, true); + swipeAnimationTime = styled.getInteger(R.styleable.SwipeListView_swipeAnimationTime, 0); + swipeCloseAllItemsWhenMoveList = styled.getBoolean(R.styleable.SwipeListView_swipeCloseAllItemsWhenMoveList, true); + swipeFrontView = styled.getResourceId(R.styleable.SwipeListView_swipeFrontView, 0); + swipeBackView = styled.getResourceId(R.styleable.SwipeListView_swipeBackView, 0); + } + + if (swipeFrontView == 0 || swipeBackView == 0) { + throw new RuntimeException("Missed attribute swipeFrontView or swipeBackView"); + } + + final ViewConfiguration configuration = ViewConfiguration.get(getContext()); + touchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); + touchListener = new SwipeListViewTouchListener(this, swipeFrontView, swipeBackView); + if (swipeAnimationTime > 0) { + touchListener.setAnimationTime(swipeAnimationTime); + } + touchListener.setOffsetRight(swipeOffsetRight); + touchListener.setOffsetLeft(swipeOffsetLeft); + touchListener.setSwipeActionLeft(swipeActionLeft); + touchListener.setSwipeActionRight(swipeActionRight); + touchListener.setSwipeMode(swipeMode); + touchListener.setSwipeCloseAllItemsWhenMoveList(swipeCloseAllItemsWhenMoveList); + touchListener.setSwipeOpenOnLongPress(swipeOpenOnLongPress); + setOnTouchListener(touchListener); + setOnScrollListener(touchListener.makeScrollListener()); + } + + @Override + public void setAdapter(ListAdapter adapter) { + super.setAdapter(adapter); + touchListener.resetItems(); + adapter.registerDataSetObserver(new DataSetObserver() { + @Override + public void onChanged() { + super.onChanged(); + onListChanged(); + touchListener.resetItems(); + } + }); + } + + public void openAnimate(int position) { + touchListener.openAnimate(position); + } + + public void closeAnimate(int position) { + touchListener.closeAnimate(position); + } + + protected void onDismiss(int[] reverseSortedPositions) { + if (swipeListViewListener != null) { + swipeListViewListener.onDismiss(reverseSortedPositions); + } + } + + protected void onClickFrontView(int position) { + if (swipeListViewListener != null) { + swipeListViewListener.onClickFrontView(position); + } + } + + protected void onClickBackView(int position) { + if (swipeListViewListener != null) { + swipeListViewListener.onClickBackView(position); + } + } + + protected void onOpened(int position, boolean toRight) { + if (swipeListViewListener != null) { + swipeListViewListener.onOpened(position, toRight); + } + } + + protected void onClosed(int position, boolean fromRight) { + if (swipeListViewListener != null) { + swipeListViewListener.onClosed(position, fromRight); + } + } + + protected void onListChanged() { + if (swipeListViewListener != null) { + swipeListViewListener.onListChanged(); + } + } + + protected void onMove(int position, float x) { + if (swipeListViewListener != null) { + swipeListViewListener.onMove(position, x); + } + } + + public void setSwipeListViewListener(SwipeListViewListener swipeListViewListener) { + this.swipeListViewListener = swipeListViewListener; + } + + public void resetScrolling() { + touchState = TOUCH_STATE_REST; + } + + public void setOffsetRight(float offsetRight) { + touchListener.setOffsetRight(offsetRight); + } + + public void setOffsetLeft(float offsetLeft) { + touchListener.setOffsetLeft(offsetLeft); + } + + public void setSwipeCloseAllItemsWhenMoveList(boolean swipeCloseAllItemsWhenMoveList) { + touchListener.setSwipeCloseAllItemsWhenMoveList(swipeCloseAllItemsWhenMoveList); + } + + public void setSwipeOpenOnLongPress(boolean swipeOpenOnLongPress) { + touchListener.setSwipeOpenOnLongPress(swipeOpenOnLongPress); + } + + public void setSwipeMode(int swipeMode) { + touchListener.setSwipeMode(swipeMode); + } + + public float getSwipeActionLeft() { + return touchListener.getSwipeActionLeft(); + } + + public void setSwipeActionLeft(int swipeActionLeft) { + touchListener.setSwipeActionLeft(swipeActionLeft); + } + + public float getSwipeActionRight() { + return touchListener.getSwipeActionRight(); + } + + public void setSwipeActionRight(int swipeActionRight) { + touchListener.setSwipeActionRight(swipeActionRight); + } + + public void setAnimationTime(long animationTime) { + touchListener.setAnimationTime(animationTime); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + int action = MotionEventCompat.getActionMasked(ev); + final float x = ev.getX(); + final float y = ev.getY(); + + if (touchState == TOUCH_STATE_SCROLLING_X) { + return touchListener.onTouch(this, ev); + } + + switch (action) { + case MotionEvent.ACTION_MOVE: + checkInMovingX(x, y); + return touchState==TOUCH_STATE_SCROLLING_Y; + case MotionEvent.ACTION_DOWN: + touchListener.onTouch(this, ev); + lastMotionX = x; + lastMotionY = y; + return false; + case MotionEvent.ACTION_CANCEL: + touchState = TOUCH_STATE_REST; + break; + case MotionEvent.ACTION_UP: + touchListener.onTouch(this, ev); + touchState = TOUCH_STATE_REST; + return touchState==TOUCH_STATE_SCROLLING_Y; + default: + break; + } + + return true; + } + + private void checkInMovingX(float x, float y) { + final int xDiff = (int) Math.abs(x - lastMotionX); + final int yDiff = (int) Math.abs(y - lastMotionY); + + final int touchSlop = this.touchSlop; + boolean xMoved = xDiff > touchSlop; + boolean yMoved = yDiff > touchSlop; + + if (xMoved) { + touchState = TOUCH_STATE_SCROLLING_X; + lastMotionX = x; + lastMotionY = y; + } + + if (yMoved) { + touchState = TOUCH_STATE_SCROLLING_Y; + lastMotionX = x; + lastMotionY = y; + } + } + +} diff --git a/src/main/java/com/fortysevendeg/android/swipelistview/SwipeListViewListener.java b/src/main/java/com/fortysevendeg/android/swipelistview/SwipeListViewListener.java new file mode 100644 index 0000000..3f4efcc --- /dev/null +++ b/src/main/java/com/fortysevendeg/android/swipelistview/SwipeListViewListener.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 47 Degrees, LLC + * http://47deg.com + * hello@47deg.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.fortysevendeg.android.swipelistview; + +public interface SwipeListViewListener { + + void onOpened(int position, boolean toRight); + + void onClosed(int position, boolean fromRight); + + void onListChanged(); + + void onMove(int position, float x); + + void onClickFrontView(int position); + + void onClickBackView(int position); + + void onDismiss(int[] reverseSortedPositions); + +} diff --git a/src/main/java/com/fortysevendeg/android/swipelistview/SwipeListViewTouchListener.java b/src/main/java/com/fortysevendeg/android/swipelistview/SwipeListViewTouchListener.java new file mode 100644 index 0000000..7117577 --- /dev/null +++ b/src/main/java/com/fortysevendeg/android/swipelistview/SwipeListViewTouchListener.java @@ -0,0 +1,537 @@ +/* + * Copyright (C) 2013 47 Degrees, LLC + * http://47deg.com + * hello@47deg.com + * + * Copyright 2012 Roman Nurik + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.fortysevendeg.android.swipelistview; + +import android.graphics.Rect; +import android.view.*; +import android.widget.AbsListView; +import android.widget.ListView; +import com.nineoldandroids.animation.Animator; +import com.nineoldandroids.animation.AnimatorListenerAdapter; +import com.nineoldandroids.animation.ValueAnimator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.nineoldandroids.view.ViewHelper.setAlpha; +import static com.nineoldandroids.view.ViewHelper.setTranslationX; +import static com.nineoldandroids.view.ViewPropertyAnimator.animate; + +public class SwipeListViewTouchListener implements View.OnTouchListener { + + private int swipeMode = SwipeListView.SWIPE_MODE_BOTH; + private boolean swipeOpenOnLongPress = true; + private boolean swipeCloseAllItemsWhenMoveList = true; + + private int swipeFrontView = 0; + private int swipeBackView = 0; + + // Cached ViewConfiguration and system-wide constant values + private int slop; + private int minFlingVelocity; + private int maxFlingVelocity; + private long configShortAnimationTime; + private long animationTime; + + private float offsetLeft = 0; + private float offsetRight = 0; + + // Fixed properties + private SwipeListView swipeListView; + private int viewWidth = 1; // 1 and not 0 to prevent dividing by zero + + private List pendingDismisses = new ArrayList(); + private int dismissAnimationRefCount = 0; + + private float downX; + private boolean swiping; + private VelocityTracker velocityTracker; + private int downPosition; + private View parentView; + private View frontView; + private View backView; + private boolean paused; + + private int swipeCurrentAction = SwipeListView.SWIPE_ACTION_NONE; + + private int swipeActionLeft = SwipeListView.SWIPE_ACTION_REVEAL; + private int swipeActionRight = SwipeListView.SWIPE_ACTION_REVEAL; + + private List opened = new ArrayList(); + private List openedRight = new ArrayList(); + + public SwipeListViewTouchListener(SwipeListView swipeListView, int swipeFrontView, int swipeBackView) { + this.swipeFrontView = swipeFrontView; + this.swipeBackView = swipeBackView; + ViewConfiguration vc = ViewConfiguration.get(swipeListView.getContext()); + slop = vc.getScaledTouchSlop(); + minFlingVelocity = vc.getScaledMinimumFlingVelocity(); + maxFlingVelocity = vc.getScaledMaximumFlingVelocity(); + configShortAnimationTime = swipeListView.getContext().getResources().getInteger(android.R.integer.config_shortAnimTime); + animationTime = configShortAnimationTime; + this.swipeListView = swipeListView; + } + + private void setParentView(View parentView) { + this.parentView = parentView; + } + + private void setFrontView(View frontView) { + this.frontView = frontView; + frontView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + swipeListView.onClickFrontView(downPosition); + } + }); + if (swipeOpenOnLongPress) { + frontView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + openAnimate(downPosition); + return false; + } + }); + } + } + + private void setBackView(View backView) { + this.backView = backView; + backView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + swipeListView.onClickBackView(downPosition); + } + }); + } + + public void setAnimationTime(long animationTime) { + if (animationTime > 0) { + this.animationTime = animationTime; + } else { + this.animationTime = configShortAnimationTime; + } + } + + public void setOffsetRight(float offsetRight) { + this.offsetRight = offsetRight; + } + + public void setOffsetLeft(float offsetLeft) { + this.offsetLeft = offsetLeft; + } + + public void setSwipeCloseAllItemsWhenMoveList(boolean swipeCloseAllItemsWhenMoveList) { + this.swipeCloseAllItemsWhenMoveList = swipeCloseAllItemsWhenMoveList; + } + + public void setSwipeOpenOnLongPress(boolean swipeOpenOnLongPress) { + this.swipeOpenOnLongPress = swipeOpenOnLongPress; + } + + public void setSwipeMode(int swipeMode) { + this.swipeMode = swipeMode; + } + + public float getSwipeActionLeft() { + return swipeActionLeft; + } + + public void setSwipeActionLeft(int swipeActionLeft) { + this.swipeActionLeft = swipeActionLeft; + } + + public float getSwipeActionRight() { + return swipeActionRight; + } + + public void setSwipeActionRight(int swipeActionRight) { + this.swipeActionRight = swipeActionRight; + } + + public void resetItems() { + if (swipeListView.getAdapter() != null) { + int count = swipeListView.getAdapter().getCount(); + for (int i = opened.size(); i <= count; i++) { + opened.add(false); + openedRight.add(false); + } + } + } + + protected void openAnimate(int position) { + openAnimate(swipeListView.getChildAt(position - swipeListView.getFirstVisiblePosition()).findViewById(swipeFrontView), position); + } + + protected void closeAnimate(int position) { + closeAnimate(swipeListView.getChildAt(position - swipeListView.getFirstVisiblePosition()).findViewById(swipeFrontView), position); + } + + private void openAnimate(View view, int position) { + if (!opened.get(position)) { + generateRevealAnimate(view, true, false, position); + } + } + + private void closeAnimate(View view, int position) { + if (opened.get(position)) { + generateRevealAnimate(view, true, false, position); + } + } + + private void generateAnimate(final View view, final boolean swap, final boolean swapRight, final int position) { + if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_REVEAL) { + generateRevealAnimate(view, swap, swapRight, position); + } + if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_DISMISS) { + generateDismissAnimate(parentView, swap, swapRight, position); + } + } + + private void generateDismissAnimate(final View view, final boolean swap, final boolean swapRight, final int position) { + int moveTo = 0; + if (opened.get(position)) { + if (!swap) { + moveTo = openedRight.get(position) ? (int) (viewWidth - offsetRight) : (int) (-viewWidth + offsetLeft); + } + } else { + if (swap) { + moveTo = swapRight ? (int) (viewWidth - offsetRight) : (int) (-viewWidth + offsetLeft); + } + } + + int alpha = 1; + if (swap) { + ++dismissAnimationRefCount; + alpha = 0; + } + + animate(view) + .translationX(moveTo) + .alpha(alpha) + .setDuration(animationTime) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (swap) { + performDismiss(view, position); + } + } + }); + + } + + private void generateRevealAnimate(final View view, final boolean swap, final boolean swapRight, final int position) { + int moveTo = 0; + if (opened.get(position)) { + if (!swap) { + moveTo = openedRight.get(position) ? (int) (viewWidth - offsetRight) : (int) (-viewWidth + offsetLeft); + } + } else { + if (swap) { + moveTo = swapRight ? (int) (viewWidth - offsetRight) : (int) (-viewWidth + offsetLeft); + } + } + + animate(view) + .translationX(moveTo) + .setDuration(animationTime) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + swipeListView.resetScrolling(); + if (swap) { + boolean aux = !opened.get(position); + opened.set(position, aux); + if (aux) { + swipeListView.onOpened(position, swapRight); + openedRight.set(position, swapRight); + } else { + swipeListView.onClosed(position, openedRight.get(position)); + } + } + } + }); + } + + public void setEnabled(boolean enabled) { + paused = !enabled; + } + + public AbsListView.OnScrollListener makeScrollListener() { + return new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView absListView, int scrollState) { + setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); + if (swipeCloseAllItemsWhenMoveList && scrollState == SCROLL_STATE_TOUCH_SCROLL) { + closeItemsOpened(); + } + if (scrollState != AbsListView.OnScrollListener.SCROLL_STATE_FLING && scrollState != SCROLL_STATE_TOUCH_SCROLL) { + swipeListView.resetScrolling(); + } + } + + @Override + public void onScroll(AbsListView absListView, int i, int i1, int i2) { + } + }; + } + + private void closeItemsOpened() { + if (opened != null) { + int start = swipeListView.getFirstVisiblePosition(); + int end = swipeListView.getLastVisiblePosition(); + for (int i = start; i <= end; i++) { + if (opened.get(i)) { + closeAnimate(swipeListView.getChildAt(i - start).findViewById(swipeFrontView), i); + } + } + } + + } + + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + if (viewWidth < 2) { + viewWidth = swipeListView.getWidth(); + } + + switch (motionEvent.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + if (paused) { + return false; + } + swipeCurrentAction = SwipeListView.SWIPE_ACTION_NONE; + Rect rect = new Rect(); + int childCount = swipeListView.getChildCount(); + int[] listViewCoords = new int[2]; + swipeListView.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 = swipeListView.getChildAt(i); + child.getHitRect(rect); + if (rect.contains(x, y)) { + setParentView(child); + setFrontView(child.findViewById(swipeFrontView)); + downX = motionEvent.getRawX(); + downPosition = swipeListView.getPositionForView(child); + + frontView.setClickable(!opened.get(downPosition)); + frontView.setLongClickable(!opened.get(downPosition)); + + velocityTracker = VelocityTracker.obtain(); + velocityTracker.addMovement(motionEvent); + if (swipeBackView > 0) { + setBackView(child.findViewById(swipeBackView)); + } + break; + } + } + view.onTouchEvent(motionEvent); + return true; + } + + case MotionEvent.ACTION_UP: { + if (velocityTracker == null || !swiping) { + break; + } + + float deltaX = motionEvent.getRawX() - downX; + velocityTracker.addMovement(motionEvent); + velocityTracker.computeCurrentVelocity(1000); + float velocityX = Math.abs(velocityTracker.getXVelocity()); + if (!opened.get(downPosition)) { + if (swipeMode == SwipeListView.SWIPE_MODE_LEFT && velocityTracker.getXVelocity() > 0) { + velocityX = 0; + } + if (swipeMode == SwipeListView.SWIPE_MODE_RIGHT && velocityTracker.getXVelocity() < 0) { + velocityX = 0; + } + } + float velocityY = Math.abs(velocityTracker.getYVelocity()); + boolean swap = false; + boolean swapRight = false; + if (Math.abs(deltaX) > viewWidth / 2) { + swap = true; + swapRight = deltaX > 0; + } else if (minFlingVelocity <= velocityX && velocityX <= maxFlingVelocity && velocityY < velocityX) { + swap = true; + swapRight = velocityTracker.getXVelocity() > 0; + } + generateAnimate(frontView, swap, swapRight, downPosition); + + velocityTracker = null; + downX = 0; + // change clickable front view + if (swap) { + frontView.setClickable(opened.get(downPosition)); + frontView.setLongClickable(opened.get(downPosition)); + } + frontView = null; + backView = null; + this.downPosition = ListView.INVALID_POSITION; + swiping = false; + break; + } + + case MotionEvent.ACTION_MOVE: { + if (velocityTracker == null || paused) { + break; + } + + velocityTracker.addMovement(motionEvent); + float deltaX = motionEvent.getRawX() - downX; + float deltaMode = Math.abs(deltaX); + if (swipeMode == SwipeListView.SWIPE_MODE_NONE) { + deltaMode = 0; + } else if (swipeMode != SwipeListView.SWIPE_MODE_BOTH) { + if (opened.get(downPosition)) { + if (swipeMode == SwipeListView.SWIPE_MODE_LEFT && deltaX < 0) { + deltaMode = 0; + } else if (swipeMode == SwipeListView.SWIPE_MODE_RIGHT && deltaX > 0) { + deltaMode = 0; + } + } else { + if (swipeMode == SwipeListView.SWIPE_MODE_LEFT && deltaX > 0) { + deltaMode = 0; + } else if (swipeMode == SwipeListView.SWIPE_MODE_RIGHT && deltaX < 0) { + deltaMode = 0; + } + } + } + if (deltaMode > slop && swipeCurrentAction == SwipeListView.SWIPE_ACTION_NONE) { + swiping = true; + boolean swipingRight = (deltaX > 0); + if (opened.get(downPosition)) { + swipeCurrentAction = SwipeListView.SWIPE_ACTION_REVEAL; + } else { + if (swipingRight && swipeActionRight == SwipeListView.SWIPE_ACTION_DISMISS) { + swipeCurrentAction = SwipeListView.SWIPE_ACTION_DISMISS; + } else if (!swipingRight && swipeActionLeft == SwipeListView.SWIPE_ACTION_DISMISS) { + swipeCurrentAction = SwipeListView.SWIPE_ACTION_DISMISS; + } else if (swipingRight && swipeActionRight == SwipeListView.SWIPE_ACTION_CHECK) { + swipeCurrentAction = SwipeListView.SWIPE_ACTION_CHECK; + } else if (!swipingRight && swipeActionLeft == SwipeListView.SWIPE_ACTION_CHECK) { + swipeCurrentAction = SwipeListView.SWIPE_ACTION_CHECK; + } else { + swipeCurrentAction = SwipeListView.SWIPE_ACTION_REVEAL; + } + } + swipeListView.requestDisallowInterceptTouchEvent(true); + MotionEvent cancelEvent = MotionEvent.obtain(motionEvent); + cancelEvent.setAction(MotionEvent.ACTION_CANCEL | + (motionEvent.getActionIndex() + << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); + swipeListView.onTouchEvent(cancelEvent); + } + + if (swiping) { + if (opened.get(downPosition)) { + deltaX += openedRight.get(downPosition) ? viewWidth - offsetRight : -viewWidth + offsetLeft; + } + move(deltaX); + return true; + } + break; + } + } + return false; + } + + public void move(float deltaX) { + swipeListView.onMove(downPosition, deltaX); + if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_DISMISS) { + setTranslationX(parentView, deltaX); + setAlpha(parentView, Math.max(0f, Math.min(1f, + 1f - 2f * Math.abs(deltaX) / viewWidth))); + } else { + setTranslationX(frontView, deltaX); + } + } + + class PendingDismissData implements Comparable { + public int position; + public View view; + + public PendingDismissData(int position, View view) { + this.position = position; + this.view = view; + } + + @Override + public int compareTo(PendingDismissData other) { + // Sort by descending position + return other.position - position; + } + } + + private void performDismiss(final View dismissView, final int dismissPosition) { + final ViewGroup.LayoutParams lp = dismissView.getLayoutParams(); + final int originalHeight = dismissView.getHeight(); + + ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(animationTime); + + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + --dismissAnimationRefCount; + if (dismissAnimationRefCount == 0) { + // No active animations, process all pending dismisses. + // Sort by descending position + Collections.sort(pendingDismisses); + + int[] dismissPositions = new int[pendingDismisses.size()]; + for (int i = pendingDismisses.size() - 1; i >= 0; i--) { + dismissPositions[i] = pendingDismisses.get(i).position; + } + swipeListView.onDismiss(dismissPositions); + + ViewGroup.LayoutParams lp; + for (PendingDismissData pendingDismiss : pendingDismisses) { + // Reset view presentation + setAlpha(pendingDismiss.view, 1f); + setTranslationX(pendingDismiss.view, 0); + lp = pendingDismiss.view.getLayoutParams(); + lp.height = originalHeight; + pendingDismiss.view.setLayoutParams(lp); + } + + pendingDismisses.clear(); + } + } + }); + + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + lp.height = (Integer) valueAnimator.getAnimatedValue(); + dismissView.setLayoutParams(lp); + } + }); + + pendingDismisses.add(new PendingDismissData(dismissPosition, dismissView)); + animator.start(); + } + +}