├── .idea
├── .name
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── modules.xml
├── runConfigurations.xml
├── compiler.xml
├── gradle.xml
└── misc.xml
├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── food.png
│ │ │ │ ├── fullstar.png
│ │ │ │ ├── companylogo.png
│ │ │ │ ├── emptystar.png
│ │ │ │ ├── left_arrow.png
│ │ │ │ ├── right_arrow.png
│ │ │ │ ├── shadow_y5.9.png
│ │ │ │ ├── shadow_y10_white.9.png
│ │ │ │ ├── shape1.xml
│ │ │ │ ├── shape2.xml
│ │ │ │ ├── shape.xml
│ │ │ │ └── background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── styles.xml
│ │ │ ├── layout
│ │ │ │ ├── content_swipe_deck.xml
│ │ │ │ ├── activity_blank.xml
│ │ │ │ ├── star_rating.xml
│ │ │ │ ├── test_card.xml
│ │ │ │ ├── activity_swipe_deck.xml
│ │ │ │ └── test_card2.xml
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ └── menu
│ │ │ │ └── menu_swipe_deck.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── daprlabs
│ │ │ │ └── swipedeck
│ │ │ │ ├── BlankActivity.java
│ │ │ │ └── SwipeDeckActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── daprlabs
│ │ │ └── swipedeck
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── daprlabs
│ │ └── swipedeck
│ │ └── ApplicationTest.java
├── proguard-rules.pro
└── build.gradle
├── cardstack
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ └── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── attrs.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── daprlabs
│ │ │ └── cardstack
│ │ │ ├── SwipeCoordinatorLayout.java
│ │ │ ├── SwipeRelativeLayout.java
│ │ │ ├── SwipeLinearLayout.java
│ │ │ ├── SwipeFrameLayout.java
│ │ │ ├── SwipeListener.java
│ │ │ └── SwipeDeck.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── daprlabs
│ │ │ └── cardstack
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── daprlabs
│ │ └── cardstack
│ │ └── ApplicationTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── RELEASE.txt
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── LICENSE
├── gradlew.bat
├── gradlew
└── README.md
/.idea/.name:
--------------------------------------------------------------------------------
1 | Swipe-Deck
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/cardstack/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':cardstack'
2 |
--------------------------------------------------------------------------------
/RELEASE.txt:
--------------------------------------------------------------------------------
1 | 0.0.1
2 | Initial release.
3 | still needs a lot of optimization work, expect bugs.
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/food.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/app/src/main/res/drawable/food.png
--------------------------------------------------------------------------------
/cardstack/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Card Stack
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/fullstar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/app/src/main/res/drawable/fullstar.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/companylogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/app/src/main/res/drawable/companylogo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/emptystar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/app/src/main/res/drawable/emptystar.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/left_arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/app/src/main/res/drawable/left_arrow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/right_arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/app/src/main/res/drawable/right_arrow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shadow_y5.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/app/src/main/res/drawable/shadow_y5.9.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shadow_y10_white.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/app/src/main/res/drawable/shadow_y10_white.9.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronbond/Swipe-Deck/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/cardstack/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SwipeDeck
3 | BlankActivity
4 |
5 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Feb 07 16:01:25 AEDT 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_swipe_deck.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape1.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape2.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/daprlabs/swipedeck/BlankActivity.java:
--------------------------------------------------------------------------------
1 | package com.daprlabs.swipedeck;
2 |
3 | import android.os.Bundle;
4 | import android.app.Activity;
5 |
6 | public class BlankActivity extends Activity {
7 |
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | setContentView(R.layout.activity_blank);
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/test/java/com/daprlabs/swipedeck/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.daprlabs.swipedeck;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/cardstack/src/test/java/com/daprlabs/cardstack/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.daprlabs.cardstack;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/daprlabs/swipedeck/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.daprlabs.swipedeck;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/cardstack/src/androidTest/java/com/daprlabs/cardstack/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.daprlabs.cardstack;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_swipe_deck.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/cardstack/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
8 | #F37B61
9 | #4A4C54
10 | #72747A
11 | #f4f5f9
12 |
13 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_blank.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/cardstack/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Users\aaron\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/cardstack/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Users\aaron\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/app/src/main/res/layout/star_rating.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
11 |
15 |
19 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.2"
6 |
7 | defaultConfig {
8 | applicationId "com.daprlabs.swipedeck"
9 | minSdkVersion 14
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 |
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | }
22 |
23 | dependencies {
24 | compile fileTree(dir: 'libs', include: ['*.jar'])
25 | compile 'junit:junit:4.12'
26 | compile 'com.android.support:appcompat-v7:23.1.1'
27 | compile 'com.android.support:design:23.1.1'
28 | compile 'com.android.support:cardview-v7:23.1.1'
29 | compile 'com.squareup.picasso:picasso:2.5.2'
30 | compile project(':cardstack')
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 aaronbond
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/test_card.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
15 |
21 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
7 |
8 |
9 |
10 |
11 | -
12 |
14 |
15 |
16 |
17 |
18 |
19 |
27 |
28 |
29 |
30 |
31 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_swipe_deck.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
22 |
23 |
29 |
35 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/cardstack/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | ext {
4 | bintrayRepo = 'maven'
5 | bintrayName = 'swipedeck'
6 |
7 | publishedGroupId = 'com.daprlabs.aaron'
8 | libraryName = 'swipedeck'
9 | artifact = 'cardstack'
10 |
11 | libraryDescription = 'A Tinder Style card deck view for Android'
12 |
13 | siteUrl = 'https://github.com/aaronbond/Swipe-Deck'
14 | gitUrl = 'https://github.com/aaronbond/Swipe-Deck.git'
15 |
16 | libraryVersion = '0.3.1'
17 |
18 | developerId = 'aaronbond'
19 | developerName = 'Aaron Bond'
20 | developerEmail = 'aaron.r.bond@gmail.com'
21 |
22 | licenseName = 'The MIT License'
23 | licenseUrl = 'https://opensource.org/licenses/MIT'
24 | allLicenses = ["MIT"]
25 | }
26 |
27 | android {
28 | compileSdkVersion 23
29 | buildToolsVersion "23.0.2"
30 |
31 | repositories {
32 | maven {url "https://clojars.org/repo/"}
33 | }
34 |
35 | defaultConfig {
36 | minSdkVersion 14
37 | targetSdkVersion 23
38 | versionCode 27
39 | versionName "0.3.1"
40 | }
41 | buildTypes {
42 | release {
43 | minifyEnabled false
44 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
45 | }
46 | }
47 | }
48 |
49 | dependencies {
50 | compile fileTree(dir: 'libs', include: ['*.jar'])
51 | testCompile 'junit:junit:4.12'
52 | compile 'com.android.support:appcompat-v7:23.2.0'
53 | compile 'com.android.support:design:23.2.0'
54 | }
55 |
56 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle'
57 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle'
--------------------------------------------------------------------------------
/cardstack/src/main/java/com/daprlabs/cardstack/SwipeCoordinatorLayout.java:
--------------------------------------------------------------------------------
1 | package com.daprlabs.cardstack;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.support.design.widget.CoordinatorLayout;
7 | import android.util.AttributeSet;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 |
11 | import java.util.ArrayList;
12 |
13 | /**
14 | * Created by aaron on 23/12/2015.
15 | */
16 | public class SwipeCoordinatorLayout extends CoordinatorLayout {
17 | public SwipeCoordinatorLayout(Context context) {
18 | super(context);
19 | setClipChildren(false);
20 | }
21 |
22 | public SwipeCoordinatorLayout(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | setClipChildren(false);
25 | }
26 |
27 | public SwipeCoordinatorLayout(Context context, AttributeSet attrs, int defStyleAttr) {
28 | super(context, attrs, defStyleAttr);
29 | setClipChildren(false);
30 | }
31 |
32 | //this is so that on versions of android pre lollipop it will render the cardstack above
33 | //everything else within the layout
34 | @Override
35 | protected void onFinishInflate() {
36 | super.onFinishInflate();
37 | int childCount = getChildCount();
38 | ViewGroup.LayoutParams params = getLayoutParams();
39 |
40 | ArrayList children = new ArrayList<>();
41 | View swipeDeck = null;
42 | for(int i=0; i< childCount; ++i){
43 | View child = getChildAt(i);
44 | if(child instanceof SwipeDeck){
45 | swipeDeck = getChildAt(i);
46 | }else{
47 | children.add(child);
48 | }
49 | }
50 | removeAllViews();
51 | removeAllViewsInLayout();
52 | for(View v : children){
53 | addViewInLayout(v, -1, v.getLayoutParams(), true);
54 | }
55 | if(swipeDeck != null){
56 | addViewInLayout(swipeDeck, -1, swipeDeck.getLayoutParams(), true);
57 | }
58 | invalidate();
59 | requestLayout();
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/cardstack/src/main/java/com/daprlabs/cardstack/SwipeRelativeLayout.java:
--------------------------------------------------------------------------------
1 | package com.daprlabs.cardstack;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.RelativeLayout;
10 |
11 | import java.util.ArrayList;
12 |
13 | /**
14 | * Created by aaron on 23/12/2015.
15 | */
16 | public class SwipeRelativeLayout extends RelativeLayout {
17 | public SwipeRelativeLayout(Context context) {
18 | super(context);
19 | setClipChildren(false);
20 | }
21 |
22 | public SwipeRelativeLayout(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | setClipChildren(false);
25 | }
26 |
27 | public SwipeRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
28 | super(context, attrs, defStyleAttr);
29 | setClipChildren(false);
30 | }
31 |
32 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
33 | public SwipeRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
34 | super(context, attrs, defStyleAttr, defStyleRes);
35 | setClipChildren(false);
36 | }
37 |
38 | @Override
39 | protected void onFinishInflate() {
40 | super.onFinishInflate();
41 | int childCount = getChildCount();
42 | ViewGroup.LayoutParams params = getLayoutParams();
43 |
44 | ArrayList children = new ArrayList<>();
45 | View swipeDeck = null;
46 | for(int i=0; i< childCount; ++i){
47 | View child = getChildAt(i);
48 | if(child instanceof SwipeDeck){
49 | swipeDeck = getChildAt(i);
50 | }else{
51 | children.add(child);
52 | }
53 | }
54 | removeAllViews();
55 | removeAllViewsInLayout();
56 | for(View v : children){
57 | addViewInLayout(v, -1, v.getLayoutParams(), true);
58 | }
59 | if(swipeDeck != null){
60 | addViewInLayout(swipeDeck, -1, swipeDeck.getLayoutParams(), true);
61 | }
62 | invalidate();
63 | requestLayout();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/cardstack/src/main/java/com/daprlabs/cardstack/SwipeLinearLayout.java:
--------------------------------------------------------------------------------
1 | package com.daprlabs.cardstack;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.LinearLayout;
10 |
11 | import java.util.ArrayList;
12 |
13 | /**
14 | * Created by aaron on 23/12/2015.
15 | */
16 | public class SwipeLinearLayout extends LinearLayout {
17 | public SwipeLinearLayout(Context context) {
18 | super(context);
19 | setClipChildren(false);
20 | }
21 |
22 | public SwipeLinearLayout(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | setClipChildren(false);
25 | }
26 |
27 | public SwipeLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
28 | super(context, attrs, defStyleAttr);
29 | setClipChildren(false);
30 | }
31 |
32 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
33 | public SwipeLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
34 | super(context, attrs, defStyleAttr, defStyleRes);
35 | setClipChildren(false);
36 | }
37 |
38 | // @Override
39 | // protected void onFinishInflate() {
40 | // super.onFinishInflate();
41 | // int childCount = getChildCount();
42 | // ViewGroup.LayoutParams params = getLayoutParams();
43 | //
44 | // ArrayList children = new ArrayList<>();
45 | // View swipeDeck = null;
46 | // for(int i=0; i< childCount; ++i){
47 | // View child = getChildAt(i);
48 | // if(child instanceof SwipeDeck){
49 | // swipeDeck = getChildAt(i);
50 | // }else{
51 | // children.add(child);
52 | // }
53 | // }
54 | // removeAllViews();
55 | // removeAllViewsInLayout();
56 | // for(View v : children){
57 | // addViewInLayout(v, -1, v.getLayoutParams(), true);
58 | // }
59 | // if(swipeDeck != null){
60 | // addViewInLayout(swipeDeck, -1, swipeDeck.getLayoutParams(), true);
61 | // }
62 | // invalidate();
63 | // requestLayout();
64 | // }
65 | }
66 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/cardstack/src/main/java/com/daprlabs/cardstack/SwipeFrameLayout.java:
--------------------------------------------------------------------------------
1 | package com.daprlabs.cardstack;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.util.AttributeSet;
7 | import android.util.Log;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.FrameLayout;
11 |
12 | import java.util.ArrayList;
13 |
14 | /**
15 | * Created by aaron on 23/12/2015.
16 | */
17 | public class SwipeFrameLayout extends FrameLayout {
18 | public SwipeFrameLayout(Context context) {
19 | super(context);
20 | setClipChildren(false);
21 | }
22 |
23 | public SwipeFrameLayout(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | setClipChildren(false);
26 | }
27 |
28 | public SwipeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
29 | super(context, attrs, defStyleAttr);
30 | setClipChildren(false);
31 | }
32 |
33 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
34 | public SwipeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
35 | super(context, attrs, defStyleAttr, defStyleRes);
36 | setClipChildren(false);
37 | }
38 |
39 | //this is so that on versions of android pre lollipop it will render the cardstack above
40 | //everything else within the layout
41 | @Override
42 | protected void onFinishInflate() {
43 | super.onFinishInflate();
44 | int childCount = getChildCount();
45 | ViewGroup.LayoutParams params = getLayoutParams();
46 |
47 | ArrayList children = new ArrayList<>();
48 | View swipeDeck = null;
49 | for(int i=0; i< childCount; ++i){
50 | View child = getChildAt(i);
51 | if(child instanceof SwipeDeck){
52 | swipeDeck = getChildAt(i);
53 | }else{
54 | children.add(child);
55 | }
56 | }
57 | removeAllViews();
58 | removeAllViewsInLayout();
59 | for(View v : children){
60 | addViewInLayout(v, -1, v.getLayoutParams(), true);
61 | }
62 | if(swipeDeck != null){
63 | addViewInLayout(swipeDeck, -1, swipeDeck.getLayoutParams(), true);
64 | }
65 | invalidate();
66 | requestLayout();
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/test_card2.xml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
17 |
20 |
27 |
28 |
29 |
30 |
31 |
36 |
44 |
52 |
53 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/src/main/java/com/daprlabs/swipedeck/SwipeDeckActivity.java:
--------------------------------------------------------------------------------
1 | package com.daprlabs.swipedeck;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.util.Log;
8 | import android.view.LayoutInflater;
9 | import android.view.Menu;
10 | import android.view.MenuItem;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.BaseAdapter;
14 | import android.widget.Button;
15 | import android.widget.CursorAdapter;
16 | import android.widget.ImageView;
17 | import android.widget.TextView;
18 |
19 | import com.daprlabs.cardstack.SwipeDeck;
20 | import com.squareup.picasso.Picasso;
21 |
22 | import java.util.ArrayList;
23 | import java.util.List;
24 |
25 | public class SwipeDeckActivity extends AppCompatActivity {
26 |
27 | private static final String TAG = "MainActivity";
28 | private SwipeDeck cardStack;
29 | private Context context = this;
30 |
31 | private SwipeDeckAdapter adapter;
32 | private ArrayList testData;
33 |
34 | @Override
35 | protected void onCreate(Bundle savedInstanceState) {
36 | super.onCreate(savedInstanceState);
37 | setContentView(R.layout.activity_swipe_deck);
38 | cardStack = (SwipeDeck) findViewById(R.id.swipe_deck);
39 | cardStack.setHardwareAccelerationEnabled(true);
40 |
41 | testData = new ArrayList<>();
42 | testData.add("0");
43 | testData.add("1");
44 | testData.add("2");
45 | testData.add("3");
46 | testData.add("4");
47 |
48 | adapter = new SwipeDeckAdapter(testData, this);
49 | cardStack.setAdapter(adapter);
50 |
51 | cardStack.setEventCallback(new SwipeDeck.SwipeEventCallback() {
52 | @Override
53 | public void cardSwipedLeft(int position) {
54 | Log.i("MainActivity", "card was swiped left, position in adapter: " + position);
55 | }
56 |
57 | @Override
58 | public void cardSwipedRight(int position) {
59 | Log.i("MainActivity", "card was swiped right, position in adapter: " + position);
60 | }
61 |
62 | @Override
63 | public void cardsDepleted() {
64 | Log.i("MainActivity", "no more cards");
65 | }
66 |
67 | @Override
68 | public void cardActionDown() {
69 | Log.i(TAG, "cardActionDown");
70 | }
71 |
72 | @Override
73 | public void cardActionUp() {
74 | Log.i(TAG, "cardActionUp");
75 | }
76 |
77 | });
78 | cardStack.setLeftImage(R.id.left_image);
79 | cardStack.setRightImage(R.id.right_image);
80 |
81 | Button btn = (Button) findViewById(R.id.button);
82 | btn.setOnClickListener(new View.OnClickListener() {
83 | @Override
84 | public void onClick(View v) {
85 | cardStack.swipeTopCardLeft(180);
86 |
87 | }
88 | });
89 | Button btn2 = (Button) findViewById(R.id.button2);
90 | btn2.setOnClickListener(new View.OnClickListener() {
91 | @Override
92 | public void onClick(View v) {
93 | cardStack.swipeTopCardRight(180);
94 | }
95 | });
96 |
97 | Button btn3 = (Button) findViewById(R.id.button3);
98 | btn3.setOnClickListener(new View.OnClickListener() {
99 | @Override
100 | public void onClick(View v) {
101 | testData.add("a sample string.");
102 | // ArrayList newData = new ArrayList<>();
103 | // newData.add("some new data");
104 | // newData.add("some new data");
105 | // newData.add("some new data");
106 | // newData.add("some new data");
107 | //
108 | // SwipeDeckAdapter adapter = new SwipeDeckAdapter(newData, context);
109 | // cardStack.setAdapter(adapter);
110 | adapter.notifyDataSetChanged();
111 | }
112 | });
113 | }
114 |
115 | @Override
116 | public boolean onCreateOptionsMenu(Menu menu) {
117 | // Inflate the menu; this adds items to the action bar if it is present.
118 | getMenuInflater().inflate(R.menu.menu_swipe_deck, menu);
119 | return true;
120 | }
121 |
122 | @Override
123 | public boolean onOptionsItemSelected(MenuItem item) {
124 | // Handle action bar item clicks here. The action bar will
125 | // automatically handle clicks on the Home/Up button, so long
126 | // as you specify a parent activity in AndroidManifest.xml.
127 | int id = item.getItemId();
128 |
129 | //noinspection SimplifiableIfStatement
130 | if (id == R.id.action_settings) {
131 | return true;
132 | }
133 |
134 | return super.onOptionsItemSelected(item);
135 | }
136 |
137 | public class SwipeDeckAdapter extends BaseAdapter {
138 |
139 | private List data;
140 | private Context context;
141 |
142 | public SwipeDeckAdapter(List data, Context context) {
143 | this.data = data;
144 | this.context = context;
145 | }
146 |
147 | @Override
148 | public int getCount() {
149 | return data.size();
150 | }
151 |
152 | @Override
153 | public Object getItem(int position) {
154 | return data.get(position);
155 | }
156 |
157 | @Override
158 | public long getItemId(int position) {
159 | return position;
160 | }
161 |
162 | @Override
163 | public View getView(final int position, View convertView, ViewGroup parent) {
164 |
165 | View v = convertView;
166 | if (v == null) {
167 | LayoutInflater inflater = getLayoutInflater();
168 | // normally use a viewholder
169 | v = inflater.inflate(R.layout.test_card2, parent, false);
170 | }
171 | //((TextView) v.findViewById(R.id.textView2)).setText(data.get(position));
172 | ImageView imageView = (ImageView) v.findViewById(R.id.offer_image);
173 | Picasso.with(context).load(R.drawable.food).fit().centerCrop().into(imageView);
174 | TextView textView = (TextView) v.findViewById(R.id.sample_text);
175 | String item = (String)getItem(position);
176 | textView.setText(item);
177 |
178 | v.setOnClickListener(new View.OnClickListener() {
179 | @Override
180 | public void onClick(View v) {
181 | Log.i("Layer type: ", Integer.toString(v.getLayerType()));
182 | Log.i("Hwardware Accel type:", Integer.toString(View.LAYER_TYPE_HARDWARE));
183 | Intent i = new Intent(v.getContext(), BlankActivity.class);
184 | v.getContext().startActivity(i);
185 | }
186 | });
187 | return v;
188 | }
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/cardstack/src/main/java/com/daprlabs/cardstack/SwipeListener.java:
--------------------------------------------------------------------------------
1 | package com.daprlabs.cardstack;
2 |
3 | import android.animation.Animator;
4 | import android.util.Log;
5 | import android.view.MotionEvent;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.view.ViewPropertyAnimator;
9 | import android.view.animation.OvershootInterpolator;
10 |
11 | /**
12 | * Created by aaron on 4/12/2015.
13 | */
14 | public class SwipeListener implements View.OnTouchListener {
15 |
16 | private float ROTATION_DEGREES = 15f;
17 | float OPACITY_END = 0.33f;
18 | private float initialX;
19 | private float initialY;
20 |
21 | private int mActivePointerId;
22 | private float initialXPress;
23 | private float initialYPress;
24 | private ViewGroup parent;
25 | private float parentWidth;
26 | private int paddingLeft;
27 |
28 | private View card;
29 | SwipeCallback callback;
30 | private boolean deactivated;
31 | private View rightView;
32 | private View leftView;
33 |
34 |
35 | public SwipeListener(View card, final SwipeCallback callback, float initialX, float initialY, float rotation, float opacityEnd) {
36 | this.card = card;
37 | this.initialX = initialX;
38 | this.initialY = initialY;
39 | this.callback = callback;
40 | this.parent = (ViewGroup) card.getParent();
41 | this.parentWidth = parent.getWidth();
42 | this.ROTATION_DEGREES = rotation;
43 | this.OPACITY_END = opacityEnd;
44 | this.paddingLeft = ((ViewGroup) card.getParent()).getPaddingLeft();
45 | }
46 |
47 | public SwipeListener(View card, final SwipeCallback callback, float initialX, float initialY, float rotation, float opacityEnd, int screenWidth) {
48 | this.card = card;
49 | this.initialX = initialX;
50 | this.initialY = initialY;
51 | this.callback = callback;
52 | this.parent = (ViewGroup) card.getParent();
53 | this.parentWidth = screenWidth;
54 | this.ROTATION_DEGREES = rotation;
55 | this.OPACITY_END = opacityEnd;
56 | this.paddingLeft = ((ViewGroup) card.getParent()).getPaddingLeft();
57 | }
58 |
59 |
60 | private boolean click = true;
61 |
62 | @Override
63 | public boolean onTouch(View v, MotionEvent event) {
64 | if (deactivated) return false;
65 | switch (event.getAction() & MotionEvent.ACTION_MASK) {
66 |
67 | case MotionEvent.ACTION_DOWN:
68 | click = true;
69 | //gesture has begun
70 | float x;
71 | float y;
72 | //cancel any current animations
73 | v.clearAnimation();
74 |
75 | mActivePointerId = event.getPointerId(0);
76 |
77 | x = event.getX();
78 | y = event.getY();
79 |
80 | if(event.findPointerIndex(mActivePointerId) == 0) {
81 | callback.cardActionDown();
82 | }
83 |
84 | initialXPress = x;
85 | initialYPress = y;
86 | break;
87 |
88 | case MotionEvent.ACTION_MOVE:
89 | //gesture is in progress
90 |
91 | final int pointerIndex = event.findPointerIndex(mActivePointerId);
92 | //Log.i("pointer index: " , Integer.toString(pointerIndex));
93 | if(pointerIndex < 0 || pointerIndex > 0 ){
94 | break;
95 | }
96 |
97 | final float xMove = event.getX(pointerIndex);
98 | final float yMove = event.getY(pointerIndex);
99 |
100 | //calculate distance moved
101 | final float dx = xMove - initialXPress;
102 | final float dy = yMove - initialYPress;
103 |
104 | //throw away the move in this case as it seems to be wrong
105 | //TODO: figure out why this is the case
106 | if((int)initialXPress == 0 && (int) initialYPress == 0){
107 | //makes sure the pointer is valid
108 | break;
109 | }
110 | //calc rotation here
111 | float posX = card.getX() + dx;
112 | float posY = card.getY() + dy;
113 |
114 | //in this circumstance consider the motion a click
115 | if (Math.abs(dx + dy) > 5) click = false;
116 |
117 | card.setX(posX);
118 | card.setY(posY);
119 |
120 | //card.setRotation
121 | float distobjectX = posX - initialX;
122 | float rotation = ROTATION_DEGREES * 2.f * distobjectX / parentWidth;
123 | card.setRotation(rotation);
124 |
125 | if (rightView != null && leftView != null){
126 | //set alpha of left and right image
127 | float alpha = (((posX - paddingLeft) / (parentWidth * OPACITY_END)));
128 | //float alpha = (((posX - paddingLeft) / parentWidth) * ALPHA_MAGNITUDE );
129 | //Log.i("alpha: ", Float.toString(alpha));
130 | rightView.setAlpha(alpha);
131 | leftView.setAlpha(-alpha);
132 | }
133 |
134 | break;
135 |
136 | case MotionEvent.ACTION_UP:
137 | //gesture has finished
138 | //check to see if card has moved beyond the left or right bounds or reset
139 | //card position
140 | checkCardForEvent();
141 |
142 | if(event.findPointerIndex(mActivePointerId) == 0) {
143 | callback.cardActionUp();
144 | }
145 | //check if this is a click event and then perform a click
146 | //this is a workaround, android doesn't play well with multiple listeners
147 |
148 | if (click) v.performClick();
149 | //if(click) return false;
150 |
151 | break;
152 |
153 | default:
154 | return false;
155 | }
156 | return true;
157 | }
158 |
159 | public void checkCardForEvent() {
160 |
161 | if (cardBeyondLeftBorder()) {
162 | animateOffScreenLeft(160)
163 | .setListener(new Animator.AnimatorListener() {
164 |
165 | @Override
166 | public void onAnimationStart(Animator animation) {
167 |
168 | }
169 |
170 | @Override
171 | public void onAnimationEnd(Animator animation) {
172 |
173 | callback.cardOffScreen();
174 | }
175 |
176 | @Override
177 | public void onAnimationCancel(Animator animation) {
178 |
179 | }
180 |
181 | @Override
182 | public void onAnimationRepeat(Animator animation) {
183 | }
184 | });
185 | callback.cardSwipedLeft();
186 | this.deactivated = true;
187 | } else if (cardBeyondRightBorder()) {
188 | animateOffScreenRight(160)
189 | .setListener(new Animator.AnimatorListener() {
190 |
191 | @Override
192 | public void onAnimationStart(Animator animation) {
193 |
194 | }
195 |
196 | @Override
197 | public void onAnimationEnd(Animator animation) {
198 | callback.cardOffScreen();
199 | }
200 |
201 | @Override
202 | public void onAnimationCancel(Animator animation) {
203 |
204 | }
205 |
206 | @Override
207 | public void onAnimationRepeat(Animator animation) {
208 |
209 | }
210 | });
211 | callback.cardSwipedRight();
212 | this.deactivated = true;
213 | } else {
214 | resetCardPosition();
215 | }
216 | }
217 |
218 | private boolean cardBeyondLeftBorder() {
219 | //check if cards middle is beyond the left quarter of the screen
220 | return (card.getX() + (card.getWidth() / 2) < (parentWidth / 4.f));
221 | }
222 |
223 | private boolean cardBeyondRightBorder() {
224 | //check if card middle is beyond the right quarter of the screen
225 | return (card.getX() + (card.getWidth() / 2) > ((parentWidth / 4.f) * 3));
226 | }
227 |
228 | private ViewPropertyAnimator resetCardPosition() {
229 | if(rightView!=null)rightView.setAlpha(0);
230 | if(leftView!=null)leftView.setAlpha(0);
231 | return card.animate()
232 | .setDuration(200)
233 | .setInterpolator(new OvershootInterpolator(1.5f))
234 | .x(initialX)
235 | .y(initialY)
236 | .rotation(0);
237 | }
238 |
239 | public ViewPropertyAnimator animateOffScreenLeft(int duration) {
240 | return card.animate()
241 | .setDuration(duration)
242 | .x(-(parentWidth))
243 | .y(0)
244 | .rotation(-30);
245 | }
246 |
247 |
248 | public ViewPropertyAnimator animateOffScreenRight(int duration) {
249 | return card.animate()
250 | .setDuration(duration)
251 | .x(parentWidth * 2)
252 | .y(0)
253 | .rotation(30);
254 | }
255 |
256 | public void setRightView(View image) {
257 | this.rightView = image;
258 | }
259 |
260 | public void setLeftView(View image) {
261 | this.leftView = image;
262 | }
263 |
264 | public interface SwipeCallback {
265 | void cardSwipedLeft();
266 | void cardSwipedRight();
267 | void cardOffScreen();
268 | void cardActionDown();
269 | void cardActionUp();
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Swipe-Deck
2 | ## A Tinder style Swipeable deck view for Android
3 |
4 | ## Note
5 | I Have re written this project from the ground up as SwipeDeck2 and it is available here: https://github.com/aaronbond/SwipeDeck2
6 |
7 | I initially hacked this together rather speedily beginning at a hackathon, as a result it wasn't really as maintainable and updateable as I would have liked. After getting almost daily emails and support requests for something I considered throwaway code I decided to re write and start again. So please if you're adventurous go and try out SwipeDeck2 and help me make it suitable for a wide range of use cases.
8 |
9 | 
10 |
11 | [](http://android-arsenal.com/details/1/2970) [](https://gitter.im/aaronbond/Swipe-Deck?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
12 |
13 | ## A Message To Developers
14 |
15 | This project is still under considerable amount of development, as such i tend to tweak the API occasionally, if things change a little come and read the README or send me an issue. Please send me issues and pull requests if you need something fixed or have a feature you want and be sure to tell me if you find a bug!
16 |
17 | ## Installation
18 |
19 | In your repositories and dependencies section add these parameters:
20 |
21 | ```groovy
22 | dependencies {
23 | compile 'com.daprlabs.aaron:cardstack:0.3.1-beta0'
24 | }
25 | ```
26 | Sync Gradle and import Swipe-Deck into your project
27 |
28 | ```java
29 | import com.daprlabs.cardstack.SwipeDeck;
30 | ```
31 |
32 | ## Example
33 |
34 | Start by defining a card view, this can be made in the normal way in XML:
35 |
36 | ```xml
37 |
38 |
46 |
51 |
57 |
64 |
65 |
66 | ```
67 | You can use any type of view you like (not just a Card View) but i would recommend adding a drop shadow or border of some kind.
68 |
69 | Next Swipe Deck takes an adapter just like you're used to with other adapter views. Here's a quick sample adapter:
70 |
71 | ```java
72 | public class SwipeDeckAdapter extends BaseAdapter {
73 |
74 | private List data;
75 | private Context context;
76 |
77 | public SwipeDeckAdapter(List data, Context context) {
78 | this.data = data;
79 | this.context = context;
80 | }
81 |
82 | @Override
83 | public int getCount() {
84 | return data.size();
85 | }
86 |
87 | @Override
88 | public Object getItem(int position) {
89 | return data.get(position);
90 | }
91 |
92 | @Override
93 | public long getItemId(int position) {
94 | return position;
95 | }
96 |
97 | @Override
98 | public View getView(int position, View convertView, ViewGroup parent) {
99 |
100 | View v = convertView;
101 | if(v == null){
102 | LayoutInflater inflater = getLayoutInflater();
103 | // normally use a viewholder
104 | v = inflater.inflate(R.layout.test_card, parent, false);
105 | }
106 | ((TextView) v.findViewById(R.id.textView2)).setText(data.get(position));
107 |
108 | v.setOnClickListener(new View.OnClickListener() {
109 | @Override
110 | public void onClick(View v) {
111 | String item = (String)getItem(position);
112 | Log.i("MainActivity", item);
113 | }
114 | });
115 |
116 | return v;
117 | }
118 | }
119 |
120 | ```
121 |
122 | Now we add a swipe deck to our layout:
123 |
124 | ```xml
125 |
126 |
132 |
133 |
142 |
143 |
148 |
149 |
150 |
151 | ```
152 | I've included some modified layouts (SwipeFrameLayout, SwipeRelativeLayout etc) for ease of use, but you can use any layout you desire. However you may not get the desired outcome unless you set android:clipChildren="false" on your containing layout. If you choose not to do this cards will be clipped as they move outside their view boundary.
153 |
154 | Now we simply give our card deck an adapter and perhaps a callback from our Activity:
155 |
156 | ```java
157 | @Override
158 | protected void onCreate(Bundle savedInstanceState) {
159 | super.onCreate(savedInstanceState);
160 | setContentView(R.layout.activity_swipe_deck);
161 | cardStack = (SwipeDeck) findViewById(R.id.swipe_deck);
162 |
163 | final ArrayList testData = new ArrayList<>();
164 | testData.add("0");
165 | testData.add("1");
166 | testData.add("2");
167 | testData.add("3");
168 | testData.add("4");
169 |
170 | final SwipeDeckAdapter adapter = new SwipeDeckAdapter(testData, this);
171 | cardStack.setAdapter(adapter);
172 |
173 | cardStack.setEventCallback(new SwipeDeck.SwipeEventCallback() {
174 | @Override
175 | public void cardSwipedLeft(int position) {
176 | Log.i("MainActivity", "card was swiped left, position in adapter: " + position);
177 | }
178 |
179 | @Override
180 | public void cardSwipedRight(int position) {
181 | Log.i("MainActivity", "card was swiped right, position in adapter: " + position);
182 | }
183 |
184 | @Override
185 | public void cardsDepleted() {
186 | Log.i("MainActivity", "no more cards");
187 | }
188 | });
189 |
190 | ```
191 |
192 | # Deck XML Attributes
193 |
194 | ```xml
195 | "max_visible" - (Integer) number of cards rendered in the deck
196 |
197 | "rotation_degrees" - (Float) degree of tilt offset as the card moves left / right
198 |
199 | "card_spacing" - (Dimension) amount to offset each card on the Y axis, 0dp will put cards directly atop each other (dp, px etc)
200 |
201 | "render_above" - (Boolean) render the cards above other views in the layout
202 |
203 | "render_below" - (Boolean) render the cards below other views in the layout
204 |
205 | "opacity_end" - (Float) if using the left and right swipe image feature, range from 0 - 1,
206 | this is the point where your swipe images reach full opacity, for example 0.33 would mean
207 | full opacity when the card moves as far as 1/3 of the screen space left or right
208 |
209 | ```
210 |
211 | # Features
212 |
213 | ### Easily design cards and deck container in XML and have cards render over the top (or underneath) elements in your layout
214 |
215 | ```xml
216 |
217 |
223 |
224 |
234 |
235 |
241 |
242 |
243 |
244 | ```
245 |
246 | 
247 |
248 | ### Indicator images for swiping left and right, simply add a left and right swipe view to your card layout and register their resource
249 | id with swipe deck:
250 | ```xml
251 |
259 |
267 | ```
268 |
269 | ```java
270 | final SwipeDeckAdapter adapter = new SwipeDeckAdapter(testData, this);
271 | cardStack.setAdapter(adapter);
272 |
273 | cardStack.setLeftImage(R.id.left_image);
274 | cardStack.setRightImage(R.id.right_image);
275 | ```
276 |
277 | 
278 |
279 | ### Programatically Swipe the top card left / right:
280 |
281 | ```java
282 | Button btn = (Button) findViewById(R.id.button);
283 | btn.setOnClickListener(new View.OnClickListener() {
284 | @Override
285 | public void onClick(View v) {
286 | cardStack.swipeTopCardLeft();
287 |
288 | }
289 | });
290 | Button btn2 = (Button) findViewById(R.id.button2);
291 | btn2.setOnClickListener(new View.OnClickListener() {
292 | @Override
293 | public void onClick(View v) {
294 | cardStack.swipeTopCardRight();
295 | }
296 | });
297 | ```
298 | 
299 |
300 | ## Hardware Acceleration
301 | In a future release this will be enabled by default but for now:
302 |
303 | ```Java
304 | cardStack = (SwipeDeck) findViewById(R.id.swipe_deck);
305 | cardStack.setHardwareAccelerationEnabled(true);
306 | ```
307 |
308 | currently this just enables rendering the cards to an offscreen buffer. It works well on every device i've tested
309 | but if you run into issues please let me know.
310 |
311 | # TODO
312 | Lots of optimisation work
313 | Plenty of features left to add (let me know if you think of any)
314 |
315 | # Addendum
316 |
317 | Feel free to contact me or log an issue if there's something broken or missing. I'd be happy to fix it up. This is currently very early work and you should fully expect some issues I have yet to spot.
318 | currently requires minSdkVersion 14, i haven't tested it but should easily support all the way back to sdk 12. If for some reason people want that let me know and i'll release a version.
319 |
--------------------------------------------------------------------------------
/cardstack/src/main/java/com/daprlabs/cardstack/SwipeDeck.java:
--------------------------------------------------------------------------------
1 | package com.daprlabs.cardstack;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.database.DataSetObserver;
7 | import android.os.AsyncTask;
8 | import android.os.Build;
9 | import android.support.v4.view.ViewCompat;
10 | import android.util.AttributeSet;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.Adapter;
14 | import android.widget.FrameLayout;
15 |
16 | import java.util.ArrayList;
17 |
18 | /**
19 | * Created by aaron on 4/12/2015.
20 | */
21 | public class SwipeDeck extends FrameLayout {
22 |
23 | private static final String TAG = "SwipeDeck.java";
24 | private static int NUMBER_OF_CARDS;
25 | private float ROTATION_DEGREES;
26 | private float CARD_SPACING;
27 | private boolean RENDER_ABOVE;
28 | private boolean RENDER_BELOW;
29 | private float OPACITY_END;
30 | private int CARD_GRAVITY;
31 | private int paddingLeft;
32 | private boolean hardwareAccelerationEnabled = true;
33 |
34 | private int paddingRight;
35 | private int paddingTop;
36 | private int paddingBottom;
37 |
38 | private SwipeEventCallback eventCallback;
39 | private CardPositionCallback cardPosCallback;
40 |
41 | /**
42 | * The adapter with all the data
43 | */
44 | private Adapter mAdapter;
45 | DataSetObserver observer;
46 | int nextAdapterCard = 0;
47 | private boolean restoreInstanceState = false;
48 |
49 | private SwipeListener swipeListener;
50 | private int leftImageResource;
51 | private int rightImageResource;
52 | private boolean cardInteraction;
53 |
54 | public SwipeDeck(Context context, AttributeSet attrs) {
55 | super(context, attrs);
56 |
57 | TypedArray a = context.getTheme().obtainStyledAttributes(
58 | attrs,
59 | R.styleable.SwipeDeck,
60 | 0, 0);
61 | try {
62 | NUMBER_OF_CARDS = a.getInt(R.styleable.SwipeDeck_max_visible, 3);
63 | ROTATION_DEGREES = a.getFloat(R.styleable.SwipeDeck_rotation_degrees, 15f);
64 | CARD_SPACING = a.getDimension(R.styleable.SwipeDeck_card_spacing, 15f);
65 | RENDER_ABOVE = a.getBoolean(R.styleable.SwipeDeck_render_above, true);
66 | RENDER_BELOW = a.getBoolean(R.styleable.SwipeDeck_render_below, false);
67 | CARD_GRAVITY = a.getInt(R.styleable.SwipeDeck_card_gravity, 0);
68 | OPACITY_END = a.getFloat(R.styleable.SwipeDeck_opacity_end, 0.33f);
69 | } finally {
70 | a.recycle();
71 | }
72 |
73 | paddingBottom = getPaddingBottom();
74 | paddingLeft = getPaddingLeft();
75 | paddingRight = getPaddingRight();
76 | paddingTop = getPaddingTop();
77 |
78 | //set clipping of view parent to false so cards render outside their view boundary
79 | //make sure not to clip to padding
80 | setClipToPadding(false);
81 | setClipChildren(false);
82 |
83 | this.setWillNotDraw(false);
84 |
85 | //render the cards and card deck above or below everything
86 | if (RENDER_ABOVE) {
87 | ViewCompat.setTranslationZ(this, Float.MAX_VALUE);
88 | }
89 | if (RENDER_BELOW) {
90 | ViewCompat.setTranslationZ(this, Float.MIN_VALUE);
91 | }
92 | }
93 |
94 | /**
95 | * Set Hardware Acceleration Enabled.
96 | *
97 | * @param accel
98 | */
99 | public void setHardwareAccelerationEnabled(Boolean accel) {
100 | this.hardwareAccelerationEnabled = accel;
101 | }
102 |
103 | public void setAdapter(Adapter adapter) {
104 | if (this.mAdapter != null) {
105 | this.mAdapter.unregisterDataSetObserver(observer);
106 | }
107 | mAdapter = adapter;
108 | // if we're not restoring previous instance state
109 | if(!restoreInstanceState)nextAdapterCard = 0;
110 |
111 | observer = new DataSetObserver() {
112 | @Override
113 | public void onChanged() {
114 | super.onChanged();
115 | //handle data set changes
116 | //if we need to add any cards at this point (ie. the amount of cards on screen
117 | //is less than the max number of cards to display) add the cards.
118 | int childCount = getChildCount();
119 | //only perform action if there are less cards on screen than NUMBER_OF_CARDS
120 | if(childCount < NUMBER_OF_CARDS) {
121 | for (int i = childCount; i < NUMBER_OF_CARDS; ++i) {
122 | addNextCard();
123 | }
124 | //position the items correctly on screen
125 | for (int i = 0; i < getChildCount(); ++i) {
126 | positionItem(i);
127 | }
128 | }
129 | }
130 |
131 | @Override
132 | public void onInvalidated() {
133 | //reset state, remove views and request layout
134 | nextAdapterCard = 0;
135 | removeAllViews();
136 | requestLayout();
137 | }
138 | };
139 |
140 | adapter.registerDataSetObserver(observer);
141 | removeAllViewsInLayout();
142 | requestLayout();
143 | }
144 |
145 |
146 | public void setSelection(int position){
147 | if(position < mAdapter.getCount()){
148 | this.nextAdapterCard = position;
149 | removeAllViews();
150 | requestLayout();
151 | }
152 | }
153 |
154 | public View getSelectedView() {
155 | throw new UnsupportedOperationException("Not supported");
156 | }
157 |
158 | @Override
159 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
160 | super.onLayout(changed, left, top, right, bottom);
161 |
162 | // if we don't have an adapter, we don't need to do anything
163 | if (mAdapter == null || mAdapter.getCount() == 0) {
164 | nextAdapterCard = 0;
165 | removeAllViewsInLayout();
166 | return;
167 | }
168 |
169 | //pull in views from the adapter at the position the top of the deck is set to
170 | //stop when you get to for cards or the end of the adapter
171 | int childCount = getChildCount();
172 | for (int i = childCount; i < NUMBER_OF_CARDS; ++i) {
173 | addNextCard();
174 | }
175 | for (int i = 0; i < getChildCount(); ++i) {
176 | positionItem(i);
177 | }
178 | //position the new children we just added and set up the top card with a listener etc
179 | }
180 |
181 | private void removeTopCard() {
182 | //top card is now the last in view children
183 | int childOffset = getChildCount() - NUMBER_OF_CARDS + 1;
184 | final View child = getChildAt(getChildCount() - childOffset);
185 | if (child != null) {
186 | child.setOnTouchListener(null);
187 | swipeListener = null;
188 | //this will also check to see if cards are depleted
189 | removeViewWaitForAnimation(child);
190 | }
191 | }
192 |
193 | private void removeViewWaitForAnimation(View child) {
194 | new RemoveViewOnAnimCompleted().execute(child);
195 |
196 |
197 | }
198 |
199 | @Override
200 | public void removeView(View view) {
201 | super.removeView(view);
202 | }
203 |
204 | private void addNextCard() {
205 | if (nextAdapterCard < mAdapter.getCount()) {
206 |
207 | // TODO: Make view recycling work
208 | // TODO: Instead of removing the view from here and adding it again when it's swiped
209 | // ... don't remove and add to this instance: don't call removeView & addView in sequence.
210 | View newBottomChild = mAdapter.getView(nextAdapterCard, null/*lastRemovedView*/, this);
211 |
212 | if (hardwareAccelerationEnabled) {
213 | //set backed by an off-screen buffer
214 | newBottomChild.setLayerType(View.LAYER_TYPE_HARDWARE, null);
215 | }
216 |
217 | //set the initial Y value so card appears from under the deck
218 | //newBottomChild.setY(paddingTop);
219 | addAndMeasureChild(newBottomChild);
220 | nextAdapterCard++;
221 | }
222 | setupTopCard();
223 | }
224 |
225 |
226 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
227 | private void setZTranslations() {
228 | //this is only needed to add shadows to cardviews on > lollipop
229 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
230 | int count = getChildCount();
231 | for (int i = 0; i < count; ++i) {
232 | getChildAt(i).setTranslationZ(i * 10);
233 | }
234 | }
235 | }
236 |
237 | /**
238 | * Adds a view as a child view and takes care of measuring it
239 | *
240 | * @param child The view to add
241 | */
242 | private void addAndMeasureChild(View child) {
243 | ViewGroup.LayoutParams params = child.getLayoutParams();
244 | if (params == null) {
245 | params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
246 | }
247 |
248 | //ensure new card is under the deck at the beginning
249 | child.setY(paddingTop);
250 |
251 | //every time we add and measure a child refresh the children on screen and order them
252 | ArrayList children = new ArrayList<>();
253 | children.add(child);
254 | for (int i = 0; i < getChildCount(); ++i) {
255 | children.add(getChildAt(i));
256 | }
257 |
258 | removeAllViews();
259 |
260 | for (View c : children) {
261 | addViewInLayout(c, -1, params, true);
262 | int itemWidth = getWidth() - (paddingLeft + paddingRight);
263 | int itemHeight = getHeight() - (paddingTop + paddingBottom);
264 | c.measure(MeasureSpec.EXACTLY | itemWidth, MeasureSpec.EXACTLY | itemHeight); //MeasureSpec.UNSPECIFIED
265 |
266 | //ensure that if there's a left and right image set their alpha to 0 initially
267 | //alpha animation is handled in the swipe listener
268 | if (leftImageResource != 0) child.findViewById(leftImageResource).setAlpha(0);
269 | if (rightImageResource != 0) child.findViewById(rightImageResource).setAlpha(0);
270 | }
271 | setZTranslations();
272 | }
273 |
274 | /**
275 | * Positions the children at the "correct" positions
276 | */
277 | private void positionItem(int index) {
278 |
279 | View child = getChildAt(index);
280 |
281 | int width = child.getMeasuredWidth();
282 | int height = child.getMeasuredHeight();
283 | int left = (getWidth() - width) / 2;
284 | child.layout(left, paddingTop, left + width, paddingTop + height);
285 | //layout each child slightly above the previous child (we start with the bottom)
286 | int childCount = getChildCount();
287 | float offset = (int) (((childCount - 1) * CARD_SPACING) - (index * CARD_SPACING));
288 | //child.setY(paddingTop + offset);
289 |
290 | child.animate()
291 | .setDuration(restoreInstanceState ? 0 : 160)
292 | .y(paddingTop + offset);
293 |
294 | restoreInstanceState = false;
295 | }
296 |
297 | // @Override
298 | // protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
299 | //
300 | // int widthSize = MeasureSpec.getSize(widthMeasureSpec);
301 | // int heightSize = MeasureSpec.getSize(heightMeasureSpec);
302 | // int widthMode = MeasureSpec.getMode(widthMeasureSpec);
303 | // int heightMode = MeasureSpec.getMode(heightMeasureSpec);
304 | //
305 | // int width;
306 | // int height;
307 | //
308 | // if (widthMode == MeasureSpec.EXACTLY) {
309 | // //Must be this size
310 | // width = widthSize;
311 | // } else if (widthMode == MeasureSpec.AT_MOST) {
312 | // //Can't be bigger than...
313 | // width = widthSize;
314 | // } else {
315 | // //Be whatever you want
316 | // width = widthSize;
317 | // }
318 | //
319 | // //Measure Height
320 | // if (heightMode == MeasureSpec.EXACTLY) {
321 | // //Must be this size
322 | // height = heightSize;
323 | // } else if (heightMode == MeasureSpec.AT_MOST) {
324 | // //Can't be bigger than...
325 | // height = heightSize;
326 | // } else {
327 | // //Be whatever you want
328 | // height = heightSize;
329 | // }
330 | // setMeasuredDimension(width, height);
331 | // }
332 |
333 |
334 | private void setupTopCard() {
335 | //TODO: maybe find a better solution this is kind of hacky
336 | //if there's an extra card on screen that means the top card is still being animated
337 | //in that case setup the next card along
338 | int childOffset = getChildCount() - NUMBER_OF_CARDS + 1;
339 | final View child = getChildAt(getChildCount() - childOffset);
340 |
341 | //this calculation is to get the correct position in the adapter of the current top card
342 | //the card position on setup top card is currently always the bottom card in the view
343 | //at any given time.
344 | int initialX = paddingLeft;
345 | int initialY = paddingTop;
346 |
347 | if (child != null) {
348 | //make sure we have a card
349 | swipeListener = new SwipeListener(child, new SwipeListener.SwipeCallback() {
350 | @Override
351 | public void cardSwipedLeft() {
352 | int positionInAdapter = nextAdapterCard - getChildCount();
353 | removeTopCard();
354 | if (eventCallback != null) eventCallback.cardSwipedLeft(positionInAdapter);
355 | addNextCard();
356 | }
357 |
358 | @Override
359 | public void cardSwipedRight() {
360 | int positionInAdapter = nextAdapterCard - getChildCount();
361 | removeTopCard();
362 | if (eventCallback != null) eventCallback.cardSwipedRight(positionInAdapter);
363 | addNextCard();
364 | }
365 |
366 | @Override
367 | public void cardOffScreen() {
368 | }
369 |
370 | @Override
371 | public void cardActionDown() {
372 | if(eventCallback!=null) eventCallback.cardActionDown();
373 | cardInteraction = true;
374 | }
375 |
376 | @Override
377 | public void cardActionUp() {
378 |
379 | if(eventCallback!=null) eventCallback.cardActionUp();
380 | cardInteraction = false;
381 | }
382 |
383 | }, initialX, initialY, ROTATION_DEGREES, OPACITY_END);
384 |
385 |
386 | //if we specified these image resources, get the views and pass them to the swipe listener
387 | //for the sake of animating them
388 | View rightView = null;
389 | View leftView = null;
390 | if (!(rightImageResource == 0)) rightView = child.findViewById(rightImageResource);
391 | if (!(leftImageResource == 0)) leftView = child.findViewById(leftImageResource);
392 | swipeListener.setLeftView(leftView);
393 | swipeListener.setRightView(rightView);
394 |
395 | child.setOnTouchListener(swipeListener);
396 | }
397 | }
398 |
399 | public void setEventCallback(SwipeEventCallback eventCallback) {
400 | this.eventCallback = eventCallback;
401 | }
402 |
403 |
404 | public void swipeTopCardLeft(int duration) {
405 |
406 | int childCount = getChildCount();
407 | if (childCount > 0 && getChildCount() < (NUMBER_OF_CARDS + 1)) {
408 | swipeListener.animateOffScreenLeft(duration);
409 |
410 | int positionInAdapter = nextAdapterCard - getChildCount();
411 | removeTopCard();
412 | if (eventCallback != null) eventCallback.cardSwipedLeft(positionInAdapter);
413 | addNextCard();
414 | }
415 |
416 | }
417 |
418 | public void swipeTopCardRight(int duration) {
419 | int childCount = getChildCount();
420 | if (childCount > 0 && getChildCount() < (NUMBER_OF_CARDS + 1)) {
421 | swipeListener.animateOffScreenRight(duration);
422 |
423 | int positionInAdapter = nextAdapterCard - getChildCount();
424 | removeTopCard();
425 | if (eventCallback != null) eventCallback.cardSwipedRight(positionInAdapter);
426 | addNextCard();
427 | }
428 | }
429 |
430 | public void setPositionCallback(CardPositionCallback callback) {
431 | cardPosCallback = callback;
432 | }
433 |
434 | public void setLeftImage(int imageResource) {
435 | leftImageResource = imageResource;
436 | }
437 |
438 | public void setRightImage(int imageResource) {
439 | rightImageResource = imageResource;
440 | }
441 |
442 | public interface SwipeEventCallback {
443 | //returning the object position in the adapter
444 | void cardSwipedLeft(int position);
445 |
446 | void cardSwipedRight(int position);
447 |
448 | void cardsDepleted();
449 |
450 | void cardActionDown();
451 |
452 | void cardActionUp();
453 | }
454 |
455 | public interface CardPositionCallback {
456 | void xPos(Float x);
457 | void yPos(Float y);
458 | }
459 |
460 | private int AnimationTime = 160;
461 | private class RemoveViewOnAnimCompleted extends AsyncTask {
462 |
463 | @Override
464 | protected View doInBackground(View... params) {
465 | android.os.SystemClock.sleep(AnimationTime);
466 | return params[0];
467 | }
468 |
469 | @Override
470 | protected void onPostExecute(View view) {
471 | super.onPostExecute(view);
472 | removeView(view);
473 |
474 | //if there are no more children left after top card removal let the callback know
475 | if (getChildCount() <= 0 && eventCallback != null) {
476 | eventCallback.cardsDepleted();
477 | }
478 | }
479 | }
480 | }
481 |
482 |
483 |
--------------------------------------------------------------------------------