├── .gitignore
├── LICENSE
├── README.md
├── asyncexpandablelist
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── ericliu
│ │ └── asyncexpandablelist
│ │ ├── CollectionView.java
│ │ ├── CollectionViewCallbacks.java
│ │ └── async
│ │ ├── AsyncExpandableListView.java
│ │ ├── AsyncExpandableListViewCallbacks.java
│ │ └── AsyncHeaderViewHolder.java
│ └── res
│ └── values
│ └── strings.xml
├── build.gradle
├── sampleapp
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── ericliu
│ │ └── asyncexpandablelistsample
│ │ ├── AsyncActivity.java
│ │ ├── MainActivity.java
│ │ └── News.java
│ └── res
│ ├── drawable-hdpi
│ ├── ic_arrow_down.png
│ ├── ic_arrow_up.png
│ ├── ic_launcher.png
│ └── tile.9.png
│ ├── drawable-mdpi
│ ├── ic_arrow_down.png
│ ├── ic_arrow_up.png
│ └── ic_launcher.png
│ ├── drawable-xhdpi
│ ├── ic_arrow_down.png
│ ├── ic_arrow_up.png
│ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ ├── ic_arrow_down.png
│ ├── ic_arrow_up.png
│ └── ic_launcher.png
│ ├── layout
│ ├── activity_async.xml
│ ├── activity_main.xml
│ ├── header_row_item.xml
│ ├── header_row_item_async.xml
│ ├── recycler_view_frag.xml
│ ├── text_row_item.xml
│ └── text_row_item_async.xml
│ ├── menu
│ └── main.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 | */.idea*
3 | /.gradle*
4 | /.idea*
5 | /build/*
6 | /gradle/*
7 | /gradlew*
8 | /out*
9 | *.iml
10 | *.swp
11 | local.properties
12 | *~
13 | build-local.gradle
14 | gradle.properties
15 | settings-local.gradle
16 | screenshots/
17 | # Built application files
18 | *.apk
19 | *.ap_
20 |
21 | # Files for the Dalvik VM
22 | *.dex
23 |
24 | # Java class files
25 | *.class
26 |
27 | # Generated files
28 | bin/
29 | gen/
30 |
31 | # Gradle files
32 | .gradle/
33 | build/
34 |
35 | # Local configuration file (sdk path, etc)
36 | local.properties
37 |
38 | # Proguard folder generated by Eclipse
39 | proguard/
40 |
41 | # Log Files
42 | *.log
43 |
44 | # Android Studio Navigation editor temp files
45 | .navigation/
46 |
47 | # Android Studio captures folder
48 | captures/
49 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Eric Liu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # async-expandable-list
2 | [](https://android-arsenal.com/details/1/5092)
3 |
4 | ============
5 | async-expandable-list contains 2 View classes: CollectionView and AsyncExpandableListView.
6 |
7 |
8 |  
9 |
10 | Add async-expandable-list to your project
11 | ----------------------------
12 | Gradle:
13 | ```gradle
14 | compile 'com.ericliu.asyncexpandablelist:asyncexpandablelist:1.1.0'
15 | ```
16 |
17 |
18 | Please make sure you have jcenter() in your project's repository. Check build.gradle file under the project's root directory. Add the following lines if they are missing.
19 | ```gradle
20 | allprojects {
21 | repositories {
22 | jcenter()
23 | }
24 | }
25 | ```
26 |
27 | Introduction
28 | -------------------
29 | CollectionView displays a list of headers and sub-items:
30 | * Header A
31 | * item a1
32 | * item a2
33 | * Header B
34 | * item b1
35 | * item b2
36 | * item b3
37 | * Header C
38 | * item c1
39 | * item c2
40 | * item c3
41 |
42 |
43 | AsyncExpandableListView displays a list of headers and loads a sub-list under a header when a header item is clicked. The loading of sub-items can be done asynchronously and there are callbacks to populate the data into the list when it's done loading.
44 |
45 |
46 | 1. CollectionView in 3 steps
47 | -------------------
48 | 1. Add the CollectionView to the layout.xml file where you want to display the list (Optional, population the CollectionView class in java has the same result)
49 | ```xml
50 |
54 |
55 | ```
56 |
57 | 2. Pouplating data
58 | * find the CollectionView instance and call setCollectionCallbacks() to setup callbacks for the CollectionView, which will be responsible for creating ViewHolders and binding data into the ViewHoders - works the same as the RecyclerView.Adapter except that you don't have to worry about view types.
59 | * Create a CollectionView.Inventory instance, the Inventory instance represents the whole data structure that's gonna be populated into the list.
60 |
61 |
62 | ```java
63 | public class MainActivity extends Activity implements CollectionViewCallbacks {
64 | private CollectionView mCollectionView;
65 | private CollectionView.Inventory inventory;
66 |
67 | @Override
68 | protected void onCreate(Bundle savedInstanceState) {
69 | super.onCreate(savedInstanceState);
70 | setContentView(R.layout.activity_main);
71 |
72 | mCollectionView = (CollectionView) findViewById(R.id.collectionView);
73 | mCollectionView.setCollectionCallbacks(this);
74 |
75 | // the inventory represent all the whole data structure that's gonna be populated into the list.
76 | inventory = new CollectionView.Inventory<>();
77 |
78 | ```
79 | * Create InventoryGroup intances and add header item and sub-items into the InventoryGroup instance.
80 | Note the the newGroup(int groupOrdinal) method provided in the Inventory class requires an integer parameter: groupOrdinal.
81 | All the groups will be displayed in the list in an ascending order on the groupOrdinal.
82 | An InventoryGroup represents a header item and all sub-items under that header in the list.
83 |
84 |
85 | ```java
86 | int groupOrdinal = 0; // groupOrdinal dictates the sequence of groups to be displayed in the list
87 | CollectionView.InventoryGroup group1 = inventory.newGroup(groupOrdinal);
88 |
89 | // creating objects to be populated into the list.
90 | News news1 = new News();
91 | ...
92 | News news2 = new News(); ......
93 | .......
94 |
95 | // set the header item, in this case, it is simply a String.
96 | group1.setHeaderItem("Top Stories");
97 | // add items under this header.
98 | group1.addItem(news1);
99 | group1.addItem(news2);
100 | group1.addItem(news3);
101 | ....
102 |
103 | ```
104 |
105 | * Call updateInventory() to display the data structure we just created
106 | ```java
107 | mCollectionView.updateInventory(inventory);
108 | ```
109 | All done, the list will display the exact header-items structure.
110 |
111 | 2. AsyncExpandableListView in 3 steps
112 | -------------------
113 |
114 | 1. add AsyncExpandableListView to layout.xml file where you want to display the expandable list. (Optional, population the AsyncExpandableListView class in java has the same result).
115 | ```xml
116 |
120 | ```
121 | 2. Populating data
122 | * find the AsyncExpandableListView and call setCallbacks() and supply an AsyncExpandableListViewCallbacks instance to the view. The callbacks will handle the creation of ViewHolders and binding data to the ViewHolders.
123 | ```java
124 | public class AsyncActivity extends Activity implements AsyncExpandableListViewCallbacks {
125 |
126 | private AsyncExpandableListView mAsyncExpandableListView;
127 | private CollectionView.Inventory inventory;
128 |
129 |
130 | @Override
131 | protected void onCreate(Bundle savedInstanceState) {
132 | super.onCreate(savedInstanceState);
133 | setContentView(R.layout.activity_async);
134 | mAsyncExpandableListView = (AsyncExpandableListView) findViewById(R.id.asyncExpandableCollectionView);
135 | mAsyncExpandableListView.setCallbacks(this);
136 |
137 | ```
138 |
139 |
140 | * In particular the ``` void onStartLoadingGroup(int groupOrdinal) ``` method in the AsyncExpandableListViewCallbacks will be triggered on the header item click events, which gives the client a change to trigger loading sub-item data calls. When the call comes back, the client should call the method ``` onFinishLoadingGroup(mGroupOrdinal, items)``` on the AsyncExpandableListView instance to display the data as well as updating UI.
141 | * The steps to add groups are the same as CollectionView mentioned above, but we don't need to add sub-items to groups at this point because only headers will be shown in the beginning in an expandable list, as the code snippet showed below:
142 | ```java
143 | inventory = new CollectionView.Inventory<>();
144 |
145 | CollectionView.InventoryGroup group1 = inventory.newGroup(0); // groupOrdinal is the smallest, displayed first
146 | group1.setHeaderItem("Top Stories");
147 |
148 |
149 | CollectionView.InventoryGroup group2 = inventory.newGroup(2); // 2 is the second smallest ordinal, displayed second
150 | group2.setHeaderItem("World");
151 |
152 |
153 | CollectionView.InventoryGroup group3 = inventory.newGroup(3);
154 | group3.setHeaderItem("Australia");
155 |
156 | CollectionView.InventoryGroup group4 = inventory.newGroup(4);
157 | group4.setHeaderItem("International");
158 |
159 | CollectionView.InventoryGroup group5 = inventory.newGroup(5);
160 | group5.setHeaderItem("Businesses");
161 |
162 | CollectionView.InventoryGroup group6 = inventory.newGroup(6);
163 | group6.setHeaderItem("Technology");
164 |
165 | mAsyncExpandableListView.updateInventory(inventory);
166 |
167 | ```
168 |
169 | 3. Handle the async calls
170 | * Making the call to load all sub-items under a header in the method onStartLoadingGroup() in the AsyncExpandableListViewCallbacks.
171 |
172 | ```java
173 | @Override
174 | public void onStartLoadingGroup(int groupOrdinal) {
175 | new LoadDataTask(groupOrdinal, mAsyncExpandableListView).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
176 | }
177 | ```
178 | * When the data come back, call ``` onFinishLoadingGroup(mGroupOrdinal, items); ``` to display data.
179 | ```java
180 | mAsyncExpandableListView.onFinishLoadingGroup(mGroupOrdinal, items);
181 | ```
182 |
183 | References:
184 | -------------------
185 | Inspired by CollectionView in Google iosche
186 |
187 | https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/widget/CollectionView.java
188 |
--------------------------------------------------------------------------------
/asyncexpandablelist/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/asyncexpandablelist/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 | apply plugin: 'com.jfrog.bintray'
4 | version = "1.1.0"
5 |
6 | android {
7 | compileSdkVersion rootProject.compileSdk
8 | buildToolsVersion rootProject.buildToolsVersion
9 |
10 | defaultConfig {
11 | minSdkVersion rootProject.minSdk
12 | targetSdkVersion rootProject.targetSdk
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | buildToolsVersion '25.0.0'
24 | }
25 |
26 | dependencies {
27 | compile fileTree(dir: 'libs', include: ['*.jar'])
28 |
29 | compile "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion"
30 |
31 | testCompile 'junit:junit:4.12'
32 |
33 | }
34 |
35 |
36 | def siteUrl = 'https://github.com/Ericliu001/async-expandable-list' //Homepage URL of the library
37 | def gitUrl = 'https://github.com/Ericliu001/async-expandable-list.git' //Git repository url
38 | def issueUrl = 'https://github.com/Ericliu001/async-expandable-list/issues' //issue url of the library
39 | group = 'com.ericliu.asyncexpandablelist' //
40 |
41 |
42 | install {
43 | repositories.mavenInstaller {
44 | // This generates POM.xml with proper parameters
45 | pom {
46 | project {
47 | packaging 'aar'
48 | // Add your description here
49 | name 'A custom recyclerview that handles expandable list and async loading'
50 | url siteUrl
51 | // Set your license
52 | licenses {
53 | license {
54 | name 'The MIT License (MIT)'
55 | url 'https://opensource.org/licenses/MIT'
56 | }
57 | }
58 | developers {
59 | developer {
60 | id 'ericliu001' //your user ID
61 | name 'Eric Liu' //your name
62 | email 'eric.liu.developer@gmail.com' //your email
63 | }
64 | }
65 | scm {
66 | connection gitUrl
67 | developerConnection gitUrl
68 | url siteUrl
69 | }
70 | }
71 | }
72 | }
73 | }
74 |
75 |
76 | task sourcesJar(type: Jar) {
77 | from android.sourceSets.main.java.srcDirs
78 | classifier = 'sources'
79 | }
80 | task javadoc(type: Javadoc) {
81 | source = android.sourceSets.main.java.srcDirs
82 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
83 | }
84 | task javadocJar(type: Jar, dependsOn: javadoc) {
85 | classifier = 'javadoc'
86 | from javadoc.destinationDir
87 | }
88 | artifacts {
89 | archives javadocJar
90 | archives sourcesJar
91 | }
92 |
93 | Properties properties = new Properties()
94 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
95 |
96 | bintray {
97 | user = properties.getProperty("bintray.user")
98 | key = properties.getProperty("bintray.apikey")
99 | configurations = ['archives']
100 | pkg {
101 |
102 | repo = 'maven' //Bintray repository
103 | name = 'async-expandable-list'
104 | desc = 'A custom recyclerview that handles expandable list and async loading' //Project description
105 | websiteUrl = siteUrl
106 | vcsUrl = gitUrl
107 | issueTrackerUrl = issueUrl
108 | licenses = ["MIT"]
109 | labels = ['android'] //标签
110 | publish = true
111 | publicDownloadNumbers = true
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/asyncexpandablelist/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/asyncexpandablelist/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/asyncexpandablelist/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
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 |
--------------------------------------------------------------------------------
/asyncexpandablelist/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 |
--------------------------------------------------------------------------------
/asyncexpandablelist/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 |
--------------------------------------------------------------------------------
/asyncexpandablelist/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 /Users/ericliu/Library/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 |
--------------------------------------------------------------------------------
/asyncexpandablelist/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/asyncexpandablelist/src/main/java/com/ericliu/asyncexpandablelist/CollectionView.java:
--------------------------------------------------------------------------------
1 | package com.ericliu.asyncexpandablelist;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.LinearLayoutManager;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.util.AttributeSet;
7 | import android.util.Log;
8 | import android.util.SparseArray;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | /**
16 | * Created by Eric Liu on 18/01/2016.
17 | */
18 | public class CollectionView extends RecyclerView {
19 | private static final String TAG = CollectionView.class.getSimpleName();
20 | private static final int VIEWTYPE_HEADER = 0;
21 | private static final int VIEW_TYPE_NON_HEADER = 10;
22 | protected final LinearLayoutManager mLinearLayoutManager;
23 |
24 | protected Inventory mInventory = new Inventory<>();
25 | private CollectionViewCallbacks mCallbacks = null;
26 | private MyListAdapter mAdapter = null;
27 |
28 |
29 | public CollectionView(Context context) {
30 | this(context, null);
31 | }
32 |
33 | public CollectionView(Context context, AttributeSet attrs) {
34 | this(context, attrs, 0);
35 | }
36 |
37 |
38 | public CollectionView(Context context, AttributeSet attrs, int defStyle) {
39 | super(context, attrs, defStyle);
40 | mLinearLayoutManager = new LinearLayoutManager(getContext());
41 | mLinearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
42 | this.setLayoutManager(mLinearLayoutManager);
43 | mAdapter = new MyListAdapter();
44 | setAdapter(mAdapter);
45 | }
46 |
47 |
48 | public void setCollectionCallbacks(CollectionViewCallbacks adapter) {
49 | mCallbacks = adapter;
50 | }
51 |
52 |
53 |
54 |
55 | public void updateInventory(final Inventory inventory) {
56 | mInventory = new Inventory<>(inventory);
57 | mAdapter.notifyDataSetChanged();
58 | }
59 |
60 |
61 |
62 | public T1 getHeader(int groupOrdinal) {
63 | InventoryGroup group = mInventory.mGroups.get(groupOrdinal);
64 | if (group != null) {
65 | return group.getHeaderItem();
66 | } else {
67 | return null;
68 | }
69 | }
70 |
71 | public void addGroup(InventoryGroup group) {
72 | mInventory.addGroup(group);
73 | int itemCountBeforeGroup = mInventory.getRowCountBeforeGroup(group);
74 |
75 | mAdapter.notifyItemRangeInserted(itemCountBeforeGroup, group.getRowCount());
76 | }
77 |
78 | public void addItemsInGroup(int groupOrdinal, List extends T2> items) {
79 | InventoryGroup group = mInventory.findGroup(groupOrdinal);
80 | int rowCountBeforeAddingItems = group.getRowCount();
81 | int itemCountBeforeGroup = mInventory.getRowCountBeforeGroup(group);
82 |
83 | group.addItems(items);
84 |
85 | mAdapter.notifyItemRangeInserted(itemCountBeforeGroup + rowCountBeforeAddingItems, items.size());
86 | }
87 |
88 |
89 | public void removeAllItemsInGroup(int groupOrdinal) {
90 | InventoryGroup group = mInventory.findGroup(groupOrdinal);
91 | int itemCount = group.mItems.size();
92 | int itemCountBeforeGroup = mInventory.getRowCountBeforeGroup(group);
93 | group.mItems.clear();
94 |
95 | mAdapter.notifyItemRangeRemoved(itemCountBeforeGroup + 1, itemCount);
96 |
97 | }
98 |
99 |
100 | private final class MyListAdapter extends Adapter {
101 | private MyListAdapter() {
102 | }
103 |
104 | @Override
105 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
106 | return getRowViewHolder(parent, viewType);
107 | }
108 |
109 | @Override
110 | public void onBindViewHolder(ViewHolder holder, int position) {
111 | populatRoweData(holder, position);
112 | }
113 |
114 | @Override
115 | public int getItemViewType(int position) {
116 | RowInformation rowInfo = computeRowContent(position);
117 | if (rowInfo.isComputedSuccessful) {
118 | if (rowInfo.isHeader) {
119 | return VIEWTYPE_HEADER - mInventory.mGroups.indexOfKey(rowInfo.groupOrdinal);
120 | } else {
121 | return VIEW_TYPE_NON_HEADER + mInventory.mGroups.indexOfKey(rowInfo.groupOrdinal);
122 | }
123 |
124 | } else {
125 | Log.e(TAG, "Invalid row passed to getItemViewType: " + position);
126 | return 0;
127 | }
128 | }
129 |
130 |
131 | @Override
132 | public int getItemCount() {
133 | int rowCount = 0;
134 |
135 | for (int i = 0; i < mInventory.mGroups.size(); i++) {
136 | int key = mInventory.mGroups.keyAt(i);
137 | InventoryGroup group = mInventory.mGroups.get(key);
138 | int thisGroupRowCount = group.getRowCount();
139 | rowCount += thisGroupRowCount;
140 | }
141 |
142 | return rowCount;
143 | }
144 | }
145 |
146 | protected RowInformation populatRoweData(ViewHolder holder, int position) {
147 | if (mCallbacks == null) {
148 | return null;
149 | }
150 |
151 | RowInformation rowInfo = computeRowContent(position);
152 | if (!rowInfo.isComputedSuccessful) {
153 | return null;
154 | }
155 |
156 | if (rowInfo.isHeader) {
157 | mCallbacks.bindCollectionHeaderView(getContext(), holder, rowInfo.groupOrdinal, rowInfo.group.getHeaderItem());
158 | } else {
159 | T2 item = rowInfo.group.getItem(rowInfo.positionInGroup);
160 | mCallbacks.bindCollectionItemView(getContext(), holder, rowInfo.groupOrdinal, item);
161 | }
162 |
163 | return rowInfo;
164 | }
165 |
166 | private ViewHolder getRowViewHolder(ViewGroup parent, final int viewType) {
167 | ViewHolder placeHolder = new ViewHolder(new View(getContext())) {
168 | @Override
169 | public String toString() {
170 | return "Invalid Item, view type: " + viewType;
171 | }
172 | };
173 | if (mCallbacks == null) {
174 | Log.e(TAG, "Call to makeRow without an adapter installed");
175 | return placeHolder;
176 | }
177 |
178 |
179 | ViewHolder holder;
180 | if (viewType <= VIEWTYPE_HEADER) {
181 | int groupIndex = VIEWTYPE_HEADER - viewType;
182 | int key = mInventory.mGroups.keyAt(groupIndex);
183 | int groupOrdinal = mInventory.mGroups.get(key).mOrdinal;
184 |
185 | // return header ViewHolder
186 | holder = mCallbacks.newCollectionHeaderView(getContext(), groupOrdinal, parent);
187 | } else {
188 | int groupIndex = viewType - VIEW_TYPE_NON_HEADER;
189 | int key = mInventory.mGroups.keyAt(groupIndex);
190 | int groupOrdinal = mInventory.mGroups.get(key).mOrdinal;
191 | // return item ViewHolder
192 | holder = mCallbacks.newCollectionItemView(getContext(), groupOrdinal, parent);
193 | }
194 |
195 | if (holder != null) {
196 | return holder;
197 | } else {
198 | return placeHolder;
199 | }
200 |
201 | }
202 |
203 |
204 | protected static class RowInformation {
205 | boolean isComputedSuccessful = false;
206 | int row;
207 | boolean isHeader;
208 | int groupOrdinal;
209 | InventoryGroup group;
210 | int positionInGroup;
211 |
212 | public boolean isHeader() {
213 | return isHeader;
214 | }
215 |
216 | public int getGroupOrdinal() {
217 | return groupOrdinal;
218 | }
219 |
220 | public int getPositionInGroup() {
221 | return positionInGroup;
222 | }
223 | }
224 |
225 |
226 | protected RowInformation computeRowContent(int row) {
227 | RowInformation result = new RowInformation<>();
228 | int rowCounter = 0;
229 | int positionInGroup;
230 |
231 |
232 | for (int i = 0; i < mInventory.mGroups.size(); i++) {
233 | int key = mInventory.mGroups.keyAt(i);
234 | InventoryGroup group = mInventory.mGroups.get(key);
235 | if (rowCounter == row) {
236 | // row is a group header
237 | result.isComputedSuccessful = true;
238 | result.row = row;
239 | result.isHeader = true;
240 | result.groupOrdinal = group.mOrdinal;
241 | result.group = group;
242 | result.positionInGroup = -1;
243 | return result;
244 | }
245 | rowCounter++; // incremented by 1 because it just past the Header row
246 |
247 | positionInGroup = 0;
248 | while (positionInGroup < group.mItems.size()) {
249 | if (rowCounter == row) {
250 | // this is the row we are looking for
251 | result.isComputedSuccessful = true;
252 | result.row = row;
253 | result.isHeader = false;
254 | result.groupOrdinal = group.mOrdinal;
255 | result.group = group;
256 | result.positionInGroup = positionInGroup;
257 | return result;
258 | }
259 |
260 | // move to the next row
261 | positionInGroup++;
262 | rowCounter++;
263 | }
264 | }
265 |
266 | return result;
267 | }
268 |
269 |
270 | /**
271 | * Represents a group of items with a header to be displayed in the {@link CollectionView}.
272 | */
273 | public final static class InventoryGroup {
274 |
275 | final private int mOrdinal;
276 |
277 | private T1 mHeaderItem;
278 | private ArrayList mItems = new ArrayList<>();
279 |
280 |
281 | private InventoryGroup(int oridinal) {
282 | mOrdinal = oridinal;
283 | }
284 |
285 |
286 | public int getOrdinal() {
287 | return mOrdinal;
288 | }
289 |
290 | public T1 getHeaderItem() {
291 | return mHeaderItem;
292 | }
293 |
294 | public InventoryGroup setHeaderItem(T1 headerItem) {
295 | mHeaderItem = headerItem;
296 | return this;
297 | }
298 |
299 |
300 | public void addItem(T2 item) {
301 | mItems.add(item);
302 | }
303 |
304 |
305 | public int getRowCount() {
306 | return 1 + mItems.size();
307 | }
308 |
309 |
310 | public T2 getItem(int index) {
311 | return mItems.get(index);
312 | }
313 |
314 | public void addItems(List extends T2> items) {
315 | mItems.addAll(items);
316 | }
317 |
318 |
319 | }
320 |
321 |
322 | /**
323 | * Represents the data of the items to display in the {@link CollectionView}.
324 | * This is defined as a list of {@link InventoryGroup} which represents a group of items with a
325 | * header.
326 | */
327 | public final static class Inventory {
328 | private SparseArray> mGroups = new SparseArray<>();
329 |
330 |
331 | public Inventory() {
332 | }
333 |
334 | private Inventory(Inventory copyFrom) {
335 | mGroups = copyFrom.mGroups.clone();
336 | }
337 |
338 | private void addGroup(InventoryGroup group) {
339 | mGroups.put(group.mOrdinal, group);
340 | }
341 |
342 | public InventoryGroup newGroup(int groupOrdinal) {
343 | InventoryGroup group = new InventoryGroup<>(groupOrdinal);
344 | addGroup(group);
345 | return group;
346 | }
347 |
348 | public InventoryGroup putGroup(int groupOrdinal) {
349 | return newGroup(groupOrdinal);
350 | }
351 |
352 |
353 | private InventoryGroup findGroup(int groupOrdinal) {
354 | return mGroups.get(groupOrdinal);
355 | }
356 |
357 |
358 | public int getTotalItemCount() {
359 | int total = 0;
360 |
361 | for (int i = 0; i < mGroups.size(); i++) {
362 | int key = mGroups.keyAt(i);
363 | total += mGroups.get(key).mItems.size();
364 | }
365 | return total;
366 | }
367 |
368 | public int getGroupCount() {
369 | return mGroups.size();
370 | }
371 |
372 | public int getGroupIndex(int groupOrdinal) {
373 | for (int i = 0; i < mGroups.size(); i++) {
374 | int key = mGroups.keyAt(i);
375 | if (mGroups.get(key).mOrdinal == groupOrdinal) {
376 | return i;
377 | }
378 | }
379 | return -1;
380 | }
381 |
382 | public int getRowCountBeforeGroup(InventoryGroup group) {
383 | return getRowCountBeforeGroup(group.mOrdinal);
384 | }
385 |
386 | public int getRowCountBeforeGroup(int groupOrdinal) {
387 | int count = 0;
388 | for (int i = 0; i < mGroups.size(); i++) {
389 | if (groupOrdinal == mGroups.keyAt(i)) {
390 | break;
391 | }
392 | int key = mGroups.keyAt(i);
393 | count += mGroups.get(key).getRowCount();
394 | }
395 | return count;
396 | }
397 |
398 |
399 | public SparseArray> getGroups() {
400 | return mGroups;
401 | }
402 | }
403 | }
404 |
--------------------------------------------------------------------------------
/asyncexpandablelist/src/main/java/com/ericliu/asyncexpandablelist/CollectionViewCallbacks.java:
--------------------------------------------------------------------------------
1 | package com.ericliu.asyncexpandablelist;/*
2 | * Copyright 2014 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 |
18 | import android.content.Context;
19 | import android.support.v7.widget.RecyclerView;
20 | import android.view.ViewGroup;
21 |
22 | /**
23 | * Defines an interface to the callbacks that a {@link CollectionView} will be called to create each
24 | * elements of the collection.
25 | */
26 | public interface CollectionViewCallbacks {
27 |
28 | /**
29 | * Returns a new custom View that will be used for each of the collection group headers.
30 | */
31 | RecyclerView.ViewHolder newCollectionHeaderView(Context context, int groupOrdinal, ViewGroup parent);
32 |
33 |
34 | /**
35 | * Returns a new custom View that will be used for each of the collection item.
36 | *
37 | * @param context
38 | * @param groupOrdinal - the groupOrdinal decides the sequence of groups being displayed, the smallest int is displayed first and in an asending order
39 | * @param parent
40 | * @return
41 | */
42 | RecyclerView.ViewHolder newCollectionItemView(Context context, int groupOrdinal, ViewGroup parent);
43 |
44 | /**
45 | * Binds the given data (like the header label) with the given collection group header View.
46 | */
47 | void bindCollectionHeaderView(Context context, RecyclerView.ViewHolder holder, int groupOrdinal, T1 headerItem);
48 |
49 | /**
50 | * Binds the given data with the given collection item View.
51 | */
52 | void bindCollectionItemView(Context context, RecyclerView.ViewHolder holder, int groupOrdinal, T2 item);
53 |
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/asyncexpandablelist/src/main/java/com/ericliu/asyncexpandablelist/async/AsyncExpandableListView.java:
--------------------------------------------------------------------------------
1 | package com.ericliu.asyncexpandablelist.async;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.util.AttributeSet;
6 | import android.view.ViewGroup;
7 |
8 | import com.ericliu.asyncexpandablelist.CollectionView;
9 | import com.ericliu.asyncexpandablelist.CollectionViewCallbacks;
10 |
11 | import java.util.List;
12 | import java.util.Map;
13 | import java.util.WeakHashMap;
14 |
15 |
16 | /**
17 | * Created by ericliu on 6/10/2016.
18 | */
19 |
20 | public class AsyncExpandableListView extends CollectionView {
21 |
22 | private AsyncExpandableListViewCallbacks mCallbacks;
23 | protected Map mOnGroupStateChangeListeners = new WeakHashMap<>();
24 | protected int expandedGroupOrdinal = -1;
25 |
26 |
27 | public AsyncExpandableListView(Context context) {
28 | super(context);
29 | }
30 |
31 | public AsyncExpandableListView(Context context, AttributeSet attrs) {
32 | super(context, attrs);
33 | }
34 |
35 | public AsyncExpandableListView(Context context, AttributeSet attrs, int defStyle) {
36 | super(context, attrs, defStyle);
37 | }
38 |
39 |
40 | @Override
41 | protected CollectionView.RowInformation populatRoweData(RecyclerView.ViewHolder holder, int position) {
42 | CollectionView.RowInformation rowInfo = super.populatRoweData(holder, position);
43 | if (rowInfo.isHeader()) {
44 | mOnGroupStateChangeListeners.put((AsyncHeaderViewHolder) holder, rowInfo.getGroupOrdinal());
45 | }
46 |
47 | return rowInfo;
48 | }
49 |
50 | public interface OnGroupStateChangeListener {
51 |
52 | void onGroupStartExpending();
53 |
54 | void onGroupExpanded();
55 |
56 | void onGroupCollapsed();
57 | }
58 |
59 |
60 | public void onGroupClicked(int groupOrdinal) {
61 | if (groupOrdinal != expandedGroupOrdinal) {
62 | onStartExpandingGroup(groupOrdinal);
63 | } else {
64 | collapseGroup(groupOrdinal);
65 | }
66 |
67 | }
68 |
69 |
70 | public void setCallbacks(final AsyncExpandableListViewCallbacks callbacks) {
71 | CollectionViewCallbacks collectionViewCallbacks = new CollectionViewCallbacks() {
72 | @Override
73 | public ViewHolder newCollectionHeaderView(Context context, int groupOrdinal, ViewGroup parent) {
74 | return callbacks.newCollectionHeaderView(context, groupOrdinal, parent);
75 | }
76 |
77 | @Override
78 | public ViewHolder newCollectionItemView(Context context, int groupOrdinal, ViewGroup parent) {
79 | return callbacks.newCollectionItemView(context, groupOrdinal, parent);
80 | }
81 |
82 | @Override
83 | public void bindCollectionHeaderView(Context context, ViewHolder holder, int groupOrdinal, T1 headerItem) {
84 | callbacks.bindCollectionHeaderView(context, (AsyncHeaderViewHolder) holder, groupOrdinal, headerItem);
85 | }
86 |
87 | @Override
88 | public void bindCollectionItemView(Context context, ViewHolder holder, int groupOrdinal, T2 item) {
89 | callbacks.bindCollectionItemView(context, holder, groupOrdinal, item);
90 | }
91 | };
92 | setCollectionCallbacks(collectionViewCallbacks);
93 | mCallbacks = callbacks;
94 | }
95 |
96 | protected void collapseGroup(int groupOrdinal) {
97 | expandedGroupOrdinal = -1;
98 | removeAllItemsInGroup(groupOrdinal);
99 | for (OnGroupStateChangeListener onGroupStateChangeListener : mOnGroupStateChangeListeners.keySet()) {
100 | if (mOnGroupStateChangeListeners.get(onGroupStateChangeListener) == groupOrdinal) {
101 | onGroupStateChangeListener.onGroupCollapsed();
102 | }
103 | }
104 | }
105 |
106 |
107 | protected void onStartExpandingGroup(int groupOrdinal) {
108 | int ordinal = 0;
109 | for (int i = 0; i < mInventory.getGroups().size(); i++) {
110 | ordinal = mInventory.getGroups().keyAt(i);
111 | if (ordinal != groupOrdinal) {
112 | collapseGroup(ordinal);
113 | }
114 | }
115 |
116 | expandedGroupOrdinal = groupOrdinal;
117 | for (OnGroupStateChangeListener onGroupStateChangeListener : mOnGroupStateChangeListeners.keySet()) {
118 | if (mOnGroupStateChangeListeners.get(onGroupStateChangeListener) == groupOrdinal) {
119 | onGroupStateChangeListener.onGroupStartExpending();
120 | }
121 | }
122 | mCallbacks.onStartLoadingGroup(groupOrdinal);
123 | }
124 |
125 |
126 | public boolean onFinishLoadingGroup(int groupOrdinal, List items) {
127 | if (expandedGroupOrdinal < 0 || groupOrdinal != expandedGroupOrdinal) {
128 | return false;
129 | }
130 |
131 | addItemsInGroup(expandedGroupOrdinal, items);
132 | for (OnGroupStateChangeListener onGroupStateChangeListener : mOnGroupStateChangeListeners.keySet()) {
133 | if (mOnGroupStateChangeListeners.get(onGroupStateChangeListener) == expandedGroupOrdinal) {
134 | onGroupStateChangeListener.onGroupExpanded();
135 | }
136 | }
137 |
138 | return true;
139 | }
140 |
141 | }
142 |
--------------------------------------------------------------------------------
/asyncexpandablelist/src/main/java/com/ericliu/asyncexpandablelist/async/AsyncExpandableListViewCallbacks.java:
--------------------------------------------------------------------------------
1 | package com.ericliu.asyncexpandablelist.async;
2 |
3 |
4 | import android.content.Context;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.view.ViewGroup;
7 |
8 | /**
9 | * Created by ericliu on 11/10/16.
10 | */
11 |
12 | public interface AsyncExpandableListViewCallbacks {
13 |
14 | void onStartLoadingGroup(int groupOrdinal);
15 |
16 | /**
17 | * Returns a new custom View that will be used for each of the collection group headers.
18 | */
19 | AsyncHeaderViewHolder newCollectionHeaderView(Context context, int groupOrdinal, ViewGroup parent);
20 |
21 |
22 | /**
23 | * Returns a new custom View that will be used for each of the collection item.
24 | *
25 | * @param context
26 | * @param groupOrdinal - the groupOrdinal decides the sequence of groups being displayed, the smallest int is displayed first and in an asending order
27 | * @param parent
28 | * @return
29 | */
30 | RecyclerView.ViewHolder newCollectionItemView(Context context, int groupOrdinal, ViewGroup parent);
31 |
32 | /**
33 | * Binds the given data (like the header label) with the given collection group header View.
34 | */
35 | void bindCollectionHeaderView(Context context, AsyncHeaderViewHolder holder, int groupOrdinal, T1 headerItem);
36 |
37 | /**
38 | * Binds the given data with the given collection item View.
39 | */
40 | void bindCollectionItemView(Context context, RecyclerView.ViewHolder holder, int groupOrdinal, T2 item);
41 |
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/asyncexpandablelist/src/main/java/com/ericliu/asyncexpandablelist/async/AsyncHeaderViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.ericliu.asyncexpandablelist.async;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by ericliu on 12/10/16.
8 | */
9 |
10 | public abstract class AsyncHeaderViewHolder extends RecyclerView.ViewHolder implements AsyncExpandableListView.OnGroupStateChangeListener {
11 | private final int mGroupOrdinal;
12 | private final AsyncExpandableListView mAsyncExpandableListView;
13 |
14 | public AsyncHeaderViewHolder(View itemView, int groupOrdinal, AsyncExpandableListView asyncExpandableListView) {
15 | super(itemView);
16 | mGroupOrdinal = groupOrdinal;
17 | mAsyncExpandableListView = asyncExpandableListView;
18 | itemView.setOnClickListener(new View.OnClickListener() {
19 | @Override
20 | public void onClick(View v) {
21 | mAsyncExpandableListView.onGroupClicked(mGroupOrdinal);
22 | onItemClick(v);
23 | }
24 | });
25 | }
26 |
27 | /**
28 | * triggered by onClick event, to be overriden.
29 | * @param view
30 | */
31 | public void onItemClick(View view){}
32 |
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/asyncexpandablelist/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AsyncExpandableList
3 |
4 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.0'
9 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4'
10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | mavenCentral()
20 | maven {
21 | url 'https://oss.sonatype.org/content/groups/public'
22 | }
23 | }
24 | }
25 |
26 |
27 |
28 | project.ext.supportLibraryVersion = '24.2.0'
29 | project.ext.buildToolsVersion = '24.0.2'
30 |
31 | project.ext.targetSdk = 21
32 | project.ext.minSdk = 15
33 | project.ext.compileSdk = 24
34 |
--------------------------------------------------------------------------------
/sampleapp/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | android {
3 | compileSdkVersion rootProject.compileSdk
4 | buildToolsVersion rootProject.buildToolsVersion
5 |
6 | defaultConfig {
7 | applicationId "com.ericliu.asyncexpandablelistsample"
8 | minSdkVersion rootProject.minSdk
9 | targetSdkVersion rootProject.targetSdk
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | lintOptions {
20 | abortOnError false
21 | }
22 |
23 | buildToolsVersion '25.0.0'
24 | }
25 |
26 |
27 | dependencies {
28 | compile fileTree(dir: 'libs', include: ['*.jar'])
29 | compile project(':asyncexpandablelist')
30 | compile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
31 | compile "com.android.support:support-v4:$rootProject.supportLibraryVersion"
32 | compile "com.android.support:cardview-v7:$rootProject.supportLibraryVersion"
33 | }
34 |
--------------------------------------------------------------------------------
/sampleapp/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
21 |
22 |
23 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/sampleapp/src/main/java/com/ericliu/asyncexpandablelistsample/AsyncActivity.java:
--------------------------------------------------------------------------------
1 | package com.ericliu.asyncexpandablelistsample;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.os.AsyncTask;
6 | import android.os.Bundle;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.ImageView;
12 | import android.widget.ProgressBar;
13 | import android.widget.TextView;
14 |
15 | import com.ericliu.asyncexpandablelist.CollectionView;
16 | import com.ericliu.asyncexpandablelist.async.AsyncExpandableListView;
17 | import com.ericliu.asyncexpandablelist.async.AsyncExpandableListViewCallbacks;
18 | import com.ericliu.asyncexpandablelist.async.AsyncHeaderViewHolder;
19 |
20 | import java.lang.ref.WeakReference;
21 | import java.util.ArrayList;
22 | import java.util.List;
23 |
24 |
25 | public class AsyncActivity extends Activity implements AsyncExpandableListViewCallbacks {
26 |
27 | private AsyncExpandableListView mAsyncExpandableListView;
28 | private CollectionView.Inventory inventory;
29 |
30 |
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | setContentView(R.layout.activity_async);
35 | mAsyncExpandableListView = (AsyncExpandableListView) findViewById(R.id.asyncExpandableCollectionView);
36 | mAsyncExpandableListView.setCallbacks(this);
37 |
38 | inventory = new CollectionView.Inventory<>();
39 |
40 | CollectionView.InventoryGroup group1 = inventory.newGroup(0); // groupOrdinal is the smallest, displayed first
41 | group1.setHeaderItem("Top Stories");
42 |
43 |
44 | CollectionView.InventoryGroup group2 = inventory.newGroup(2);
45 | group2.setHeaderItem("World");
46 |
47 |
48 | CollectionView.InventoryGroup group3 = inventory.newGroup(3);
49 | group3.setHeaderItem("Australia");
50 |
51 | CollectionView.InventoryGroup group4 = inventory.newGroup(4);
52 | group4.setHeaderItem("International");
53 |
54 | CollectionView.InventoryGroup group5 = inventory.newGroup(5);
55 | group5.setHeaderItem("Businesses");
56 |
57 | CollectionView.InventoryGroup group6 = inventory.newGroup(6);
58 | group6.setHeaderItem("Technology");
59 |
60 | CollectionView.InventoryGroup group7 = inventory.newGroup(7);
61 | group7.setHeaderItem("Environment");
62 |
63 | CollectionView.InventoryGroup group8 = inventory.newGroup(8);
64 | group8.setHeaderItem("Health");
65 |
66 | CollectionView.InventoryGroup group9 = inventory.newGroup(9);
67 | group9.setHeaderItem("Science");
68 |
69 | CollectionView.InventoryGroup group10 = inventory.newGroup(10);
70 | group10.setHeaderItem("Sports");
71 |
72 | CollectionView.InventoryGroup group11 = inventory.newGroup(11);
73 | group11.setHeaderItem("Entertainment");
74 |
75 | CollectionView.InventoryGroup group12 = inventory.newGroup(12);
76 | group12.setHeaderItem("Politics");
77 |
78 | mAsyncExpandableListView.updateInventory(inventory);
79 | }
80 |
81 | @Override
82 | public void onStartLoadingGroup(int groupOrdinal) {
83 | new LoadDataTask(groupOrdinal, mAsyncExpandableListView).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
84 | }
85 |
86 |
87 |
88 |
89 | private static class LoadDataTask extends AsyncTask {
90 |
91 | private final int mGroupOrdinal;
92 | private WeakReference> listviewRef = null;
93 |
94 | public LoadDataTask(int groupOrdinal, AsyncExpandableListView listview) {
95 | mGroupOrdinal = groupOrdinal;
96 | listviewRef = new WeakReference<>(listview);
97 | }
98 |
99 | @Override
100 | protected Void doInBackground(Void... params) {
101 | try {
102 | Thread.sleep(1500);
103 | } catch (InterruptedException e) {
104 | e.printStackTrace();
105 | }
106 | return null;
107 | }
108 |
109 |
110 | @Override
111 | protected void onPostExecute(Void aVoid) {
112 | List items = new ArrayList<>();
113 | News news = new News();
114 | news.setNewsTitle("Lawyers meet voluntary pro bono target for first time since 2013");
115 | news.setNewsBody("A voluntary target for the amount of pro bono work done by Australian lawyers has been met for the first time since 2013. Key points: The Australian Pro Bono Centre's asks lawyers to do 35 hours of free community work a year; Pro bono services can help ...\n");
116 | items.add(news);
117 |
118 | news = new News();
119 | news.setNewsTitle("HSC 2016: 77000 students to sit first exams across NSW");
120 | news.setNewsBody("More than 77,000 NSW high school students will sit their first HSC exams this week as one of the final cohorts to sit the test before the NSW government enacts sweeping reforms across the state.");
121 | items.add(news);
122 |
123 | if (listviewRef.get() != null) {
124 | listviewRef.get().onFinishLoadingGroup(mGroupOrdinal, items);
125 | }
126 | }
127 |
128 | }
129 |
130 | @Override
131 | public AsyncHeaderViewHolder newCollectionHeaderView(Context context, int groupOrdinal, ViewGroup parent) {
132 | // Create a new view.
133 | View v = LayoutInflater.from(context)
134 | .inflate(R.layout.header_row_item_async, parent, false);
135 |
136 | return new MyHeaderViewHolder(v, groupOrdinal, mAsyncExpandableListView);
137 | }
138 |
139 | @Override
140 | public RecyclerView.ViewHolder newCollectionItemView(Context context, int groupOrdinal, ViewGroup parent) {
141 | // Create a new view.
142 | View v = LayoutInflater.from(context)
143 | .inflate(R.layout.text_row_item_async, parent, false);
144 |
145 | return new MainActivity.NewsItemHolder(v);
146 | }
147 |
148 | @Override
149 | public void bindCollectionHeaderView(Context context, AsyncHeaderViewHolder holder, int groupOrdinal, String headerItem) {
150 | MyHeaderViewHolder myHeaderViewHolder = (MyHeaderViewHolder) holder;
151 | myHeaderViewHolder.getTextView().setText(headerItem);
152 | }
153 |
154 | @Override
155 | public void bindCollectionItemView(Context context, RecyclerView.ViewHolder holder, int groupOrdinal, News item) {
156 | MainActivity.NewsItemHolder newsItemHolder = (MainActivity.NewsItemHolder) holder;
157 | newsItemHolder.getTextViewTitle().setText(item.getNewsTitle());
158 | newsItemHolder.getTextViewDescrption().setText(item.getNewsBody());
159 | }
160 |
161 | public static class MyHeaderViewHolder extends AsyncHeaderViewHolder implements AsyncExpandableListView.OnGroupStateChangeListener {
162 |
163 | private final TextView textView;
164 | private final ProgressBar mProgressBar;
165 | private ImageView ivExpansionIndicator;
166 |
167 | public MyHeaderViewHolder(View v, int groupOrdinal, AsyncExpandableListView asyncExpandableListView) {
168 | super(v, groupOrdinal, asyncExpandableListView);
169 | textView = (TextView) v.findViewById(R.id.title);
170 | mProgressBar = (ProgressBar) v.findViewById(R.id.progressBar);
171 | mProgressBar.getIndeterminateDrawable().setColorFilter(0xFFFFFFFF,
172 | android.graphics.PorterDuff.Mode.MULTIPLY);
173 | ivExpansionIndicator = (ImageView) v.findViewById(R.id.ivExpansionIndicator);
174 | }
175 |
176 |
177 | public TextView getTextView() {
178 | return textView;
179 | }
180 |
181 |
182 | @Override
183 | public void onGroupStartExpending() {
184 | mProgressBar.setVisibility(View.VISIBLE);
185 | ivExpansionIndicator.setVisibility(View.INVISIBLE);
186 | }
187 |
188 | @Override
189 | public void onGroupExpanded() {
190 | mProgressBar.setVisibility(View.GONE);
191 | ivExpansionIndicator.setVisibility(View.VISIBLE);
192 | ivExpansionIndicator.setImageResource(R.drawable.ic_arrow_up);
193 | }
194 |
195 | @Override
196 | public void onGroupCollapsed() {
197 | mProgressBar.setVisibility(View.GONE);
198 | ivExpansionIndicator.setVisibility(View.VISIBLE);
199 | ivExpansionIndicator.setImageResource(R.drawable.ic_arrow_down);
200 |
201 | }
202 | }
203 | }
204 |
--------------------------------------------------------------------------------
/sampleapp/src/main/java/com/ericliu/asyncexpandablelistsample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.ericliu.asyncexpandablelistsample;/*
2 | * Copyright 2013 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 |
18 | import android.app.Activity;
19 | import android.content.Context;
20 | import android.content.Intent;
21 | import android.os.Bundle;
22 | import android.support.v7.widget.RecyclerView;
23 | import android.util.Log;
24 | import android.view.LayoutInflater;
25 | import android.view.Menu;
26 | import android.view.MenuInflater;
27 | import android.view.MenuItem;
28 | import android.view.View;
29 | import android.view.ViewGroup;
30 | import android.widget.TextView;
31 |
32 | import com.ericliu.asyncexpandablelist.CollectionView;
33 | import com.ericliu.asyncexpandablelist.CollectionViewCallbacks;
34 |
35 |
36 | /**
37 | * A simple launcher activity containing a summary sample description, sample log and a custom
38 | * {@link android.support.v4.app.Fragment} which can display a view.
39 | *
40 | * For devices with displays with a width of 720dp or greater, the sample log is always visible,
41 | * on other devices it's visibility is controlled by an item on the Action Bar.
42 | */
43 | public class MainActivity extends Activity implements CollectionViewCallbacks {
44 |
45 | public static final String TAG = "MainActivity";
46 |
47 |
48 | private CollectionView mCollectionView;
49 | private CollectionView.Inventory inventory;
50 | private boolean flag;
51 |
52 | @Override
53 | protected void onCreate(Bundle savedInstanceState) {
54 | super.onCreate(savedInstanceState);
55 | setContentView(R.layout.activity_main);
56 |
57 | mCollectionView = (CollectionView) findViewById(R.id.collectionView);
58 | mCollectionView.setCollectionCallbacks(this);
59 |
60 | inventory = new CollectionView.Inventory<>();
61 |
62 | // groupOrdinal dictates the sequence of groups to be displayed in the list,
63 | // the groups will be displayed in an ascending order on groupOrdinal
64 | int groupOrdinal = 0;
65 | CollectionView.InventoryGroup group1 = inventory.newGroup(groupOrdinal);
66 | News news;
67 |
68 | group1.setHeaderItem(getString(R.string.news_header_top_stories));
69 | news = new News();
70 | news.setNewsTitle(getString(R.string.news_title1));
71 | news.setNewsBody(getString(R.string.news_body1));
72 | group1.addItem(news);
73 |
74 | news = new News();
75 | news.setNewsTitle(getString(R.string.news_title2));
76 | news.setNewsBody(getString(R.string.news_body2));
77 | group1.addItem(news);
78 |
79 | news = new News();
80 | news.setNewsTitle(getString(R.string.news_title_3));
81 | news.setNewsBody(getString(R.string.news_body3));
82 | group1.addItem(news);
83 |
84 | CollectionView.InventoryGroup group2 = inventory.newGroup(2);
85 | group2.setHeaderItem(getString(R.string.news_header_world));
86 |
87 | news = new News();
88 | news.setNewsTitle(getString(R.string.news_title4));
89 | news.setNewsBody(getString(R.string.news_body4));
90 | group2.addItem(news);
91 |
92 | news = new News();
93 | news.setNewsTitle(getString(R.string.news_title5));
94 | news.setNewsBody(getString(R.string.news_body5));
95 | group2.addItem(news);
96 |
97 |
98 |
99 | CollectionView.InventoryGroup group3 = inventory.newGroup(3); // 2 is smaller than 10, displayed second
100 | group3.setHeaderItem(getString(R.string.news_header_australia));
101 |
102 | news = new News();
103 | news.setNewsTitle(getString(R.string.news_title6));
104 | news.setNewsBody(getString(R.string.news_body6));
105 | group3.addItem(news);
106 |
107 | news = new News();
108 | news.setNewsTitle(getString(R.string.news_title7));
109 | news.setNewsBody(getString(R.string.news_body7));
110 | group3.addItem(news);
111 |
112 |
113 | news = new News();
114 | news.setNewsTitle(getString(R.string.news_title8));
115 | news.setNewsBody(getString(R.string.news_body8));
116 | group3.addItem(news);
117 |
118 |
119 | mCollectionView.updateInventory(inventory);
120 |
121 | }
122 |
123 |
124 | @Override
125 | public RecyclerView.ViewHolder newCollectionHeaderView(Context context, int groupOrdinal, ViewGroup parent) {
126 | // Create a new view.
127 | View v = LayoutInflater.from(context)
128 | .inflate(R.layout.header_row_item, parent, false);
129 |
130 | return new TitleHolder(v);
131 | }
132 |
133 | @Override
134 | public RecyclerView.ViewHolder newCollectionItemView(Context context, int groupOrdinal, ViewGroup parent) {
135 | // Create a new view.
136 | View v = LayoutInflater.from(context)
137 | .inflate(R.layout.text_row_item, parent, false);
138 |
139 | return new NewsItemHolder(v);
140 | }
141 |
142 | @Override
143 | public void bindCollectionHeaderView(Context context, RecyclerView.ViewHolder holder, int groupOrdinal, String headerItem) {
144 | ((TitleHolder) holder).getTextView().setText((String) headerItem);
145 | }
146 |
147 | @Override
148 | public void bindCollectionItemView(Context context, RecyclerView.ViewHolder holder, int groupOrdinal, News item) {
149 | NewsItemHolder newsItemHolder = (NewsItemHolder) holder;
150 | newsItemHolder.getTextViewTitle().setText(item.getNewsTitle());
151 | newsItemHolder.getTextViewDescrption().setText(item.getNewsBody());
152 | }
153 |
154 |
155 | public static class TitleHolder extends RecyclerView.ViewHolder {
156 |
157 | private final TextView textView;
158 |
159 | public TitleHolder(View itemView) {
160 | super(itemView);
161 | textView = (TextView) itemView.findViewById(R.id.title);
162 | }
163 |
164 | public TextView getTextView() {
165 | return textView;
166 | }
167 | }
168 |
169 | public static class NewsItemHolder extends RecyclerView.ViewHolder {
170 |
171 |
172 | private final TextView tvTitle;
173 | private final TextView tvDescription;
174 |
175 | public NewsItemHolder(View v) {
176 | super(v);
177 | // Define click listener for the ViewHolder's View.
178 | v.setOnClickListener(new View.OnClickListener() {
179 | @Override
180 | public void onClick(View v) {
181 | Log.d(TAG, "Element " + getPosition() + " clicked.");
182 | }
183 | });
184 | tvTitle = (TextView) v.findViewById(R.id.title);
185 | tvDescription = (TextView) v.findViewById(R.id.description);
186 | }
187 |
188 | public TextView getTextViewTitle() {
189 | return tvTitle;
190 | }
191 |
192 | public TextView getTextViewDescrption() {
193 | return tvDescription;
194 | }
195 | }
196 |
197 |
198 | @Override
199 | public boolean onCreateOptionsMenu(Menu menu) {
200 | super.onCreateOptionsMenu(menu);
201 |
202 | MenuInflater inflater = getMenuInflater();
203 | inflater.inflate(R.menu.main, menu);
204 | return true;
205 | }
206 |
207 | @Override
208 | public boolean onOptionsItemSelected(MenuItem item) {
209 | if (item.getItemId() == R.id.launch) {
210 | Intent intent = new Intent(this, AsyncActivity.class);
211 | startActivity(intent);
212 | }
213 |
214 | return super.onOptionsItemSelected(item);
215 | }
216 | }
217 |
218 |
--------------------------------------------------------------------------------
/sampleapp/src/main/java/com/ericliu/asyncexpandablelistsample/News.java:
--------------------------------------------------------------------------------
1 | package com.ericliu.asyncexpandablelistsample;
2 |
3 | /**
4 | * Created by ericliu on 12/10/16.
5 | */
6 |
7 | public class News {
8 | private String newsTitle;
9 | private String newsBody;
10 |
11 |
12 | public String getNewsTitle() {
13 | return newsTitle;
14 | }
15 |
16 | public void setNewsTitle(String newsTitle) {
17 | this.newsTitle = newsTitle;
18 | }
19 |
20 | public String getNewsBody() {
21 | return newsBody;
22 | }
23 |
24 | public void setNewsBody(String newsBody) {
25 | this.newsBody = newsBody;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable-hdpi/ic_arrow_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/sampleapp/src/main/res/drawable-hdpi/ic_arrow_down.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable-hdpi/ic_arrow_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/sampleapp/src/main/res/drawable-hdpi/ic_arrow_up.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/sampleapp/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable-hdpi/tile.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/sampleapp/src/main/res/drawable-hdpi/tile.9.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable-mdpi/ic_arrow_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/sampleapp/src/main/res/drawable-mdpi/ic_arrow_down.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable-mdpi/ic_arrow_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/sampleapp/src/main/res/drawable-mdpi/ic_arrow_up.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/sampleapp/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable-xhdpi/ic_arrow_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/sampleapp/src/main/res/drawable-xhdpi/ic_arrow_down.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable-xhdpi/ic_arrow_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/sampleapp/src/main/res/drawable-xhdpi/ic_arrow_up.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/sampleapp/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable-xxhdpi/ic_arrow_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/sampleapp/src/main/res/drawable-xxhdpi/ic_arrow_down.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable-xxhdpi/ic_arrow_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/sampleapp/src/main/res/drawable-xxhdpi/ic_arrow_up.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ericliu001/async-expandable-list/8db3d7f6e45020c950c5877a94c701ec640c3a4c/sampleapp/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/layout/activity_async.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
16 |
23 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/layout/header_row_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
32 |
33 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/layout/header_row_item_async.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
31 |
32 |
33 |
36 |
37 |
38 |
52 |
53 |
54 |
65 |
66 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/layout/recycler_view_frag.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
22 |
28 |
32 |
36 |
37 |
38 |
42 |
43 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/layout/text_row_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
31 |
32 |
40 |
41 |
49 |
50 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/layout/text_row_item_async.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
31 |
32 |
40 |
41 |
49 |
50 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
23 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #00BCD4
5 | #00838F
6 | #fff
7 |
8 | #f5f5f5
9 |
10 | #de000000
11 | #8a000000
12 | #60000000
13 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | 72dp
20 |
21 | 16dp
22 | 16dp
23 |
24 |
25 | 48dp
26 | 2dp
27 | 0dp
28 | 4dp
29 |
30 |
31 |
32 |
33 | 8dp
34 | 4dp
35 | 16dp
36 |
37 | 4dp
38 | 8dp
39 | 16dp
40 | 32dp
41 | 64dp
42 |
43 | 11sp
44 | 12sp
45 | 13sp
46 | 14sp
47 | 18sp
48 | 20sp
49 | 6sp
50 |
51 |
52 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | AsyncExpandableList
20 |
21 | Element
22 | Grid Layout Manager
23 | Linear Layout Manager
24 | CollectionView
25 | AsyncExpandableListView
26 | Top Stories
27 | Australian Police Arrest 2 Sydney Teens, Seize Knives
28 | SYDNEY - Australian police arrested two teenagers and seized knives in Sydney on Wednesday as the country marked the 14th anniversary of extremist bombings in Indonesia that killed 202, including 88 Australians, police said.
29 | Queensland report outlines 50 per cent renewable energy map
30 | The security of Queensland\'s power supply won\'t be undermined by a government target of 50 per cent renewable energy by 2030, Energy Minister Mark Bailey says.
31 | VB and sheep farms, Lee brings the laughs
32 | VB beer, barbecues and sheep farms - they\'re not the kind of things you expect to hear from a visiting leader in a formal speech to federal parliament.
33 | World
34 | \'Sanctions brought nothing\': German politicians call for rapprochement with Russia
35 | The current policy of “saber rattling” should not continue, Erwin Sellering, the prime minister of the German state of Mecklenburg-Western Pomerania, told Germany\'s weekly Welt am Sonntag newspaper, as he called for lifting anti-Russian sanctions.
36 | Thai prince to visit ailing king, hospital says as health fears grow
37 | Thai King Bhumibol Adulyadej attends a parade to mark his 81st birthday in Bangkok, Thailand, 02 December 2008. (AAP ). Previous Next Show Grid.
38 | Australia
39 | \'I beg you, do not just put it in the filing cabinet\', witness pleads at NT Royal Commission
40 | The survival of Indigenous people in the Northern Territory depended on the outcome of the Royal Commission into Child Protection and Detention, a witness told the commission hearing in Darwin.
41 | HSC 2016: 77000 students to sit first exams across NSW
42 | More than 77,000 NSW high school students will sit their first HSC exams this week as one of the final cohorts to sit the test before the NSW government enacts sweeping reforms across the state.
43 | Lawyers meet voluntary pro bono target for first time since 2013
44 | A voluntary target for the amount of pro bono work done by Australian lawyers has been met for the first time since 2013. Key points: The Australian Pro Bono Centre\'s asks lawyers to do 35 hours of free community work a year; Pro bono services can help ...\n
45 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
19 |
20 |
23 |
24 |
29 |
30 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':asyncexpandablelist', ':sampleapp'
--------------------------------------------------------------------------------