├── app
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ └── styles.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
│ │ ├── drawable
│ │ │ └── gradient.xml
│ │ └── layout
│ │ │ └── activity_example.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── yalantis
│ │ └── beamazingtoday
│ │ └── sample
│ │ ├── Goal.java
│ │ └── ExampleActivity.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── content_shot_to-do_dribbble.gif
├── library
├── src
│ └── main
│ │ ├── assets
│ │ ├── font.otf
│ │ └── avenir.ttf
│ │ ├── res
│ │ ├── drawable-xxxhdpi
│ │ │ ├── ic_menu.png
│ │ │ ├── button_checked.png
│ │ │ └── button_unchecked.png
│ │ ├── 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
│ │ ├── drawable-hdpi
│ │ │ ├── button_checked.png
│ │ │ └── button_unchecked.png
│ │ ├── drawable-ldpi
│ │ │ ├── button_checked.png
│ │ │ └── button_unchecked.png
│ │ ├── drawable-mdpi
│ │ │ ├── button_checked.png
│ │ │ └── button_unchecked.png
│ │ ├── drawable-xhdpi
│ │ │ ├── button_checked.png
│ │ │ └── button_unchecked.png
│ │ ├── drawable-xxhdpi
│ │ │ ├── button_checked.png
│ │ │ └── button_unchecked.png
│ │ ├── drawable
│ │ │ ├── list_item_background.xml
│ │ │ ├── background_card.xml
│ │ │ ├── header_background_rounded.xml
│ │ │ ├── recycler_view_background.xml
│ │ │ ├── selector_button_add.xml
│ │ │ ├── selector_radio_button.xml
│ │ │ ├── header_background.xml
│ │ │ └── ic_cursor_drawable.xml
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── styles.xml
│ │ │ ├── colors.xml
│ │ │ ├── attrs.xml
│ │ │ └── dimens.xml
│ │ ├── values-hdpi
│ │ │ └── dimens.xml
│ │ ├── values-mdpi
│ │ │ └── dimens.xml
│ │ ├── values-xhdpi
│ │ │ └── dimens.xml
│ │ ├── values-xxxhdpi
│ │ │ └── dimens.xml
│ │ ├── anim
│ │ │ ├── blinking_anim.xml
│ │ │ └── increasing_anim.xml
│ │ ├── values-xxhdpi
│ │ │ └── dimens.xml
│ │ └── layout
│ │ │ ├── bat_add_view.xml
│ │ │ ├── bat_edit_text.xml
│ │ │ ├── bat_recycler_view.xml
│ │ │ ├── list_item.xml
│ │ │ └── bat_header_view.xml
│ │ ├── java
│ │ └── com
│ │ │ └── yalantis
│ │ │ └── beamazingtoday
│ │ │ ├── Constant.java
│ │ │ ├── ui
│ │ │ ├── callback
│ │ │ │ ├── EditListener.java
│ │ │ │ ├── OnCursorMovedListener.java
│ │ │ │ └── BatCallback.java
│ │ │ ├── widget
│ │ │ │ ├── WatcherEditText.java
│ │ │ │ ├── AddView.java
│ │ │ │ ├── BatEditText.java
│ │ │ │ ├── BatRecyclerView.java
│ │ │ │ └── BatHeaderView.java
│ │ │ ├── adapter
│ │ │ │ └── BatAdapter.java
│ │ │ └── animator
│ │ │ │ └── BatItemAnimator.java
│ │ │ ├── listeners
│ │ │ ├── OnOutsideClickedListener.java
│ │ │ ├── MoveAnimationListener.java
│ │ │ ├── BatListener.java
│ │ │ ├── OnItemClickListener.java
│ │ │ └── AnimationListener.java
│ │ │ ├── interfaces
│ │ │ ├── BatModel.java
│ │ │ └── AnimationType.java
│ │ │ └── util
│ │ │ ├── TypefaceUtil.java
│ │ │ └── AnimationUtil.java
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── gradlew
└── README.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 |
--------------------------------------------------------------------------------
/content_shot_to-do_dribbble.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/content_shot_to-do_dribbble.gif
--------------------------------------------------------------------------------
/library/src/main/assets/font.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/assets/font.otf
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/library/src/main/assets/avenir.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/assets/avenir.ttf
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BeAmazingToday
3 |
4 |
--------------------------------------------------------------------------------
/.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/Yalantis/ToDoList/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xxxhdpi/ic_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/drawable-xxxhdpi/ic_menu.png
--------------------------------------------------------------------------------
/library/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-hdpi/button_checked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/drawable-hdpi/button_checked.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-hdpi/button_unchecked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/drawable-hdpi/button_unchecked.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-ldpi/button_checked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/drawable-ldpi/button_checked.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-ldpi/button_unchecked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/drawable-ldpi/button_unchecked.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-mdpi/button_checked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/drawable-mdpi/button_checked.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-mdpi/button_unchecked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/drawable-mdpi/button_unchecked.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xhdpi/button_checked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/drawable-xhdpi/button_checked.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xxhdpi/button_checked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/drawable-xxhdpi/button_checked.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xhdpi/button_unchecked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/drawable-xhdpi/button_unchecked.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xxhdpi/button_unchecked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/drawable-xxhdpi/button_unchecked.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xxxhdpi/button_checked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/drawable-xxxhdpi/button_checked.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xxxhdpi/button_unchecked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yalantis/ToDoList/HEAD/library/src/main/res/drawable-xxxhdpi/button_unchecked.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable/list_item_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Be awesome today
3 | Add new goal
4 | Add
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/library/src/main/res/values-hdpi/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 260dp
4 | 240dp
5 | 220dp
6 |
--------------------------------------------------------------------------------
/library/src/main/res/values-mdpi/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 240dp
4 | 220dp
5 | 200dp
6 |
--------------------------------------------------------------------------------
/library/src/main/res/values-xhdpi/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 280dp
4 | 260dp
5 | 240dp
6 |
--------------------------------------------------------------------------------
/library/src/main/res/values-xxxhdpi/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 360dp
4 | 340dp
5 | 320dp
6 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/Constant.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday;
2 |
3 | /**
4 | * Created by galata on 15.07.16.
5 | */
6 | public class Constant {
7 |
8 | public static final long ANIM_DURATION_MILLIS = 300;
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/background_card.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/ui/callback/EditListener.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.ui.callback;
2 |
3 | /**
4 | * Created by irinagalata on 11/29/16.
5 | */
6 |
7 | public interface EditListener {
8 |
9 | void onStartEdit();
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Aug 22 08:24:47 EEST 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.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/header_background_rounded.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/listeners/OnOutsideClickedListener.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.listeners;
2 |
3 | /**
4 | * Created by galata on 30.08.16.
5 | */
6 | public interface OnOutsideClickedListener {
7 |
8 | void onOutsideClicked();
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/recycler_view_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/ui/callback/OnCursorMovedListener.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.ui.callback;
2 |
3 | /**
4 | * Created by irinagalata on 11/29/16.
5 | */
6 |
7 | public interface OnCursorMovedListener {
8 |
9 | void onCursorMoved();
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/selector_button_add.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/selector_radio_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/listeners/MoveAnimationListener.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.listeners;
2 |
3 | /**
4 | * Created by galata on 25.08.16.
5 | */
6 | public interface MoveAnimationListener {
7 |
8 | void onAnimationStarted();
9 |
10 | void onAnimationFinished();
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/header_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/interfaces/BatModel.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.interfaces;
2 |
3 | /**
4 | * Created by galata on 22.08.16.
5 | */
6 | public interface BatModel {
7 |
8 | boolean isChecked();
9 |
10 | String getText();
11 |
12 | void setChecked(boolean checked);
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/listeners/BatListener.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.listeners;
2 |
3 | /**
4 | * Created by galata on 22.08.16.
5 | */
6 | public interface BatListener {
7 |
8 | void add(String string);
9 |
10 | void delete(int position);
11 |
12 | void move(int from, int to);
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/library/src/main/res/anim/blinking_anim.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/gradient.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/listeners/OnItemClickListener.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.listeners;
2 |
3 | import com.yalantis.beamazingtoday.interfaces.BatModel;
4 |
5 | /**
6 | * Created by galata on 26.08.16.
7 | */
8 | public interface OnItemClickListener {
9 |
10 | void onClick(BatModel item, int position);
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/listeners/AnimationListener.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.listeners;
2 |
3 | /**
4 | * Created by galata on 15.07.16.
5 | */
6 | public interface AnimationListener {
7 |
8 | void onIncreaseAnimationStarted();
9 |
10 | void onDecreaseAnimationStarted();
11 |
12 | void onAddAnimationStarted();
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/ic_cursor_drawable.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/library/src/main/res/values-xxhdpi/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 300dp
4 | 280dp
5 | 260dp
6 | 3dp
7 | 20dp
8 | 1dp
9 |
--------------------------------------------------------------------------------
/library/src/main/res/anim/increasing_anim.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/interfaces/AnimationType.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.interfaces;
2 |
3 | import android.support.annotation.IntDef;
4 |
5 | /**
6 | * Created by galata on 22.08.16.
7 | */
8 |
9 | @IntDef({AnimationType.ADD, AnimationType.REMOVE, AnimationType.MOVE})
10 | public @interface AnimationType {
11 |
12 | int ADD = 0;
13 |
14 | int REMOVE = 1;
15 |
16 | int MOVE = 2;
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/bat_add_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/library/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/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 /home/igalata/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 |
--------------------------------------------------------------------------------
/library/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 /home/igalata/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 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.3"
6 |
7 | defaultConfig {
8 | applicationId "com.yalantis.beamazingtoday"
9 | minSdkVersion 16
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(include: ['*.jar'], dir: 'libs')
24 | compile 'com.android.support:appcompat-v7:23.4.0'
25 | compile project(':library')
26 | }
27 |
--------------------------------------------------------------------------------
/library/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #08ADEF
8 | #BBCEE4
9 | #94DEFF
10 | #DEDEDE
11 | #00ffffff
12 | #383939
13 | #EFEFF1
14 | #96CCEB
15 | #EEEEF0
16 |
17 |
--------------------------------------------------------------------------------
/library/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yalantis/beamazingtoday/sample/Goal.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.sample;
2 |
3 | import com.yalantis.beamazingtoday.interfaces.BatModel;
4 |
5 | /**
6 | * Created by galata on 22.08.16.
7 | */
8 | public class Goal implements BatModel {
9 |
10 | private String name;
11 |
12 | private boolean isChecked;
13 |
14 | public Goal(String name) {
15 | this.name = name;
16 | }
17 |
18 | public String getName() {
19 | return name;
20 | }
21 |
22 | public void setName(String name) {
23 | this.name = name;
24 | }
25 |
26 | public void setChecked(boolean checked) {
27 | isChecked = checked;
28 | }
29 |
30 | @Override
31 | public boolean isChecked() {
32 | return isChecked;
33 | }
34 |
35 | @Override
36 | public String getText() {
37 | return getName();
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/util/TypefaceUtil.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.util;
2 |
3 | import android.content.Context;
4 | import android.graphics.Typeface;
5 |
6 | /**
7 | * Created by galata on 26.07.16.
8 | */
9 | public class TypefaceUtil {
10 |
11 | private static final String FONT_NAME = "font.otf";
12 | private static final String AVENIR_FONT_NAME = "avenir.ttf";
13 |
14 | private static Typeface sTypeface;
15 | private static Typeface sAvenirTypeface;
16 |
17 | public static Typeface getTypeface(Context context) {
18 | if (sTypeface == null) {
19 | sTypeface = Typeface.createFromAsset(context.getAssets(), FONT_NAME);
20 | }
21 |
22 | return sTypeface;
23 | }
24 |
25 | public static Typeface getAvenirTypeface(Context context) {
26 | if (sAvenirTypeface == null) {
27 | sAvenirTypeface = Typeface.createFromAsset(context.getAssets(), AVENIR_FONT_NAME);
28 | }
29 |
30 | return sAvenirTypeface;
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/ui/callback/BatCallback.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.ui.callback;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.support.v7.widget.helper.ItemTouchHelper;
5 |
6 | import com.yalantis.beamazingtoday.listeners.BatListener;
7 |
8 | /**
9 | * Created by galata on 18.07.16.
10 | */
11 | public class BatCallback extends ItemTouchHelper.SimpleCallback {
12 |
13 | private BatListener mListener;
14 |
15 | public BatCallback(BatListener listener) {
16 | super(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
17 | mListener = listener;
18 | }
19 |
20 | @Override
21 | public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
22 | return false;
23 | }
24 |
25 | @Override
26 | public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
27 | if (mListener != null) {
28 | mListener.delete(viewHolder.getAdapterPosition());
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/library/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 | 24dp
7 | 16dp
8 | 24dp
9 | 4.5dp
10 | 22dp
11 | 16dp
12 | 6dp
13 | 10dp
14 | 18sp
15 | 20sp
16 | 2dp
17 | 60dp
18 | 10dp
19 | 360dp
20 | 80dp
21 | 20dp
22 | 0.7dp
23 |
24 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'android-apt'
3 | apply plugin: 'com.jakewharton.butterknife'
4 |
5 | android {
6 | compileSdkVersion 23
7 | buildToolsVersion "23.0.3"
8 |
9 | defaultConfig {
10 | minSdkVersion 16
11 | targetSdkVersion 23
12 | versionCode 1
13 | versionName "1.0"
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 'com.android.support:appcompat-v7:23.4.0'
26 | compile 'com.android.support:recyclerview-v7:23.4.0'
27 | compile 'com.android.support:design:23.4.0'
28 |
29 | //RX
30 | compile('io.reactivex:rxandroid:1.1.0') {
31 | exclude module: 'rxjava'
32 | }
33 | compile 'io.reactivex:rxjava:1.1.0'
34 | compile 'com.jakewharton.rxbinding:rxbinding:0.4.0'
35 | compile 'com.jakewharton:butterknife:8.2.1'
36 | apt 'com.jakewharton:butterknife-compiler:8.2.1'
37 | }
38 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/bat_edit_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
26 |
27 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/ui/widget/WatcherEditText.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.ui.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.widget.EditText;
6 |
7 | import com.yalantis.beamazingtoday.ui.callback.OnCursorMovedListener;
8 |
9 | /**
10 | * Created by irinagalata on 11/29/16.
11 | */
12 |
13 | public class WatcherEditText extends EditText {
14 |
15 | private OnCursorMovedListener mListener;
16 |
17 | public WatcherEditText(Context context) {
18 | super(context);
19 | }
20 |
21 | public WatcherEditText(Context context, AttributeSet attrs) {
22 | super(context, attrs);
23 | }
24 |
25 | public WatcherEditText(Context context, AttributeSet attrs, int defStyleAttr) {
26 | super(context, attrs, defStyleAttr);
27 | }
28 |
29 | public void setCursorListener(OnCursorMovedListener listener) {
30 | mListener = listener;
31 | }
32 |
33 | @Override
34 | protected void onSelectionChanged(int selStart, int selEnd) {
35 | super.onSelectionChanged(selStart, selEnd);
36 |
37 | if (mListener != null) {
38 | mListener.onCursorMoved();
39 | }
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
25 |
26 |
27 |
28 |
36 |
37 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/bat_recycler_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
22 |
23 |
31 |
32 |
37 |
38 |
45 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
18 |
19 |
27 |
28 |
40 |
41 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/ui/widget/AddView.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.ui.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.ColorStateList;
5 | import android.support.annotation.ColorInt;
6 | import android.support.annotation.Nullable;
7 | import android.support.v7.widget.AppCompatImageView;
8 | import android.util.AttributeSet;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.animation.AnimationUtils;
12 | import android.widget.FrameLayout;
13 | import android.widget.ImageView;
14 |
15 | import com.yalantis.beamazingtoday.R2;
16 | import com.yalantis.beamazingtoday.util.AnimationUtil;
17 | import com.yalantis.beamazingtoday.R;
18 |
19 | import butterknife.BindView;
20 | import butterknife.ButterKnife;
21 |
22 | /**
23 | * Created by galata on 14.07.16.
24 | */
25 | public class AddView extends FrameLayout {
26 |
27 | private static final int START_POSITION = 90;
28 | private static final int END_POSITION = 180;
29 |
30 | @BindView(R2.id.view_horizontal)
31 | AppCompatImageView mHorizontalView;
32 | @BindView(R2.id.view_vertical)
33 | AppCompatImageView mVerticalView;
34 |
35 | public AddView(Context context) {
36 | this(context, null);
37 | }
38 |
39 | public AddView(Context context, AttributeSet attrs) {
40 | this(context, attrs, 0);
41 | }
42 |
43 | public AddView(Context context, AttributeSet attrs, int defStyleAttr) {
44 | super(context, attrs, defStyleAttr);
45 | LayoutInflater.from(context).inflate(R.layout.bat_add_view, this, true);
46 | ButterKnife.bind(this);
47 | }
48 |
49 | public void rotate(@Nullable Runnable endAction) {
50 | AnimationUtil.rotate(mHorizontalView, END_POSITION, endAction);
51 | }
52 |
53 | public void rotateBack(@Nullable Runnable endAction) {
54 | AnimationUtil.rotate(mHorizontalView, START_POSITION, endAction);
55 | }
56 |
57 | public void increase() {
58 | setVisibility(VISIBLE);
59 | setAlpha(1);
60 | mHorizontalView.setRotation(START_POSITION);
61 | startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.increasing_anim));
62 | }
63 |
64 | public void show() {
65 | AnimationUtil.show(this);
66 | }
67 |
68 | public void hide() {
69 | AnimationUtil.hide(this);
70 | }
71 |
72 | void setColor(@ColorInt int color) {
73 | ColorStateList list = ColorStateList.valueOf(color);
74 | mHorizontalView.setSupportBackgroundTintList(list);
75 | mVerticalView.setSupportBackgroundTintList(list);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/bat_header_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
18 |
19 |
28 |
29 |
36 |
37 |
50 |
51 |
59 |
60 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/util/AnimationUtil.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.util;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.view.ViewCompat;
6 | import android.support.v4.view.ViewPropertyAnimatorCompat;
7 | import android.util.DisplayMetrics;
8 | import android.util.TypedValue;
9 | import android.view.View;
10 | import android.view.animation.AnimationUtils;
11 | import android.view.inputmethod.InputMethodManager;
12 |
13 | import com.yalantis.beamazingtoday.Constant;
14 |
15 | /**
16 | * Created by galata on 15.07.16.
17 | */
18 | public class AnimationUtil {
19 |
20 | public static void show(View view) {
21 | animateAlpha(view, 1, null);
22 | }
23 |
24 | public static void hide(View view) {
25 | animateAlpha(view, 0, null);
26 | }
27 |
28 | public static void rotate(View view, int value, @Nullable Runnable endAction) {
29 | ViewPropertyAnimatorCompat animator = ViewCompat.animate(view).rotation(value);
30 |
31 | if (endAction != null) {
32 | animator.withEndAction(endAction);
33 | }
34 |
35 | animator.setDuration(Constant.ANIM_DURATION_MILLIS).start();
36 | }
37 |
38 | public static void showViews(View ... views) {
39 | for (View view : views) {
40 | show(view);
41 | }
42 | }
43 |
44 | public static void hideViews(View ... views) {
45 | for (View view : views) {
46 | hide(view);
47 | }
48 | }
49 |
50 | public static void scaleXViews(float value, View ... views) {
51 | for (View view : views) {
52 | scaleX(value, view);
53 | }
54 | }
55 |
56 | public static void scaleX(float value, View view) {
57 | ViewCompat.animate(view).scaleX(value).setDuration(Constant.ANIM_DURATION_MILLIS).start();
58 | }
59 |
60 | public static void moveX(View view, float value) {
61 | ViewCompat.animate(view).translationX(value).setDuration(Constant.ANIM_DURATION_MILLIS).start();
62 | }
63 |
64 | public static void moveX(View view, float value, Runnable endAction) {
65 | ViewCompat.animate(view).translationX(value).withEndAction(endAction).setDuration(Constant.ANIM_DURATION_MILLIS).start();
66 | }
67 |
68 | private static void animateAlpha(final View view, final float alpha, @Nullable final Runnable endAction) {
69 | if (alpha == 1) {
70 | view.setVisibility(View.VISIBLE);
71 | ViewCompat.animate(view).alpha(0);
72 | }
73 | ViewCompat.animate(view).alpha(alpha).setDuration(Constant.ANIM_DURATION_MILLIS).withEndAction(new Runnable() {
74 | @Override
75 | public void run() {
76 | if (alpha == 0) {
77 | view.setVisibility(View.INVISIBLE);
78 | }
79 |
80 | if (endAction != null) {
81 | endAction.run();
82 | }
83 | }
84 | }).start();
85 | }
86 |
87 | public static void showKeyboard(Context context, View view) {
88 | InputMethodManager manager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
89 | manager.showSoftInput(view, InputMethodManager.SHOW_FORCED);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yalantis/beamazingtoday/sample/ExampleActivity.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.sample;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.support.v7.widget.LinearLayoutManager;
6 | import android.support.v7.widget.Toolbar;
7 | import android.support.v7.widget.helper.ItemTouchHelper;
8 | import android.view.View;
9 | import android.widget.TextView;
10 | import android.widget.Toast;
11 |
12 | import com.yalantis.beamazingtoday.interfaces.AnimationType;
13 | import com.yalantis.beamazingtoday.interfaces.BatModel;
14 | import com.yalantis.beamazingtoday.listeners.BatListener;
15 | import com.yalantis.beamazingtoday.listeners.OnItemClickListener;
16 | import com.yalantis.beamazingtoday.listeners.OnOutsideClickedListener;
17 | import com.yalantis.beamazingtoday.ui.adapter.BatAdapter;
18 | import com.yalantis.beamazingtoday.ui.animator.BatItemAnimator;
19 | import com.yalantis.beamazingtoday.ui.callback.BatCallback;
20 | import com.yalantis.beamazingtoday.ui.widget.BatRecyclerView;
21 | import com.yalantis.beamazingtoday.util.TypefaceUtil;
22 |
23 | import java.util.ArrayList;
24 | import java.util.List;
25 |
26 | /**
27 | * Created by galata on 20.07.16.
28 | */
29 | public class ExampleActivity extends AppCompatActivity implements BatListener, OnItemClickListener, OnOutsideClickedListener {
30 |
31 | private BatRecyclerView mRecyclerView;
32 | private BatAdapter mAdapter;
33 | private List mGoals;
34 | private BatItemAnimator mAnimator;
35 |
36 | @Override
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_example);
40 |
41 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
42 | setSupportActionBar(toolbar);
43 | getSupportActionBar().setTitle("");
44 |
45 | ((TextView) findViewById(R.id.text_title)).setTypeface(TypefaceUtil.getAvenirTypeface(this));
46 |
47 | mRecyclerView = (BatRecyclerView) findViewById(R.id.bat_recycler_view);
48 | mAnimator = new BatItemAnimator();
49 |
50 | mRecyclerView.getView().setLayoutManager(new LinearLayoutManager(this));
51 | mRecyclerView.getView().setAdapter(mAdapter = new BatAdapter(mGoals = new ArrayList() {{
52 | add(new Goal("first"));
53 | add(new Goal("second"));
54 | add(new Goal("third"));
55 | add(new Goal("fourth"));
56 | add(new Goal("fifth"));
57 | add(new Goal("sixth"));
58 | add(new Goal("seventh"));
59 | add(new Goal("eighth"));
60 | add(new Goal("ninth"));
61 | add(new Goal("tenth"));
62 | }}, this, mAnimator).setOnItemClickListener(this).setOnOutsideClickListener(this));
63 |
64 | ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new BatCallback(this));
65 | itemTouchHelper.attachToRecyclerView(mRecyclerView.getView());
66 | mRecyclerView.getView().setItemAnimator(mAnimator);
67 | mRecyclerView.setAddItemListener(this);
68 |
69 | findViewById(R.id.root).setOnClickListener(new View.OnClickListener() {
70 | @Override
71 | public void onClick(View v) {
72 | mRecyclerView.revertAnimation();
73 | }
74 | });
75 | }
76 |
77 | @Override
78 | public void add(String string) {
79 | mGoals.add(0, new Goal(string));
80 | mAdapter.notify(AnimationType.ADD, 0);
81 | }
82 |
83 | @Override
84 | public void delete(int position) {
85 | mGoals.remove(position);
86 | mAdapter.notify(AnimationType.REMOVE, position);
87 | }
88 |
89 | @Override
90 | public void move(int from, int to) {
91 | if (from >= 0 && to >= 0) {
92 | mAnimator.setPosition(to);
93 | BatModel model = mGoals.get(from);
94 | mGoals.remove(model);
95 | mGoals.add(to, model);
96 | mAdapter.notify(AnimationType.MOVE, from, to);
97 |
98 | if (from == 0 || to == 0) {
99 | mRecyclerView.getView().scrollToPosition(Math.min(from, to));
100 | }
101 | }
102 | }
103 |
104 | @Override
105 | public void onClick(BatModel item, int position) {
106 | Toast.makeText(this, item.getText(), Toast.LENGTH_SHORT).show();
107 | }
108 |
109 | @Override
110 | public void onOutsideClicked() {
111 | mRecyclerView.revertAnimation();
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/ui/widget/BatEditText.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.ui.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.ColorStateList;
5 | import android.support.annotation.ColorInt;
6 | import android.support.annotation.DrawableRes;
7 | import android.support.annotation.StringRes;
8 | import android.support.v7.widget.AppCompatImageView;
9 | import android.util.AttributeSet;
10 | import android.view.LayoutInflater;
11 | import android.view.MotionEvent;
12 | import android.view.animation.Animation;
13 | import android.view.animation.AnimationUtils;
14 | import android.widget.EditText;
15 | import android.widget.FrameLayout;
16 | import android.widget.TextView;
17 |
18 | import com.yalantis.beamazingtoday.R;
19 | import com.yalantis.beamazingtoday.R2;
20 | import com.yalantis.beamazingtoday.ui.callback.EditListener;
21 | import com.yalantis.beamazingtoday.ui.callback.OnCursorMovedListener;
22 | import com.yalantis.beamazingtoday.util.TypefaceUtil;
23 |
24 | import java.lang.reflect.Field;
25 |
26 | import butterknife.BindView;
27 | import butterknife.ButterKnife;
28 |
29 | /**
30 | * Created by galata on 15.07.16.
31 | */
32 | public class BatEditText extends FrameLayout implements OnCursorMovedListener {
33 |
34 | @BindView(R2.id.cursor)
35 | AppCompatImageView mCursor;
36 | @BindView(R2.id.edit_text)
37 | WatcherEditText mEditText;
38 |
39 | private boolean isInEditMode;
40 | private EditListener mListener;
41 |
42 | public BatEditText(Context context) {
43 | this(context, null);
44 | }
45 |
46 | public BatEditText(Context context, AttributeSet attrs) {
47 | this(context, attrs, 0);
48 | }
49 |
50 | public BatEditText(Context context, AttributeSet attrs, int defStyleAttr) {
51 | super(context, attrs, defStyleAttr);
52 | LayoutInflater.from(context).inflate(R.layout.bat_edit_text, this, true);
53 | ButterKnife.bind(this);
54 | mEditText.setCursorListener(this);
55 | }
56 |
57 | @Override
58 | protected void onFinishInflate() {
59 | super.onFinishInflate();
60 | mEditText.setTypeface(TypefaceUtil.getTypeface(getContext()));
61 | }
62 |
63 | void setEditListener(EditListener listener) {
64 | mListener = listener;
65 | }
66 |
67 | public EditText getView() {
68 | return mEditText;
69 | }
70 |
71 | public void clear() {
72 | mEditText.getText().clear();
73 | }
74 |
75 | public void hideCursor() {
76 | mEditText.setEnabled(false);
77 | mEditText.clearFocus();
78 | mCursor.clearAnimation();
79 | mCursor.setVisibility(GONE);
80 | isInEditMode = false;
81 | }
82 |
83 | public boolean isEnabled() {
84 | return mEditText.isEnabled();
85 | }
86 |
87 | @Override
88 | public void setEnabled(boolean enabled) {
89 | mEditText.setEnabled(enabled);
90 | }
91 |
92 | public void showCursor() {
93 | isInEditMode = true;
94 | mCursor.setVisibility(VISIBLE);
95 | startBlinking();
96 | mEditText.setCursorVisible(false);
97 | }
98 |
99 | private void startBlinking() {
100 | Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.blinking_anim);
101 | mCursor.startAnimation(animation);
102 | }
103 |
104 | public String getText() {
105 | return mEditText.getText().toString();
106 | }
107 |
108 | public void focus() {
109 | mEditText.requestFocus();
110 | }
111 |
112 | public void clearFocus() {
113 | mEditText.clearFocus();
114 | }
115 |
116 | void setHintColor(@ColorInt int color) {
117 | mEditText.setHintTextColor(color);
118 | }
119 |
120 | void setTextColor(@ColorInt int color) {
121 | mEditText.setTextColor(color);
122 | }
123 |
124 | void setCursorColor(@ColorInt int color) {
125 | mCursor.setSupportBackgroundTintList(ColorStateList.valueOf(color));
126 | }
127 |
128 | void setCursorDrawable(@DrawableRes int res) {
129 | try {
130 | Field field = TextView.class.getDeclaredField("mCursorDrawableRes");
131 | field.setAccessible(true);
132 | field.set(mEditText, res);
133 | mCursor.setImageResource(res);
134 | } catch (Exception ignored) {
135 | }
136 | }
137 |
138 | void setHint(@StringRes int hint) {
139 | mEditText.setHint(hint);
140 | }
141 |
142 | void setHint(String hint) {
143 | mEditText.setHint(hint);
144 | }
145 |
146 | @Override
147 | public boolean onTouchEvent(MotionEvent event) {
148 | if (isInEditMode) {
149 | mEditText.onTouchEvent(event);
150 | onCursorMoved();
151 | } else if (mListener != null) {
152 | mListener.onStartEdit();
153 | }
154 | return true;
155 | }
156 |
157 | @Override
158 | public boolean onInterceptTouchEvent(MotionEvent ev) {
159 | return true;
160 | }
161 |
162 | @Override
163 | public void onCursorMoved() {
164 | boolean isOnStart = mEditText.getSelectionStart() == 0;
165 | if (isOnStart) {
166 | startBlinking();
167 | } else {
168 | mCursor.clearAnimation();
169 | }
170 | mEditText.setCursorVisible(!isOnStart);
171 | mCursor.setVisibility(isOnStart ? VISIBLE : INVISIBLE);
172 | }
173 |
174 | }
175 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/ui/widget/BatRecyclerView.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.ui.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.ColorStateList;
5 | import android.content.res.TypedArray;
6 | import android.support.annotation.ColorInt;
7 | import android.support.annotation.ColorRes;
8 | import android.support.annotation.DimenRes;
9 | import android.support.annotation.DrawableRes;
10 | import android.support.annotation.StringRes;
11 | import android.support.v4.content.ContextCompat;
12 | import android.support.v4.view.ViewCompat;
13 | import android.support.v7.widget.AppCompatImageView;
14 | import android.support.v7.widget.RecyclerView;
15 | import android.util.AttributeSet;
16 | import android.view.LayoutInflater;
17 | import android.widget.FrameLayout;
18 |
19 | import com.yalantis.beamazingtoday.Constant;
20 | import com.yalantis.beamazingtoday.R;
21 | import com.yalantis.beamazingtoday.R2;
22 | import com.yalantis.beamazingtoday.listeners.AnimationListener;
23 | import com.yalantis.beamazingtoday.listeners.BatListener;
24 |
25 | import butterknife.BindView;
26 | import butterknife.ButterKnife;
27 |
28 |
29 | /**
30 | * Created by galata on 15.07.16.
31 | */
32 | public class BatRecyclerView extends FrameLayout {
33 |
34 | @BindView(R2.id.recycler_view)
35 | RecyclerView mRecyclerView;
36 | @BindView(R2.id.header_view)
37 | BatHeaderView mHeaderView;
38 | @BindView(R2.id.view)
39 | AppCompatImageView mBackground;
40 |
41 | public BatRecyclerView(Context context) {
42 | this(context, null);
43 | }
44 |
45 | public BatRecyclerView(Context context, AttributeSet attrs) {
46 | this(context, attrs, 0);
47 | }
48 |
49 | public BatRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
50 | super(context, attrs, defStyleAttr);
51 | LayoutInflater.from(context).inflate(R.layout.bat_recycler_view, this, true);
52 | ButterKnife.bind(this);
53 |
54 | obtainAttributes(context, attrs);
55 | }
56 |
57 | private void obtainAttributes(Context context, AttributeSet attributeSet) {
58 | TypedArray array = context.getTheme().obtainStyledAttributes(attributeSet, R.styleable.BatRecyclerView, 0, 0);
59 |
60 | int colorBlue = getColor(R.color.colorBlue);
61 |
62 | setDividerColor(array.getColor(R.styleable.BatRecyclerView_divider_color, getColor(R.color.colorDivider)));
63 | setPlusColor(array.getColor(R.styleable.BatRecyclerView_plus_color, colorBlue));
64 | setDividerVisibility(array.getBoolean(R.styleable.BatRecyclerView_show_divider, true));
65 | setListBackgroundColor(array.getColor(R.styleable.BatRecyclerView_list_background, getColor(R.color.colorBlueLightBackground)));
66 | setHintColor(array.getColor(R.styleable.BatRecyclerView_hint_color, getColor(R.color.colorGrey)));
67 | setCursorColor(array.getColor(R.styleable.BatRecyclerView_cursor_color, colorBlue));
68 | setHint(array.getString(R.styleable.BatRecyclerView_hint));
69 |
70 | if (array.hasValue(R.styleable.BatRecyclerView_radio_button_res)) {
71 | setRadioButtonSelector(array.getResourceId(R.styleable.BatRecyclerView_radio_button_res, R.drawable.selector_radio_button));
72 | }
73 |
74 | if (array.hasValue(R.styleable.BatRecyclerView_add_button_color)) {
75 | setAddButtonColor(array.getColorStateList(R.styleable.BatRecyclerView_add_button_color));
76 | }
77 |
78 | if (array.hasValue(R.styleable.BatRecyclerView_cursor_drawable)) {
79 | setCursorDrawable(array.getResourceId(R.styleable.BatRecyclerView_cursor_drawable, R.drawable.ic_cursor_drawable));
80 | }
81 |
82 | array.recycle();
83 | }
84 |
85 | @ColorInt int getColor(@ColorRes int color) {
86 | return ContextCompat.getColor(getContext(), color);
87 | }
88 |
89 | @Override
90 | protected void onFinishInflate() {
91 | super.onFinishInflate();
92 | mHeaderView.setAnimationListener(new AnimationListenerImpl());
93 | }
94 |
95 | public void setAddItemListener(BatListener listener) {
96 | mHeaderView.setAddItemListener(listener);
97 | }
98 |
99 | public void revertAnimation() {
100 | mHeaderView.animateDecreasing();
101 | }
102 |
103 | public RecyclerView getView() {
104 | return mRecyclerView;
105 | }
106 |
107 | public void setListBackgroundColor(@ColorInt int color) {
108 | mBackground.setSupportBackgroundTintList(ColorStateList.valueOf(color));
109 | }
110 |
111 | public void setHintColor(@ColorInt int color) {
112 | mHeaderView.mEditText.setHintColor(color);
113 | }
114 |
115 | public void setTextColor(@ColorInt int color) {
116 | mHeaderView.mEditText.setTextColor(color);
117 | }
118 |
119 | @Deprecated
120 | public void setCursorColor(@ColorInt int color) {
121 | mHeaderView.mEditText.setCursorColor(color);
122 | }
123 |
124 | public void setCursorDrawable(@DrawableRes int res) {
125 | mHeaderView.mEditText.setCursorDrawable(res);
126 | }
127 |
128 | public void setDividerColor(@ColorInt int color) {
129 | mHeaderView.setDividerColor(color);
130 | }
131 |
132 | public void setDividerVisibility(boolean visible) {
133 | mHeaderView.setDividerVisibility(visible);
134 | }
135 |
136 | public void setRadioButtonColor(@ColorInt int color) {
137 | mHeaderView.setRadioButtonColor(color);
138 | }
139 |
140 | public void setRadioButtonSelector(@DrawableRes int drawable) {
141 | mHeaderView.setRadioButtonSelector(drawable);
142 | }
143 |
144 | public void setPlusColor(@ColorInt int color) {
145 | mHeaderView.setPlusColor(color);
146 | }
147 |
148 | public void setHint(@StringRes int hint) {
149 | mHeaderView.mEditText.setHint(hint);
150 | }
151 |
152 | public void setHint(String hint) {
153 | mHeaderView.mEditText.setHint(hint);
154 | }
155 |
156 | public void setAddButtonColor(@ColorInt int color) {
157 | mHeaderView.setAddButtonColor(ColorStateList.valueOf(color));
158 | }
159 |
160 | public void setAddButtonColor(ColorStateList color) {
161 | mHeaderView.setAddButtonColor(color);
162 | }
163 |
164 | private int getDimen(@DimenRes int res) {
165 | return getContext().getResources().getDimensionPixelOffset(res);
166 | }
167 |
168 | class AnimationListenerImpl implements AnimationListener {
169 | @Override
170 | public void onIncreaseAnimationStarted() {
171 | ViewCompat.animate(mRecyclerView).translationY(getDimen(R.dimen.header_translation) * 2)
172 | .setDuration(Constant.ANIM_DURATION_MILLIS).start();
173 | }
174 |
175 | @Override
176 | public void onDecreaseAnimationStarted() {
177 | ViewCompat.animate(mRecyclerView).translationY(0).setDuration(Constant.ANIM_DURATION_MILLIS).start();
178 | }
179 |
180 | @Override
181 | public void onAddAnimationStarted() {
182 | ViewCompat.animate(mRecyclerView).translationY(0).setDuration(0).start();
183 | mRecyclerView.scrollToPosition(0);
184 | }
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/ui/adapter/BatAdapter.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.ui.adapter;
2 |
3 | import android.content.Context;
4 | import android.graphics.Typeface;
5 | import android.support.annotation.ColorRes;
6 | import android.support.annotation.DrawableRes;
7 | import android.support.annotation.Nullable;
8 | import android.support.v4.content.ContextCompat;
9 | import android.support.v7.widget.AppCompatCheckBox;
10 | import android.support.v7.widget.RecyclerView;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.CompoundButton;
15 | import android.widget.TextView;
16 |
17 | import com.yalantis.beamazingtoday.R;
18 | import com.yalantis.beamazingtoday.R2;
19 | import com.yalantis.beamazingtoday.interfaces.AnimationType;
20 | import com.yalantis.beamazingtoday.interfaces.BatModel;
21 | import com.yalantis.beamazingtoday.listeners.BatListener;
22 | import com.yalantis.beamazingtoday.listeners.MoveAnimationListener;
23 | import com.yalantis.beamazingtoday.listeners.OnItemClickListener;
24 | import com.yalantis.beamazingtoday.listeners.OnOutsideClickedListener;
25 | import com.yalantis.beamazingtoday.ui.animator.BatItemAnimator;
26 | import com.yalantis.beamazingtoday.util.TypefaceUtil;
27 |
28 | import java.util.List;
29 |
30 | import butterknife.BindView;
31 | import butterknife.ButterKnife;
32 | import butterknife.OnClick;
33 |
34 | /**
35 | * Created by galata on 15.07.16.
36 | */
37 | public class BatAdapter extends RecyclerView.Adapter implements CompoundButton.OnCheckedChangeListener, MoveAnimationListener {
38 |
39 | private List mItems;
40 | private BatListener mListener;
41 | private BatItemAnimator mAnimator;
42 | private boolean mIsBusy;
43 |
44 | @DrawableRes
45 | private int mRadioButtonRes = R.drawable.selector_radio_button;
46 | @ColorRes
47 | private int mDividerColor = R.color.colorDivider;
48 | @ColorRes
49 | private int mTextColor = R.color.colorTexts;
50 | private boolean mIsDividerVisible = true;
51 | private Typeface mTypeface;
52 | private OnItemClickListener mItemClickListener;
53 | private OnOutsideClickedListener mOnOutsideClickedListener;
54 |
55 | public BatAdapter(List goals, @Nullable BatListener listener, @Nullable BatItemAnimator animator) {
56 | mItems = goals;
57 | mListener = listener;
58 | mAnimator = animator;
59 |
60 | if (mAnimator != null) {
61 | mAnimator.setListener(this);
62 | }
63 | }
64 |
65 | public BatAdapter(List goals, @Nullable BatListener listener) {
66 | mItems = goals;
67 | mListener = listener;
68 | }
69 |
70 | public BatAdapter setRadioButtonColor(@DrawableRes int drawable) {
71 | mRadioButtonRes = drawable;
72 | return this;
73 | }
74 |
75 | public BatAdapter setDividerColor(@ColorRes int color) {
76 | mDividerColor = color;
77 | mIsDividerVisible = true;
78 | return this;
79 | }
80 |
81 | public BatAdapter setOnOutsideClickListener(OnOutsideClickedListener listener) {
82 | mOnOutsideClickedListener = listener;
83 | return this;
84 | }
85 |
86 | public BatAdapter setTextColor(@ColorRes int color) {
87 | mTextColor = color;
88 | return this;
89 | }
90 |
91 | public BatAdapter setTypeface(Typeface typeface) {
92 | mTypeface = typeface;
93 | return this;
94 | }
95 |
96 | public BatAdapter setOnItemClickListener(OnItemClickListener listener) {
97 | mItemClickListener = listener;
98 | return this;
99 | }
100 |
101 | @Override
102 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
103 | return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false));
104 | }
105 |
106 | @Override
107 | public void onBindViewHolder(ViewHolder holder, int position) {
108 | BatModel model = mItems.get(position);
109 | holder.textView.setText(model.getText());
110 | setChecked(holder.radioButton, model.isChecked());
111 | holder.radioButton.setTag(model);
112 | holder.radioButton.setBackgroundResource(mRadioButtonRes);
113 | holder.divider.setBackgroundResource(mDividerColor);
114 | holder.textView.setTextColor(getColor(holder.rootView.getContext(), mTextColor));
115 | holder.textView.setTypeface(mTypeface != null ? mTypeface : TypefaceUtil.getTypeface(holder.rootView.getContext()));
116 | holder.divider.setVisibility(mIsDividerVisible ? View.VISIBLE : View.GONE);
117 | }
118 |
119 | private int getColor(Context context, @ColorRes int color) {
120 | return ContextCompat.getColor(context, color);
121 | }
122 |
123 | @Override
124 | public int getItemCount() {
125 | return mItems != null ? mItems.size() : 0;
126 | }
127 |
128 | @Override
129 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
130 | if (!mIsBusy && mListener != null) {
131 | BatModel model = (BatModel) buttonView.getTag();
132 | mListener.move(mItems.indexOf(model), isChecked ? mItems.size() - 1 : 0);
133 | model.setChecked(isChecked);
134 | } else {
135 | setChecked(buttonView, !isChecked);
136 | }
137 | }
138 |
139 | private void setChecked(CompoundButton button, boolean checked) {
140 | button.setOnCheckedChangeListener(null);
141 | button.setChecked(checked);
142 | button.setOnCheckedChangeListener(this);
143 | }
144 |
145 | public void notify(@AnimationType int animationType, int position) {
146 | notify(animationType, position, -1);
147 | }
148 |
149 | public void notify(@AnimationType int animationType, int from, int to) {
150 | switch (animationType) {
151 |
152 | case AnimationType.ADD:
153 | notifyItemInserted(from);
154 | break;
155 | case AnimationType.MOVE:
156 | notifyItemMoved(from, to);
157 | break;
158 | case AnimationType.REMOVE:
159 | notifyItemRemoved(from);
160 | break;
161 | }
162 |
163 | if (mAnimator != null) {
164 | mAnimator.setAnimationType(animationType);
165 | }
166 | }
167 |
168 | @Override
169 | public void onAnimationStarted() {
170 | mIsBusy = true;
171 | }
172 |
173 | @Override
174 | public void onAnimationFinished() {
175 | mIsBusy = false;
176 | }
177 |
178 | public class ViewHolder extends RecyclerView.ViewHolder {
179 |
180 | @BindView(R2.id.root)
181 | public View rootView;
182 | @BindView(R2.id.text_view)
183 | TextView textView;
184 | @BindView(R2.id.radio_button)
185 | AppCompatCheckBox radioButton;
186 | @BindView(R2.id.divider)
187 | View divider;
188 |
189 | public ViewHolder(View itemView) {
190 | super(itemView);
191 | ButterKnife.bind(this, itemView);
192 | }
193 |
194 | @OnClick(R2.id.clickable_view)
195 | void onCheck() {
196 | if (!mIsBusy) {
197 | radioButton.toggle();
198 | }
199 | }
200 |
201 | @OnClick({R2.id.root, R2.id.text_view})
202 | void onClick() {
203 | if (mItemClickListener != null) {
204 | BatModel item = (BatModel) radioButton.getTag();
205 | mItemClickListener.onClick(item, mItems.indexOf(item));
206 | }
207 | }
208 |
209 | @OnClick(R2.id.full_list_item)
210 | void onOutsideClicked() {
211 | if (mOnOutsideClickedListener != null) {
212 | mOnOutsideClickedListener.onOutsideClicked();
213 | }
214 | }
215 |
216 | public int getItemPosition() {
217 | return mItems.indexOf(radioButton.getTag());
218 | }
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ToDoList
2 |
3 | []()
4 | [](https://jitpack.io/#yalantis/todolist)
5 | [](https://yalantis.com/?utm_source=github)
6 |
7 |
8 |
9 | [Live DEMO on appetize.io](https://appetize.io/app/c72udvwr5h7rgrbm23y2qrbbfc)
10 |
11 | Check this [project on dribbble](https://dribbble.com/shots/2589690-Be-amazing-today)
12 |
13 | Read how we did it [on our blog](https://yalantis.com/blog/how-we-used-micro-transitions-for-smooth-android-to-do-list-animations/)
14 |
15 |
16 |
17 | ##Requirements
18 | - Android SDK 16+
19 |
20 | ##Usage
21 |
22 | Add to your root build.gradle:
23 | ```Groovy
24 | allprojects {
25 | repositories {
26 | ...
27 | maven { url "https://jitpack.io" }
28 | }
29 | }
30 | ```
31 |
32 | Add the dependency:
33 | ```Groovy
34 | dependencies {
35 | compile 'com.github.yalantis:todolist:v1.0.1'
36 | }
37 | ```
38 |
39 | ## How to use this library
40 |
41 | Add ```BatRecyclerView``` to your xml layout
42 |
43 | ```xml
44 |
45 |
52 |
53 |
59 |
60 |
68 |
69 |
70 |
71 |
79 |
80 |
81 | ```
82 |
83 | Create ```BatListener```
84 |
85 | ```java
86 | private BatListener mListener = new BatListener() {
87 | @Override
88 | public void add(String string) {
89 | mGoals.add(0, new Goal(string));
90 | mAdapter.notify(AnimationType.ADD, 0);
91 | }
92 |
93 | @Override
94 | public void delete(int position) {
95 | mGoals.remove(position);
96 | mAdapter.notify(AnimationType.REMOVE, position);
97 | }
98 |
99 | @Override
100 | public void move(int from, int to) {
101 | if (from >= 0 && to >= 0) {
102 |
103 | //if you use 'BatItemAnimator'
104 | mAnimator.setPosition(to);
105 |
106 | BatModel model = mGoals.get(from);
107 | mGoals.remove(model);
108 | mGoals.add(to, model);
109 | mAdapter.notify(AnimationType.MOVE, from, to);
110 |
111 | if (from == 0 || to == 0) {
112 | mRecyclerView.getView().scrollToPosition(Math.min(from, to));
113 | }
114 | }
115 | }
116 | };
117 | ```
118 |
119 | Create ```BatAdapter```. Pass to its constructor list of models and ```BatListener```. Note that your model should implement ```BatModel``` interface
120 |
121 | ```java
122 | mAdapter = new BatAdapter(mGoals = new ArrayList() {{
123 | add(new Goal("first"));
124 | add(new Goal("second"));
125 | add(new Goal("third"));
126 | add(new Goal("fourth"));
127 | add(new Goal("fifth"));
128 | add(new Goal("sixth"));
129 | add(new Goal("seventh"));
130 | add(new Goal("eighth"));
131 | add(new Goal("ninth"));
132 | add(new Goal("tenth"));
133 | }}, mListener);
134 |
135 | mAdapter.setOnItemClickListener(new OnItemClickListener() {
136 | @Override
137 | public void onClick(BatModel item, int position) {
138 | Toast.makeText(ExampleActivity.this, item.getText(), Toast.LENGTH_SHORT).show();
139 | }
140 | });
141 | ```
142 |
143 | Initialize your recycler view
144 |
145 | ```java
146 | mRecyclerView.getView().setLayoutManager(new LinearLayoutManager(this));
147 | mRecyclerView.getView().setAdapter(mAdapter);
148 |
149 | ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new BatCallback(this));
150 | itemTouchHelper.attachToRecyclerView(mRecyclerView.getView());
151 | mRecyclerView.setAddItemListener(mListener);
152 | ```
153 |
154 | ## Extra
155 |
156 | You can use ```BatItemAnimator``` to animate list items just like in our demo:
157 |
158 | ```java
159 | mAnimator = new BatItemAnimator();
160 | mAdapter = new BatAdapter(mGoals = new ArrayList() {{
161 | add(new Goal("first"));
162 | add(new Goal("second"));
163 | add(new Goal("third"));
164 | add(new Goal("fourth"));
165 | add(new Goal("fifth"));
166 | add(new Goal("sixth"));
167 | add(new Goal("seventh"));
168 | add(new Goal("eighth"));
169 | add(new Goal("ninth"));
170 | add(new Goal("tenth"));
171 | }}, mListener, mAnimator);
172 | mRecyclerView.getView().setItemAnimator(mAnimator);
173 | ```
174 | You need to pass the animator instance to ```BatRecyclerView``` and to ```BatAdapter```.
175 | Also it's necessary to pass the position of the moved item in ```move()``` callback:
176 |
177 | ```java
178 | @Override
179 | public void move(int from, int to) {
180 | if (from >= 0 && to >= 0) {
181 | mAnimator.setPosition(to);
182 | BatModel model = mGoals.get(from);
183 | mGoals.remove(model);
184 | mGoals.add(to, model);
185 | mAdapter.notify(AnimationType.MOVE, from, to);
186 |
187 | if (from == 0 || to == 0) {
188 | mRecyclerView.getView().scrollToPosition(Math.min(from, to));
189 | }
190 | }
191 | }
192 | ```
193 | For more usage examples please review sample app
194 |
195 | ## Changelog
196 |
197 | ### Version: 1.0.1
198 |
199 | * Cursor fixed.
200 | * ```cursor_drawable``` attribute and ```setCursorDrawable(@DrawableRes int res)``` method added.
201 | Should be used instead of ```cursor_color``` attribute and ```setCursorColor(@ColorInt int color)``` method respectively
202 |
203 | ## Let us know!
204 |
205 | We’d be really happy if you could send us links to your projects where you use our component. Just send an email to github@yalantis.com And do let us know if you have any questions or suggestion regarding the animation.
206 |
207 | P.S. We’re going to publish more awesomeness wrapped in code and a tutorial on how to make UI for iOS (Android) better than better. Stay tuned!
208 |
209 | ## License
210 |
211 | The MIT License (MIT)
212 |
213 | Copyright © 2017 Yalantis, https://yalantis.com
214 |
215 | Permission is hereby granted, free of charge, to any person obtaining a copy
216 | of this software and associated documentation files (the "Software"), to deal
217 | in the Software without restriction, including without limitation the rights
218 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
219 | copies of the Software, and to permit persons to whom the Software is
220 | furnished to do so, subject to the following conditions:
221 |
222 | The above copyright notice and this permission notice shall be included in
223 | all copies or substantial portions of the Software.
224 |
225 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
226 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
227 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
228 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
229 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
230 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
231 | THE SOFTWARE.
232 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/ui/widget/BatHeaderView.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.ui.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.ColorStateList;
5 | import android.support.annotation.ColorInt;
6 | import android.support.annotation.DimenRes;
7 | import android.support.annotation.DrawableRes;
8 | import android.support.v4.view.ViewCompat;
9 | import android.support.v7.widget.AppCompatCheckBox;
10 | import android.text.TextUtils;
11 | import android.util.AttributeSet;
12 | import android.view.LayoutInflater;
13 | import android.view.View;
14 | import android.view.animation.Animation;
15 | import android.view.animation.AnimationUtils;
16 | import android.view.animation.LinearInterpolator;
17 | import android.view.animation.OvershootInterpolator;
18 | import android.widget.Button;
19 | import android.widget.FrameLayout;
20 |
21 | import com.jakewharton.rxbinding.widget.RxTextView;
22 | import com.yalantis.beamazingtoday.Constant;
23 | import com.yalantis.beamazingtoday.R;
24 | import com.yalantis.beamazingtoday.R2;
25 | import com.yalantis.beamazingtoday.listeners.AnimationListener;
26 | import com.yalantis.beamazingtoday.listeners.BatListener;
27 | import com.yalantis.beamazingtoday.ui.callback.EditListener;
28 | import com.yalantis.beamazingtoday.util.AnimationUtil;
29 | import com.yalantis.beamazingtoday.util.TypefaceUtil;
30 |
31 | import butterknife.BindView;
32 | import butterknife.ButterKnife;
33 | import butterknife.OnClick;
34 | import rx.functions.Action1;
35 |
36 | /**
37 | * Created by galata on 15.07.16.
38 | */
39 | public class BatHeaderView extends FrameLayout implements EditListener {
40 |
41 | @BindView(R2.id.add_view)
42 | AddView mAddView;
43 | @BindView(R2.id.bat_edit_text)
44 | BatEditText mEditText;
45 | @BindView(R2.id.button_add)
46 | Button mButtonAdd;
47 | @BindView(R2.id.root)
48 | View mRoot;
49 | @BindView(R2.id.radio_button)
50 | AppCompatCheckBox mRadioButton;
51 | @BindView(R2.id.divider)
52 | View mDivider;
53 |
54 | private AnimationListener mAnimationListener;
55 | private BatListener mAddItemListener;
56 |
57 | public BatHeaderView(Context context) {
58 | this(context, null);
59 | }
60 |
61 | public BatHeaderView(Context context, AttributeSet attrs) {
62 | this(context, attrs, 0);
63 | }
64 |
65 | public BatHeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
66 | super(context, attrs, defStyleAttr);
67 | LayoutInflater.from(context).inflate(R.layout.bat_header_view, this, true);
68 | ButterKnife.bind(this);
69 | mEditText.setEditListener(this);
70 | }
71 |
72 | @Override
73 | protected void onFinishInflate() {
74 | super.onFinishInflate();
75 | RxTextView.textChanges(mEditText.getView()).subscribe(new Action1() {
76 | @Override
77 | public void call(CharSequence charSequence) {
78 | mButtonAdd.setEnabled(!TextUtils.isEmpty(charSequence));
79 | }
80 | });
81 | mButtonAdd.setTypeface(TypefaceUtil.getTypeface(getContext()));
82 | }
83 |
84 | @OnClick({R2.id.root, R2.id.add_view})
85 | void onClick() {
86 | if (mEditText.isEnabled()) {
87 | AnimationUtil.showKeyboard(getContext(), mEditText.getView());
88 | } else {
89 | animateIncreasing();
90 | }
91 | }
92 |
93 | void animateIncreasing() {
94 | mEditText.clear();
95 | mRoot.setBackgroundResource(R.drawable.header_background_rounded);
96 | ViewCompat.animate(this).scaleX(1.1f).translationY(getDimen(R.dimen.header_translation))
97 | .setDuration(Constant.ANIM_DURATION_MILLIS).start();
98 | AnimationUtil.hide(mDivider);
99 | AnimationUtil.scaleXViews(0.9f, mButtonAdd, mEditText);
100 | AnimationUtil.moveX(mEditText, -mAddView.getWidth() - getDimen(R.dimen.cursor_width) / 2, new Runnable() {
101 | @Override
102 | public void run() {
103 | AnimationUtil.showViews(mButtonAdd);
104 | mEditText.focus();
105 | }
106 | });
107 | mAddView.rotate(new Runnable() {
108 | @Override
109 | public void run() {
110 | mEditText.showCursor();
111 | mAddView.hide();
112 | mEditText.setEnabled(true);
113 | AnimationUtil.showKeyboard(getContext(), mEditText.getView());
114 | }
115 | });
116 |
117 | if (mAnimationListener != null) {
118 | mAnimationListener.onIncreaseAnimationStarted();
119 | }
120 | }
121 |
122 | void animateDecreasing() {
123 | mEditText.setEnabled(false);
124 | mEditText.clearFocus();
125 | mEditText.clear();
126 | mAddView.show();
127 | AnimationUtil.moveX(mEditText, 0);
128 | AnimationUtil.hideViews(mButtonAdd);
129 | mEditText.hideCursor();
130 | ViewCompat.animate(this).scaleX(1f).translationY(0).setDuration(Constant.ANIM_DURATION_MILLIS).start();
131 | AnimationUtil.scaleXViews(1, mButtonAdd, mEditText);
132 |
133 | if (mAnimationListener != null) {
134 | mAnimationListener.onDecreaseAnimationStarted();
135 | }
136 | mAddView.rotateBack(new Runnable() {
137 | @Override
138 | public void run() {
139 | mRoot.setBackgroundResource(R.drawable.header_background);
140 | mRoot.requestFocus();
141 | }
142 | });
143 | }
144 |
145 | void animateAppearance() {
146 | mButtonAdd.setVisibility(INVISIBLE);
147 | mRoot.setBackgroundResource(R.drawable.header_background);
148 | mEditText.setVisibility(GONE);
149 | mEditText.clear();
150 | mEditText.clearFocus();
151 | mEditText.setEnabled(false);
152 | mEditText.hideCursor();
153 | mEditText.setTranslationX(0);
154 | setScaleY(0);
155 | setPivotY(0);
156 | setPivotY(getY() + getHeight());
157 | setTranslationY(getDimen(R.dimen.header_translation));
158 | setAlpha(1);
159 |
160 | ViewCompat.animate(this).scaleY(1.1f).setInterpolator(new OvershootInterpolator(2)).setDuration(Constant.ANIM_DURATION_MILLIS)
161 | .withEndAction(new Runnable() {
162 | @Override
163 | public void run() {
164 | setTranslationY(0);
165 | AnimationUtil.show(mDivider);
166 | setScaleY(1);
167 | mAddView.increase();
168 | AnimationUtil.show(mEditText);
169 | }
170 | }).start();
171 | }
172 |
173 | @OnClick(R2.id.button_add)
174 | void addItem() {
175 | if (mAddItemListener != null) {
176 | mAddItemListener.add(mEditText.getText());
177 | }
178 |
179 | AnimationUtil.scaleXViews(1, mButtonAdd, mEditText);
180 | bringToFront();
181 |
182 | AnimationUtil.hide(mButtonAdd);
183 | mEditText.hideCursor();
184 | setTranslationY(0);
185 | if (mAnimationListener != null) {
186 | mAnimationListener.onAddAnimationStarted();
187 | }
188 | ViewCompat.animate(mEditText).translationX(-getDimen(R.dimen.edit_text_vertical_offset))
189 | .setDuration(Constant.ANIM_DURATION_MILLIS).start();
190 | ViewCompat.animate(this).scaleX(1).translationY(getHeight() - getDimen(R.dimen.edit_text_offset))
191 | .setInterpolator(new LinearInterpolator())
192 | .setDuration(Constant.ANIM_DURATION_MILLIS).withEndAction(new Runnable() {
193 | @Override
194 | public void run() {
195 | Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.increasing_anim);
196 | animation.setAnimationListener(new Animation.AnimationListener() {
197 | @Override
198 | public void onAnimationStart(Animation animation) {
199 | AnimationUtil.scaleXViews(1, mButtonAdd, mEditText);
200 | mRadioButton.setVisibility(VISIBLE);
201 | }
202 |
203 | @Override
204 | public void onAnimationEnd(Animation animation) {
205 | setAlpha(0);
206 | setTranslationY(0);
207 | mRadioButton.setVisibility(INVISIBLE);
208 | animateAppearance();
209 | }
210 |
211 | @Override
212 | public void onAnimationRepeat(Animation animation) {
213 |
214 | }
215 | });
216 | mRadioButton.startAnimation(animation);
217 | }
218 | }).start();
219 | }
220 |
221 | private int getDimen(@DimenRes int res) {
222 | return getContext().getResources().getDimensionPixelOffset(res);
223 | }
224 |
225 | void setAnimationListener(AnimationListener animationListener) {
226 | mAnimationListener = animationListener;
227 | }
228 |
229 | void setAddItemListener(BatListener addItemListener) {
230 | mAddItemListener = addItemListener;
231 | }
232 |
233 | void setDividerVisibility(boolean visible) {
234 | mDivider.setVisibility(visible ? VISIBLE : GONE);
235 | }
236 |
237 | void setDividerColor(@ColorInt int color) {
238 | mDivider.setBackgroundColor(color);
239 | }
240 |
241 | void setPlusColor(@ColorInt int color) {
242 | mAddView.setColor(color);
243 | }
244 |
245 | void setRadioButtonColor(@ColorInt int color) {
246 | mRadioButton.setSupportButtonTintList(ColorStateList.valueOf(color));
247 | }
248 |
249 | void setRadioButtonSelector(@DrawableRes int drawable) {
250 | mRadioButton.setBackgroundResource(drawable);
251 | }
252 |
253 | void setAddButtonColor(ColorStateList list) {
254 | mButtonAdd.setTextColor(list);
255 | }
256 |
257 | @Override
258 | public void onStartEdit() {
259 | animateIncreasing();
260 | }
261 |
262 | }
263 |
--------------------------------------------------------------------------------
/library/src/main/java/com/yalantis/beamazingtoday/ui/animator/BatItemAnimator.java:
--------------------------------------------------------------------------------
1 | package com.yalantis.beamazingtoday.ui.animator;
2 |
3 | import android.support.v4.view.ViewCompat;
4 | import android.support.v4.view.ViewPropertyAnimatorCompat;
5 | import android.support.v4.view.ViewPropertyAnimatorListener;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.support.v7.widget.SimpleItemAnimator;
8 | import android.view.View;
9 |
10 | import com.yalantis.beamazingtoday.Constant;
11 | import com.yalantis.beamazingtoday.R;
12 | import com.yalantis.beamazingtoday.interfaces.AnimationType;
13 | import com.yalantis.beamazingtoday.listeners.MoveAnimationListener;
14 | import com.yalantis.beamazingtoday.ui.adapter.BatAdapter;
15 |
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | /**
20 | * Created by galata on 28.06.16.
21 | */
22 | public class BatItemAnimator extends SimpleItemAnimator {
23 | private static final long ANIM_DURATION = 250;
24 | private List mPendingRemovals = new ArrayList<>();
25 | private List mPendingAdditions = new ArrayList<>();
26 | private List mPendingMoves = new ArrayList<>();
27 | private List mPendingChanges = new ArrayList<>();
28 | private List> mAdditionsList = new ArrayList<>();
29 | private List> mMovesList = new ArrayList<>();
30 | private List> mChangesList = new ArrayList<>();
31 | private List mAddAnimations = new ArrayList<>();
32 | private List mMoveAnimations = new ArrayList<>();
33 | private List mRemoveAnimations = new ArrayList<>();
34 | private List mChangeAnimations = new ArrayList<>();
35 |
36 | private int mPosition = -1;
37 | @AnimationType
38 | private int mAnimationType;
39 | private MoveAnimationListener mListener;
40 |
41 | public void setListener(MoveAnimationListener listener) {
42 | mListener = listener;
43 | }
44 |
45 | private void animateAddImpl(final BatAdapter.ViewHolder holder) {
46 | final View view = holder.itemView;
47 | mAddAnimations.add(holder);
48 | view.setAlpha(0);
49 |
50 | final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
51 | animation.setDuration(ANIM_DURATION).
52 | setListener(new ViewPropertyAnimatorListener() {
53 | @Override
54 | public void onAnimationStart(View view) {
55 | dispatchAddStarting(holder);
56 | }
57 |
58 | @Override
59 | public void onAnimationCancel(View view) {
60 | ViewCompat.setAlpha(view, 1);
61 | }
62 |
63 | @Override
64 | public void onAnimationEnd(View view) {
65 | view.setAlpha(1);
66 | animation.setListener(null);
67 | dispatchAddFinished(holder);
68 | mAddAnimations.remove(holder);
69 | dispatchFinishedWhenDone();
70 | mListener.onAnimationFinished();
71 | }
72 | }).start();
73 | }
74 |
75 | private void animateMoveImpl(final BatAdapter.ViewHolder holder) {
76 | final View view = holder.itemView;
77 | final boolean isMainView = isMainListItem(holder.getItemPosition());
78 |
79 | if (isMainView) {
80 | holder.rootView.setBackgroundResource(R.drawable.header_background_rounded);
81 | mListener.onAnimationStarted();
82 | }
83 |
84 | ViewCompat.animate(view).scaleX(isMainView ? 1.05f : 1).scaleY(isMainView ? 1.05f : 1)
85 | .setDuration(mAnimationType == AnimationType.MOVE ? Constant.ANIM_DURATION_MILLIS : 0)
86 | .withEndAction(new Runnable() {
87 | @Override
88 | public void run() {
89 | ViewCompat.animate(view).translationX(0).translationY(0).setDuration(Constant.ANIM_DURATION_MILLIS)
90 | .withEndAction(new Runnable() {
91 | @Override
92 | public void run() {
93 | ViewCompat.animate(view).scaleX(1).scaleY(1).start();
94 | if (isMainListItem(holder.getItemPosition())) {
95 | holder.rootView.setBackgroundResource(R.drawable.list_item_background);
96 | mPosition = -1;
97 | }
98 | dispatchMoveFinished(holder);
99 | mMoveAnimations.remove(holder);
100 | dispatchFinishedWhenDone();
101 | mListener.onAnimationFinished();
102 | }
103 | });
104 | }
105 | });
106 | }
107 |
108 | private boolean isMainListItem(int itemPosition) {
109 | return mPosition == itemPosition;
110 | }
111 |
112 | public void setPosition(int position) {
113 | mPosition = position;
114 | }
115 |
116 | public void setAnimationType(@AnimationType int animationType) {
117 | mAnimationType = animationType;
118 | }
119 |
120 | @Override
121 | public void runPendingAnimations() {
122 | boolean removalsPending = !mPendingRemovals.isEmpty();
123 | boolean movesPending = !mPendingMoves.isEmpty();
124 | boolean changesPending = !mPendingChanges.isEmpty();
125 | boolean additionsPending = !mPendingAdditions.isEmpty();
126 | if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
127 | // nothing to animate
128 | return;
129 | }
130 | mPendingRemovals.clear();
131 | // Next, move stuff
132 | if (movesPending) {
133 | final ArrayList moves = new ArrayList<>();
134 | moves.addAll(mPendingMoves);
135 | mMovesList.add(moves);
136 | mPendingMoves.clear();
137 | Runnable mover = new Runnable() {
138 | @Override
139 | public void run() {
140 | for (MoveInfo moveInfo : moves) {
141 | animateMoveImpl((BatAdapter.ViewHolder) moveInfo.holder);
142 | }
143 | moves.clear();
144 | mMovesList.remove(moves);
145 | }
146 | };
147 | if (removalsPending) {
148 | View view = moves.get(0).holder.itemView;
149 | ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
150 | } else {
151 | mover.run();
152 | }
153 | }
154 | // Next, change stuff, to run in parallel with move animations
155 | if (changesPending) {
156 | final ArrayList changes = new ArrayList<>();
157 | changes.addAll(mPendingChanges);
158 | mChangesList.add(changes);
159 | mPendingChanges.clear();
160 | Runnable changer = new Runnable() {
161 | @Override
162 | public void run() {
163 | for (ChangeInfo change : changes) {
164 | animateChangeImpl(change);
165 | }
166 | changes.clear();
167 | mChangesList.remove(changes);
168 | }
169 | };
170 | if (removalsPending) {
171 | RecyclerView.ViewHolder holder = changes.get(0).oldHolder;
172 | ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
173 | } else {
174 | changer.run();
175 | }
176 | }
177 | // Next, add stuff
178 | if (additionsPending) {
179 | final ArrayList additions = new ArrayList<>();
180 | additions.addAll(mPendingAdditions);
181 | mAdditionsList.add(additions);
182 | mPendingAdditions.clear();
183 | Runnable adder = new Runnable() {
184 | public void run() {
185 | for (RecyclerView.ViewHolder holder : additions) {
186 | animateAddImpl((BatAdapter.ViewHolder) holder);
187 | }
188 | additions.clear();
189 | mAdditionsList.remove(additions);
190 | }
191 | };
192 | if (removalsPending || movesPending || changesPending) {
193 | long removeDuration = removalsPending ? getRemoveDuration() : 0;
194 | long moveDuration = movesPending ? getMoveDuration() : 0;
195 | long changeDuration = changesPending ? getChangeDuration() : 0;
196 | long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
197 | View view = additions.get(0).itemView;
198 | ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
199 | } else {
200 | adder.run();
201 | }
202 | }
203 | }
204 |
205 | @Override
206 | public boolean animateRemove(final RecyclerView.ViewHolder holder) {
207 | endAnimation(holder);
208 | mPendingRemovals.add(holder);
209 | return true;
210 | }
211 |
212 | @Override
213 | public boolean animateAdd(final RecyclerView.ViewHolder holder) {
214 | endAnimation(holder);
215 | ViewCompat.setAlpha(holder.itemView, 0);
216 |
217 | mPendingAdditions.add(holder);
218 | return true;
219 | }
220 |
221 | @Override
222 | public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY,
223 | int toX, int toY) {
224 | final View view = holder.itemView;
225 | fromX += ViewCompat.getTranslationX(holder.itemView);
226 | fromY += ViewCompat.getTranslationY(holder.itemView);
227 | endAnimation(holder);
228 | int deltaX = toX - fromX;
229 | int deltaY = toY - fromY;
230 | if (deltaX == 0 && deltaY == 0) {
231 | dispatchMoveFinished(holder);
232 | return false;
233 | }
234 | if (deltaX != 0) {
235 | ViewCompat.setTranslationX(view, -deltaX);
236 | }
237 | if (deltaY != 0) {
238 | ViewCompat.setTranslationY(view, -deltaY);
239 | }
240 | mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
241 | return true;
242 | }
243 |
244 | @Override
245 | public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,
246 | int fromX, int fromY, int toX, int toY) {
247 | final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);
248 | final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);
249 | final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
250 | endAnimation(oldHolder);
251 | int deltaX = (int) (toX - fromX - prevTranslationX);
252 | int deltaY = (int) (toY - fromY - prevTranslationY);
253 | // recover prev translation state after ending animation
254 | ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
255 | ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
256 | ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
257 | if (newHolder != null && newHolder.itemView != null) {
258 | // carry over translation values
259 | endAnimation(newHolder);
260 | ViewCompat.setTranslationX(newHolder.itemView, -deltaX);
261 | ViewCompat.setTranslationY(newHolder.itemView, -deltaY);
262 | ViewCompat.setAlpha(newHolder.itemView, 0);
263 | }
264 | mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
265 | return true;
266 | }
267 |
268 | private void animateChangeImpl(final ChangeInfo changeInfo) {
269 |
270 | }
271 |
272 | private void endChangeAnimation(List infoList, RecyclerView.ViewHolder item) {
273 | for (int i = infoList.size() - 1; i >= 0; i--) {
274 | ChangeInfo changeInfo = infoList.get(i);
275 | if (endChangeAnimationIfNecessary(changeInfo, item)) {
276 | if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {
277 | infoList.remove(changeInfo);
278 | }
279 | }
280 | }
281 | }
282 |
283 | private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {
284 | if (changeInfo.oldHolder != null) {
285 | endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);
286 | }
287 | if (changeInfo.newHolder != null) {
288 | endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
289 | }
290 | }
291 |
292 | private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, RecyclerView.ViewHolder item) {
293 | boolean oldItem = false;
294 | if (changeInfo.newHolder == item) {
295 | changeInfo.newHolder = null;
296 | } else if (changeInfo.oldHolder == item) {
297 | changeInfo.oldHolder = null;
298 | oldItem = true;
299 | } else {
300 | return false;
301 | }
302 | ViewCompat.setAlpha(item.itemView, 1);
303 | ViewCompat.setTranslationX(item.itemView, 0);
304 | ViewCompat.setTranslationY(item.itemView, 0);
305 | dispatchChangeFinished(item, oldItem);
306 | return true;
307 | }
308 |
309 | @Override
310 | public void endAnimation(RecyclerView.ViewHolder item) {
311 | final View view = item.itemView;
312 | // this will trigger end callback which should set properties to their target values.
313 | ViewCompat.animate(view).cancel();
314 | // TODO if some other animations are chained to end, how do we cancel them as well?
315 | for (int i = mPendingMoves.size() - 1; i >= 0; i--) {
316 | MoveInfo moveInfo = mPendingMoves.get(i);
317 | if (moveInfo.holder == item) {
318 | ViewCompat.setTranslationY(view, 0);
319 | ViewCompat.setTranslationX(view, 0);
320 | dispatchMoveFinished(item);
321 | mPendingMoves.remove(i);
322 | }
323 | }
324 | endChangeAnimation(mPendingChanges, item);
325 | if (mPendingRemovals.remove(item)) {
326 | ViewCompat.setAlpha(view, 1);
327 | dispatchRemoveFinished(item);
328 | }
329 | if (mPendingAdditions.remove(item)) {
330 | ViewCompat.setAlpha(view, 1);
331 | dispatchAddFinished(item);
332 | }
333 | for (int i = mChangesList.size() - 1; i >= 0; i--) {
334 | ArrayList changes = mChangesList.get(i);
335 | endChangeAnimation(changes, item);
336 | if (changes.isEmpty()) {
337 | mChangesList.remove(changes);
338 | }
339 | }
340 | for (int i = mMovesList.size() - 1; i >= 0; i--) {
341 | ArrayList moves = mMovesList.get(i);
342 | for (int j = moves.size() - 1; j >= 0; j--) {
343 | MoveInfo moveInfo = moves.get(j);
344 | if (moveInfo.holder == item) {
345 | ViewCompat.setTranslationY(view, 0);
346 | ViewCompat.setTranslationX(view, 0);
347 | dispatchMoveFinished(item);
348 | moves.remove(j);
349 | if (moves.isEmpty()) {
350 | mMovesList.remove(moves);
351 | }
352 | break;
353 | }
354 | }
355 | }
356 | for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
357 | ArrayList additions = mAdditionsList.get(i);
358 | if (additions.remove(item)) {
359 | ViewCompat.setAlpha(view, 1);
360 | dispatchAddFinished(item);
361 | if (additions.isEmpty()) {
362 | mAdditionsList.remove(additions);
363 | }
364 | }
365 | }
366 | dispatchFinishedWhenDone();
367 | }
368 |
369 | @Override
370 | public boolean isRunning() {
371 | return (!mPendingAdditions.isEmpty() ||
372 | !mPendingChanges.isEmpty() ||
373 | !mPendingMoves.isEmpty() ||
374 | !mPendingRemovals.isEmpty() ||
375 | !mMoveAnimations.isEmpty() ||
376 | !mRemoveAnimations.isEmpty() ||
377 | !mAddAnimations.isEmpty() ||
378 | !mChangeAnimations.isEmpty() ||
379 | !mMovesList.isEmpty() ||
380 | !mAdditionsList.isEmpty() ||
381 | !mChangesList.isEmpty());
382 | }
383 |
384 | private void dispatchFinishedWhenDone() {
385 | if (!isRunning()) {
386 | dispatchAnimationsFinished();
387 | }
388 | }
389 |
390 | @Override
391 | public void endAnimations() {
392 | int count = mPendingMoves.size();
393 | for (int i = count - 1; i >= 0; i--) {
394 | MoveInfo item = mPendingMoves.get(i);
395 | View view = item.holder.itemView;
396 | ViewCompat.setTranslationY(view, 0);
397 | ViewCompat.setTranslationX(view, 0);
398 | dispatchMoveFinished(item.holder);
399 | mPendingMoves.remove(i);
400 | }
401 | count = mPendingRemovals.size();
402 | for (int i = count - 1; i >= 0; i--) {
403 | RecyclerView.ViewHolder item = mPendingRemovals.get(i);
404 | dispatchRemoveFinished(item);
405 | mPendingRemovals.remove(i);
406 | }
407 | count = mPendingAdditions.size();
408 | for (int i = count - 1; i >= 0; i--) {
409 | RecyclerView.ViewHolder item = mPendingAdditions.get(i);
410 | View view = item.itemView;
411 | ViewCompat.setAlpha(view, 1);
412 | dispatchAddFinished(item);
413 | mPendingAdditions.remove(i);
414 | }
415 | count = mPendingChanges.size();
416 | for (int i = count - 1; i >= 0; i--) {
417 | endChangeAnimationIfNecessary(mPendingChanges.get(i));
418 | }
419 | mPendingChanges.clear();
420 | if (!isRunning()) {
421 | return;
422 | }
423 | int listCount = mMovesList.size();
424 | for (int i = listCount - 1; i >= 0; i--) {
425 | ArrayList moves = mMovesList.get(i);
426 | count = moves.size();
427 | for (int j = count - 1; j >= 0; j--) {
428 | MoveInfo moveInfo = moves.get(j);
429 | RecyclerView.ViewHolder item = moveInfo.holder;
430 | View view = item.itemView;
431 | ViewCompat.setTranslationY(view, 0);
432 | ViewCompat.setTranslationX(view, 0);
433 | dispatchMoveFinished(moveInfo.holder);
434 | moves.remove(j);
435 | if (moves.isEmpty()) {
436 | mMovesList.remove(moves);
437 | }
438 | }
439 | }
440 | listCount = mAdditionsList.size();
441 | for (int i = listCount - 1; i >= 0; i--) {
442 | ArrayList additions = mAdditionsList.get(i);
443 | count = additions.size();
444 | for (int j = count - 1; j >= 0; j--) {
445 | RecyclerView.ViewHolder item = additions.get(j);
446 | View view = item.itemView;
447 | ViewCompat.setAlpha(view, 1);
448 | dispatchAddFinished(item);
449 | additions.remove(j);
450 | if (additions.isEmpty()) {
451 | mAdditionsList.remove(additions);
452 | }
453 | }
454 | }
455 | listCount = mChangesList.size();
456 | for (int i = listCount - 1; i >= 0; i--) {
457 | ArrayList changes = mChangesList.get(i);
458 | count = changes.size();
459 | for (int j = count - 1; j >= 0; j--) {
460 | endChangeAnimationIfNecessary(changes.get(j));
461 | if (changes.isEmpty()) {
462 | mChangesList.remove(changes);
463 | }
464 | }
465 | }
466 | cancelAll(mRemoveAnimations);
467 | cancelAll(mMoveAnimations);
468 | cancelAll(mAddAnimations);
469 | cancelAll(mChangeAnimations);
470 | dispatchAnimationsFinished();
471 | }
472 |
473 | void cancelAll(List viewHolders) {
474 | for (int i = viewHolders.size() - 1; i >= 0; i--) {
475 | ViewCompat.animate(viewHolders.get(i).itemView).cancel();
476 | }
477 | }
478 |
479 | @Override
480 | public long getAddDuration() {
481 | return Constant.ANIM_DURATION_MILLIS;
482 | }
483 |
484 | @Override
485 | public long getRemoveDuration() {
486 | return Constant.ANIM_DURATION_MILLIS;
487 | }
488 |
489 | @Override
490 | public long getChangeDuration() {
491 | return Constant.ANIM_DURATION_MILLIS;
492 | }
493 |
494 | @Override
495 | public long getMoveDuration() {
496 | return Constant.ANIM_DURATION_MILLIS;
497 | }
498 |
499 | private static class MoveInfo {
500 | public RecyclerView.ViewHolder holder;
501 | public int fromX, fromY, toX, toY;
502 |
503 | private MoveInfo(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
504 | this.holder = holder;
505 | this.fromX = fromX;
506 | this.fromY = fromY;
507 | this.toX = toX;
508 | this.toY = toY;
509 | }
510 | }
511 |
512 | private static class ChangeInfo {
513 | public RecyclerView.ViewHolder oldHolder, newHolder;
514 | public int fromX, fromY, toX, toY;
515 |
516 | private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder) {
517 | this.oldHolder = oldHolder;
518 | this.newHolder = newHolder;
519 | }
520 |
521 | private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,
522 | int fromX, int fromY, int toX, int toY) {
523 | this(oldHolder, newHolder);
524 | this.fromX = fromX;
525 | this.fromY = fromY;
526 | this.toX = toX;
527 | this.toY = toY;
528 | }
529 | }
530 |
531 | }
532 |
--------------------------------------------------------------------------------