11 |
12 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## Version 1.5 - September 11th, 2020
4 |
5 | - BUGFIX: fix writeToParcel IndexOutOfBounds (#183).
6 | - Feature: explicitly expand/collapse state instead of just toggle (#146).
7 | - Documentation improvements (#48, #66, #143).
8 | - New maintainer. Hi, I'm Mike.
9 |
10 | ## Version 1.4 - January 17th 2017
11 | - Upgrade Robolectric version to `3.2.1`
12 | - Fix how `GroupViewHolder`'s expand and collapse method are called
13 | - Avoid unnecessary conversion between `ExpandableListPosition` and group index
14 | - Add layout for each sample activity
15 | - Only update children check states if group is expanded
16 | - Update how `clearChoices()` propagates view updates
17 | - Remove unnecessary check for only `CheckedTextView` and toggle everything in `CheckableChildViewHolder`
18 | - Update `ExpandActivity` sample to turn off flash animation
19 |
20 | ## Version 1.3 - December 15th 2016
21 | - Add ability to programmatically toggle group using group
22 | - Only expand / collapse groups if they have items
23 | - Properly deserialize expandable group from Parcel
24 |
25 | ## Version 1.2 - November 14th 2016
26 | - Ensure onSaveInstanceState is saving a `java.util.ArrayList`
27 | - Replace `SparseBooleanArray` with Boolean Array in `CheckedExpandableGroup`
28 | - Replace SparseBooleanArray in favor of Boolean array in `ExpandCollapseController`
29 |
30 |
31 | ## Version 1.1 - October 13th 2016
32 | - Upgrade Gradle build tools to version `2.2.0`
33 | - Fix bug where expand and collapse were reversed in `GroupViewHolder`
34 |
35 | ## Version 1.0 - August 16th 2016
36 | Initial Release
37 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Expandable Recycler View
2 |
3 | We love pull requests from everyone. By participating in this project, you
4 | agree to abide by the thoughtbot [code of conduct].
5 |
6 | [code of conduct]: https://thoughtbot.com/open-source-code-of-conduct
7 |
8 | Here are some ways *you* can contribute:
9 |
10 | * by reporting bugs
11 | * by suggesting new features
12 | * by writing or editing documentation
13 | * by writing specifications
14 | * by writing code ( **no patch is too small** : fix typos, add comments, clean up inconsistent whitespace )
15 | * by refactoring code
16 | * by closing [issues][]
17 | * by reviewing patches
18 |
19 | [issues]: https://github.com/thoughtbot/expandable-recycler-view/issues
20 |
21 | ## Submitting an Issue
22 | We use the [GitHub issue tracker][issues] to track bugs and features. Before
23 | submitting a bug report or feature request, check to make sure it hasn't
24 | already been submitted. When submitting a bug report, please include a [Gist][]
25 | that includes a stack trace and any details that may be necessary to reproduce
26 | the bug, including your Java version, Android Studio version, and operating system.
27 | Ideally, a bug report should include a pull request with failing specs.
28 |
29 | [gist]: https://gist.github.com/
30 |
31 | ## Submitting a Pull Request
32 | 1. [Fork][fork] the [official repository][repo].
33 | 2. [Create a topic branch.][branch]
34 | 3. Implement your feature or bug fix
35 | 4. Add, commit, and push your changes.
36 | 5. [Submit a pull request.][pr]
37 |
38 | ## Notes
39 | * Please add tests if you changed code.
40 | * Please make sure your code follows the [thoughtbot java style guide][styles]
41 |
42 | [repo]: https://github.com/thoughtbot/expandable-recycler-view/tree/master/expandablerecyclerview
43 | [fork]: https://help.github.com/articles/fork-a-repo/
44 | [branch]: https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/
45 | [pr]: https://help.github.com/articles/using-pull-requests/
46 | [styles]: https://github.com/thoughtbot/guides/tree/master/style/android/java
47 |
48 | Inspired by https://github.com/middleman/middleman-heroku/blob/master/CONTRIBUTING.md
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016 Amanda Hill and thoughtbot, inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/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.2.3'
9 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4'
10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | jcenter()
20 | }
21 | }
22 |
23 | task clean(type: Delete) {
24 | delete rootProject.buildDir
25 | }
26 |
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | #
2 | # Build configuration for Circle CI
3 | #
4 | general:
5 | artifacts:
6 | - "app/build/outputs/apk"
7 | - "app/build/reports/tests"
8 | - "app/build/outputs/androidTest-results"
9 |
10 | machine:
11 | timezone:
12 | America/Los_Angeles
13 | java:
14 | version: oraclejdk8
15 | environment:
16 | ANDROID_HOME: /usr/local/android-sdk-linux
17 |
18 | dependencies:
19 | pre:
20 | - source environmentSetup.sh && get_android_sdk_24
21 | - mkdir -p $ANDROID_HOME"/licenses"
22 | - echo $ANDROID_SDK_LICENSE > $ANDROID_HOME"/licenses/android-sdk-license"
23 | override:
24 | - ./gradlew clean assembleRelease -PdisablePreDex
25 |
26 | test:
27 | override:
28 | - ./gradlew testRelease
29 |
--------------------------------------------------------------------------------
/environmentSetup.sh:
--------------------------------------------------------------------------------
1 | # Environment Setup which is required for Circle CI
2 |
3 | function affirmative_android_update {
4 | echo y | android update sdk --no-ui --all --filter "$1"
5 | }
6 |
7 | function get_android_sdk_24 {
8 | # fix the CircleCI path
9 | # export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$PATH"
10 |
11 | DEPS="$ANDROID_HOME/installed-dependencies"
12 |
13 | if [ ! -e $DEPS ]; then
14 | echo Fetch and install Android SDK 24
15 | echo y | android update sdk --no-ui --all --filter tools,platform-tools,build-tools-24.0.2,android-24,extra-google-m2repository,extra-google-google_play_services,extra-android-m2repository,extra-android-support
16 | # create the folder so we won't do this again (this is soooo Apache Ant right here)
17 | touch $DEPS
18 | fi
19 | }
20 |
--------------------------------------------------------------------------------
/expandablecheckrecyclerview/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/expandablecheckrecyclerview/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | //ext {
4 | // bintrayRepo = 'maven'
5 | // bintrayName = 'expandablecheckrecyclerview'
6 | //
7 | // publishedGroupId = 'com.thoughtbot'
8 | // libraryName = 'expandablecheckrecyclerview'
9 | // artifact = 'expandablecheckrecyclerview'
10 | //
11 | // libraryDescription =
12 | // 'Custom Android RecyclerViewAdapters that collapse and expand and support checking and unchecking children'
13 | //
14 | // siteUrl = 'https://github.com/thoughtbot/expandable-recycler-view'
15 | // gitUrl = 'https://github.com/thoughtbot/expandable-recycler-view.git'
16 | //
17 | // libraryVersion = '1.5'
18 | //
19 | // developerId = 'mandybess'
20 | // developerName = 'Amanda Hill'
21 | // developerEmail = 'amandabesshill@gmail.com'
22 | //
23 | // licenseName = 'The Apache Software License, Version 2.0'
24 | // licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
25 | // allLicenses = ["Apache-2.0"]
26 | //}
27 |
28 | android {
29 | compileSdkVersion 23
30 | buildToolsVersion "23.0.3"
31 |
32 | defaultConfig {
33 | minSdkVersion 16
34 | targetSdkVersion 23
35 | versionCode 5
36 | versionName "1.5"
37 | }
38 | buildTypes {
39 | release {
40 | minifyEnabled false
41 | }
42 | }
43 | }
44 |
45 | dependencies {
46 | compile fileTree(dir: 'libs', include: ['*.jar'])
47 | compile 'com.thoughtbot:expandablerecyclerview:1.3'
48 |
49 | //android
50 | compile 'com.android.support:appcompat-v7:23.4.0'
51 | compile 'com.android.support:recyclerview-v7:23.4.0'
52 |
53 | //unit tests
54 | testCompile 'junit:junit:4.12'
55 | testCompile 'org.robolectric:robolectric:3.2.1'
56 | testCompile 'org.mockito:mockito-core:1.10.5'
57 | }
58 |
59 | //apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle'
60 | //apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle'
61 |
--------------------------------------------------------------------------------
/expandablecheckrecyclerview/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/expandablecheckrecyclerview/src/main/java/com/thoughtbot/expandablecheckrecyclerview/CheckableChildRecyclerViewAdapter.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablecheckrecyclerview;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import com.thoughtbot.expandablecheckrecyclerview.listeners.OnCheckChildClickListener;
8 | import com.thoughtbot.expandablecheckrecyclerview.listeners.OnChildCheckChangedListener;
9 | import com.thoughtbot.expandablecheckrecyclerview.listeners.OnChildrenCheckStateChangedListener;
10 | import com.thoughtbot.expandablecheckrecyclerview.models.CheckedExpandableGroup;
11 | import com.thoughtbot.expandablecheckrecyclerview.viewholders.CheckableChildViewHolder;
12 | import com.thoughtbot.expandablerecyclerview.ExpandableRecyclerViewAdapter;
13 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
14 | import com.thoughtbot.expandablerecyclerview.models.ExpandableListPosition;
15 | import com.thoughtbot.expandablerecyclerview.viewholders.GroupViewHolder;
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | public abstract class CheckableChildRecyclerViewAdapter
20 | extends ExpandableRecyclerViewAdapter
21 | implements OnChildCheckChangedListener, OnChildrenCheckStateChangedListener {
22 |
23 | private static final String CHECKED_STATE_MAP = "child_check_controller_checked_state_map";
24 |
25 | private ChildCheckController childCheckController;
26 | private OnCheckChildClickListener childClickListener;
27 |
28 | public CheckableChildRecyclerViewAdapter(List extends CheckedExpandableGroup> groups) {
29 | super(groups);
30 | childCheckController = new ChildCheckController(expandableList, this);
31 | }
32 |
33 | @Override
34 | public CCVH onCreateChildViewHolder(ViewGroup parent, int viewType) {
35 | CCVH CCVH = onCreateCheckChildViewHolder(parent, viewType);
36 | CCVH.setOnChildCheckedListener(this);
37 | return CCVH;
38 | }
39 |
40 | @Override
41 | public void onBindChildViewHolder(CCVH holder, int flatPosition, ExpandableGroup group,
42 | int childIndex) {
43 | ExpandableListPosition listPosition = expandableList.getUnflattenedPosition(flatPosition);
44 | holder.onBindViewHolder(flatPosition, childCheckController.isChildChecked(listPosition));
45 | onBindCheckChildViewHolder(holder, flatPosition, (CheckedExpandableGroup) group, childIndex);
46 | }
47 |
48 | @Override
49 | public void onChildCheckChanged(View view, boolean checked, int flatPos) {
50 | ExpandableListPosition listPos = expandableList.getUnflattenedPosition(flatPos);
51 | childCheckController.onChildCheckChanged(checked, listPos);
52 | if (childClickListener != null) {
53 | childClickListener.onCheckChildCLick(view, checked,
54 | (CheckedExpandableGroup) expandableList.getExpandableGroup(listPos), listPos.childPos);
55 | }
56 | }
57 |
58 | @Override
59 | public void updateChildrenCheckState(int firstChildFlattenedIndex, int numChildren) {
60 | notifyItemRangeChanged(firstChildFlattenedIndex, numChildren);
61 | }
62 |
63 | public void setChildClickListener(OnCheckChildClickListener listener) {
64 | childClickListener = listener;
65 | }
66 |
67 | /**
68 | * Stores the checked state map across state loss.
69 | *
70 | * Should be called from whatever {@link Activity} that hosts the RecyclerView that {@link
71 | * CheckableChildRecyclerViewAdapter} is attached to.
72 | *
73 | * This will make sure to add the checked state map as an extra to the
74 | * instance state bundle to be used in {@link #onRestoreInstanceState(Bundle)}.
75 | *
76 | * @param outState The {@code Bundle} into which to store the
77 | * chekced state map
78 | */
79 | @Override
80 | public void onSaveInstanceState(Bundle outState) {
81 | outState.putParcelableArrayList(CHECKED_STATE_MAP, new ArrayList(expandableList.groups));
82 | super.onSaveInstanceState(outState);
83 | }
84 |
85 | /**
86 | * Fetches the checked state map from the saved instance state {@link Bundle}
87 | * and restores the checked states of all of the child list items.
88 | *
89 | * Should be called from {@link Activity#onRestoreInstanceState(Bundle)} in
90 | * the {@link Activity} that hosts the RecyclerView that this
91 | * {@link CheckableChildRecyclerViewAdapter} is attached to.
92 | *
93 | *
94 | * @param savedInstanceState The {@code Bundle} from which the expanded
95 | * state map is loaded
96 | */
97 | @Override
98 | public void onRestoreInstanceState(Bundle savedInstanceState) {
99 | if (savedInstanceState == null || !savedInstanceState.containsKey(CHECKED_STATE_MAP)) {
100 | return;
101 | }
102 | expandableList.groups = savedInstanceState.getParcelableArrayList(CHECKED_STATE_MAP);
103 | super.onRestoreInstanceState(savedInstanceState);
104 | }
105 |
106 | /**
107 | * Manually (programmatically) update the check state of a child
108 | *
109 | * @param checked the desired check state, true will check the item, false will uncheck it if
110 | * possible
111 | * @param groupIndex the index of the {@code ExpandableGroup} within {@code getGroups()}
112 | * @param childIndex the index of the child within it's group
113 | */
114 | public void checkChild(boolean checked, int groupIndex, int childIndex) {
115 | childCheckController.checkChild(checked, groupIndex, childIndex);
116 | if (childClickListener != null) {
117 | childClickListener.onCheckChildCLick(null, checked,
118 | (CheckedExpandableGroup) expandableList.groups.get(groupIndex), childIndex);
119 | }
120 | }
121 |
122 | /**
123 | * Clear any choices previously checked
124 | */
125 | public void clearChoices() {
126 | childCheckController.clearCheckStates();
127 |
128 | //only update the child views that are visible (i.e. their group is expanded)
129 | for (int i = 0; i < getGroups().size(); i++) {
130 | ExpandableGroup group = getGroups().get(i);
131 | if (isGroupExpanded(group)) {
132 | notifyItemRangeChanged(expandableList.getFlattenedFirstChildIndex(i), group.getItemCount());
133 | }
134 | }
135 | }
136 |
137 | /**
138 | * Called from #onCreateViewHolder(ViewGroup, int) when the list item created is a child
139 | *
140 | * @param parent the {@link ViewGroup} in the list for which a {@link CCVH} is being created
141 | * @return A {@link CCVH} corresponding to child list item with the {@code ViewGroup} parent
142 | */
143 | public abstract CCVH onCreateCheckChildViewHolder(ViewGroup parent, int viewType);
144 |
145 | /**
146 | * Called from onBindViewHolder(RecyclerView.ViewHolder, int) when the list item
147 | * bound to is a child.
148 | *
149 | * Bind data to the {@link CCVH} here.
150 | *
151 | * @param holder The {@code CCVH} to bind data to
152 | * @param flatPosition the flat position (raw index) in the list at which to bind the child
153 | * @param group The {@link CheckedExpandableGroup} that the the child list item belongs to
154 | * @param childIndex the index of this child within it's {@link CheckedExpandableGroup}
155 | */
156 | public abstract void onBindCheckChildViewHolder(CCVH holder, int flatPosition,
157 | CheckedExpandableGroup group, int childIndex);
158 | }
159 |
--------------------------------------------------------------------------------
/expandablecheckrecyclerview/src/main/java/com/thoughtbot/expandablecheckrecyclerview/ChildCheckController.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablecheckrecyclerview;
2 |
3 | import android.widget.Checkable;
4 | import android.widget.ExpandableListView;
5 | import com.thoughtbot.expandablecheckrecyclerview.listeners.OnChildrenCheckStateChangedListener;
6 | import com.thoughtbot.expandablecheckrecyclerview.models.CheckedExpandableGroup;
7 | import com.thoughtbot.expandablecheckrecyclerview.viewholders.CheckableChildViewHolder;
8 | import com.thoughtbot.expandablerecyclerview.models.ExpandableList;
9 | import com.thoughtbot.expandablerecyclerview.models.ExpandableListPosition;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | public class ChildCheckController {
14 |
15 | private ExpandableList expandableList;
16 | private OnChildrenCheckStateChangedListener childrenUpdateListener;
17 | private List initialCheckedPositions;
18 |
19 | public ChildCheckController(ExpandableList expandableList,
20 | OnChildrenCheckStateChangedListener listener) {
21 | this.expandableList = expandableList;
22 | this.childrenUpdateListener = listener;
23 | initialCheckedPositions = getCheckedPositions();
24 | }
25 |
26 | /**
27 | * Triggered by a click event on a {@link CheckableChildViewHolder} causing the {@link Checkable}
28 | * object to change checked states
29 | *
30 | * @param checked The current checked state of the view
31 | * @param listPosition The flat position (raw index) of the {@link CheckableChildViewHolder}
32 | */
33 | public void onChildCheckChanged(boolean checked, ExpandableListPosition listPosition) {
34 | CheckedExpandableGroup group =
35 | (CheckedExpandableGroup) expandableList.groups.get(listPosition.groupPos);
36 | group.onChildClicked(listPosition.childPos, checked);
37 | if (childrenUpdateListener != null) {
38 | childrenUpdateListener.updateChildrenCheckState(
39 | expandableList.getFlattenedFirstChildIndex(listPosition),
40 | expandableList.getExpandableGroupItemCount(listPosition));
41 | }
42 | }
43 |
44 | public void checkChild(boolean checked, int groupIndex, int childIndex) {
45 | CheckedExpandableGroup group = (CheckedExpandableGroup) expandableList.groups.get(groupIndex);
46 | group.onChildClicked(childIndex, checked);
47 | if (childrenUpdateListener != null) {
48 | //only update children check states if group is expanded
49 | boolean isGroupExpanded = expandableList.expandedGroupIndexes[groupIndex];
50 | if (isGroupExpanded) {
51 | childrenUpdateListener.updateChildrenCheckState(
52 | expandableList.getFlattenedFirstChildIndex(groupIndex), group.getItemCount());
53 | }
54 | }
55 | }
56 |
57 | /**
58 | * @param listPosition the ExpandableListPosition representation of a child list item
59 | * @return The current checked state of the view
60 | */
61 | public boolean isChildChecked(ExpandableListPosition listPosition) {
62 | CheckedExpandableGroup group =
63 | (CheckedExpandableGroup) expandableList.groups.get(listPosition.groupPos);
64 | return group.isChildChecked(listPosition.childPos);
65 | }
66 |
67 | /**
68 | * @return list of indexes of all checked child items
69 | */
70 | public List getCheckedPositions() {
71 | List selected = new ArrayList<>();
72 | for (int i = 0; i < expandableList.groups.size(); i++) {
73 | if (expandableList.groups.get(i) instanceof CheckedExpandableGroup) {
74 | CheckedExpandableGroup group = (CheckedExpandableGroup) expandableList.groups.get(i);
75 | for (int j = 0; j < group.getItemCount(); j++) {
76 | if (group.isChildChecked(j)) {
77 | long packedPosition = ExpandableListView.getPackedPositionForChild(i, j);
78 | selected.add(expandableList.getFlattenedChildIndex(packedPosition));
79 | }
80 | }
81 | }
82 | }
83 | return selected;
84 | }
85 |
86 | /**
87 | * @return true if the checked state of any of the child items has changed since this class was
88 | * initialized
89 | */
90 | public boolean checksChanged() {
91 | return !initialCheckedPositions.equals(getCheckedPositions());
92 | }
93 |
94 | /**
95 | * Clear any choices previously checked
96 | */
97 | public void clearCheckStates() {
98 | for (int i = 0; i < expandableList.groups.size(); i++) {
99 | CheckedExpandableGroup group = (CheckedExpandableGroup) expandableList.groups.get(i);
100 | group.clearSelections();
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/expandablecheckrecyclerview/src/main/java/com/thoughtbot/expandablecheckrecyclerview/listeners/OnCheckChildClickListener.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablecheckrecyclerview.listeners;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.View;
5 | import com.thoughtbot.expandablecheckrecyclerview.CheckableChildRecyclerViewAdapter;
6 | import com.thoughtbot.expandablecheckrecyclerview.models.CheckedExpandableGroup;
7 | import com.thoughtbot.expandablecheckrecyclerview.viewholders.CheckableChildViewHolder;
8 |
9 | /**
10 | * Interface definition for a callback to be invoked when a {@link CheckableChildViewHolder}
11 | * has been clicked.
12 | */
13 | public interface OnCheckChildClickListener {
14 | /**
15 | * Callback method to be invoked when a child in the {@link CheckableChildRecyclerViewAdapter}
16 | * has been clicked.
17 | *
18 | * @param checked the current check state of the child
19 | * @param v The view within the RecyclerView that was clicked
20 | * @param group The {@link CheckedExpandableGroup} that contains the child that was clicked
21 | * @param childIndex The child position within the {@link CheckedExpandableGroup}
22 | */
23 | void onCheckChildCLick(View v, boolean checked, CheckedExpandableGroup group, int childIndex);
24 | }
25 |
--------------------------------------------------------------------------------
/expandablecheckrecyclerview/src/main/java/com/thoughtbot/expandablecheckrecyclerview/listeners/OnChildCheckChangedListener.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablecheckrecyclerview.listeners;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.View;
5 | import com.thoughtbot.expandablecheckrecyclerview.viewholders.CheckableChildViewHolder;
6 |
7 | /**
8 | * Interface definition for a callback to be invoked when a CheckableChildViewHolder#checkable
9 | * has been clicked.
10 | */
11 | public interface OnChildCheckChangedListener {
12 |
13 | /**
14 | * @param checked The current checked state of the view
15 | * @param flatPos The flat position (raw index) of the the child within the RecyclerView
16 | */
17 | void onChildCheckChanged(View view, boolean checked, int flatPos);
18 |
19 | }
--------------------------------------------------------------------------------
/expandablecheckrecyclerview/src/main/java/com/thoughtbot/expandablecheckrecyclerview/listeners/OnChildrenCheckStateChangedListener.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablecheckrecyclerview.listeners;
2 |
3 | import com.thoughtbot.expandablecheckrecyclerview.models.CheckedExpandableGroup;
4 |
5 | public interface OnChildrenCheckStateChangedListener {
6 |
7 | /**
8 | * @param firstChildFlattenedIndex the flat position of the first child in the {@link
9 | * CheckedExpandableGroup}
10 | * @param numChildren the total number of children in the {@link CheckedExpandableGroup}
11 | */
12 | void updateChildrenCheckState(int firstChildFlattenedIndex, int numChildren);
13 | }
14 |
--------------------------------------------------------------------------------
/expandablecheckrecyclerview/src/main/java/com/thoughtbot/expandablecheckrecyclerview/models/CheckedExpandableGroup.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablecheckrecyclerview.models;
2 |
3 | import android.os.Parcel;
4 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | /**
9 | * An extension of ExpandableGroup that holds onto the checked state of it's children
10 | */
11 | public abstract class CheckedExpandableGroup extends ExpandableGroup {
12 |
13 | public boolean[] selectedChildren;
14 |
15 | public CheckedExpandableGroup(String title, List items) {
16 | super(title, items);
17 | selectedChildren = new boolean[items.size()];
18 | for (int i = 0; i < items.size(); i++) {
19 | selectedChildren[i] = false;
20 | }
21 | }
22 |
23 | public void checkChild(int childIndex) {
24 | selectedChildren[childIndex] = true;
25 | }
26 |
27 | public void unCheckChild(int childIndex) {
28 | selectedChildren[childIndex] = false;
29 | }
30 |
31 | public boolean isChildChecked(int childIndex) {
32 | return selectedChildren[childIndex];
33 | }
34 |
35 | public void clearSelections() {
36 | if (selectedChildren != null) {
37 | Arrays.fill(selectedChildren, false);
38 | }
39 | }
40 |
41 | protected CheckedExpandableGroup(Parcel in) {
42 | super(in);
43 | selectedChildren = in.createBooleanArray();
44 | }
45 |
46 | @Override
47 | public void writeToParcel(Parcel dest, int flags) {
48 | super.writeToParcel(dest, flags);
49 | dest.writeBooleanArray(selectedChildren);
50 | }
51 |
52 | @Override
53 | public int describeContents() {
54 | return 0;
55 | }
56 |
57 | public abstract void onChildClicked(int childIndex, boolean checked);
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/expandablecheckrecyclerview/src/main/java/com/thoughtbot/expandablecheckrecyclerview/models/MultiCheckExpandableGroup.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablecheckrecyclerview.models;
2 |
3 | import android.os.Parcel;
4 | import java.util.List;
5 |
6 | /**
7 | * A subclass of {@link CheckedExpandableGroup} that allows for multiple children to be checked at
8 | * one time
9 | */
10 | public class MultiCheckExpandableGroup extends CheckedExpandableGroup {
11 |
12 | public MultiCheckExpandableGroup(String title, List items) {
13 | super(title, items);
14 | }
15 |
16 | @Override
17 | public void onChildClicked(int childIndex, boolean checked) {
18 | if (checked) {
19 | checkChild(childIndex);
20 | } else {
21 | unCheckChild(childIndex);
22 | }
23 | }
24 |
25 | protected MultiCheckExpandableGroup(Parcel in) {
26 | super(in);
27 | }
28 |
29 | @Override
30 | public void writeToParcel(Parcel dest, int flags) {
31 | super.writeToParcel(dest, flags);
32 | }
33 |
34 | @Override
35 | public int describeContents() {
36 | return 0;
37 | }
38 |
39 | @SuppressWarnings("unused")
40 | public static final Creator CREATOR =
41 | new Creator() {
42 | @Override
43 | public MultiCheckExpandableGroup createFromParcel(Parcel in) {
44 | return new MultiCheckExpandableGroup(in);
45 | }
46 |
47 | @Override
48 | public MultiCheckExpandableGroup[] newArray(int size) {
49 | return new MultiCheckExpandableGroup[size];
50 | }
51 | };
52 | }
53 |
--------------------------------------------------------------------------------
/expandablecheckrecyclerview/src/main/java/com/thoughtbot/expandablecheckrecyclerview/models/SingleCheckExpandableGroup.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablecheckrecyclerview.models;
2 |
3 | import android.os.Parcel;
4 | import java.util.List;
5 |
6 | /**
7 | * A subclass of {@link CheckedExpandableGroup} that allows for only *one* child to be checked at
8 | * one time
9 | */
10 | public class SingleCheckExpandableGroup extends CheckedExpandableGroup {
11 |
12 | public SingleCheckExpandableGroup(String title, List items) {
13 | super(title, items);
14 | }
15 |
16 | @Override
17 | public void onChildClicked(int childIndex, boolean checked) {
18 | if (checked) {
19 | for (int i = 0; i < getItemCount(); i++) {
20 | unCheckChild(i);
21 | }
22 | checkChild(childIndex);
23 | }
24 | }
25 |
26 | protected SingleCheckExpandableGroup(Parcel in) {
27 | super(in);
28 | }
29 |
30 | @Override
31 | public void writeToParcel(Parcel dest, int flags) {
32 | super.writeToParcel(dest, flags);
33 | }
34 |
35 | @Override
36 | public int describeContents() {
37 | return 0;
38 | }
39 |
40 | @SuppressWarnings("unused")
41 | public static final Creator CREATOR =
42 | new Creator() {
43 | @Override
44 | public SingleCheckExpandableGroup createFromParcel(Parcel in) {
45 | return new SingleCheckExpandableGroup(in);
46 | }
47 |
48 | @Override
49 | public SingleCheckExpandableGroup[] newArray(int size) {
50 | return new SingleCheckExpandableGroup[size];
51 | }
52 | };
53 | }
54 |
--------------------------------------------------------------------------------
/expandablecheckrecyclerview/src/main/java/com/thoughtbot/expandablecheckrecyclerview/viewholders/CheckableChildViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablecheckrecyclerview.viewholders;
2 |
3 | import android.view.View;
4 | import android.view.View.OnClickListener;
5 | import android.widget.Checkable;
6 | import com.thoughtbot.expandablecheckrecyclerview.listeners.OnChildCheckChangedListener;
7 | import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder;
8 |
9 | /**
10 | * An instance of ChildViewHolder that has a Checkable widget so that this view may
11 | * have a checked and unchecked state
12 | */
13 | public abstract class CheckableChildViewHolder extends ChildViewHolder implements OnClickListener {
14 |
15 | private OnChildCheckChangedListener listener;
16 | private Checkable checkable;
17 |
18 | public CheckableChildViewHolder(View itemView) {
19 | super(itemView);
20 | itemView.setOnClickListener(this);
21 | }
22 |
23 | /**
24 | * @param flatPos the raw index of this CheckableChildViewHolder in the
25 | * RecyclerView
26 | * @param checked the state to set the Checkable widget to
27 | * see ChildCheckController#isChildChecked(ExpandableListPosition)
28 | */
29 | public void onBindViewHolder(int flatPos, boolean checked) {
30 | checkable = getCheckable();
31 | checkable.setChecked(checked);
32 | }
33 |
34 | @Override
35 | public void onClick(View v) {
36 | checkable.toggle();
37 | if (listener != null) {
38 | listener.onChildCheckChanged(v, checkable.isChecked(), getAdapterPosition());
39 | }
40 | }
41 |
42 | public void setOnChildCheckedListener(OnChildCheckChangedListener listener) {
43 | this.listener = listener;
44 | }
45 |
46 | /**
47 | * Called during {@link #onBindViewHolder(int, boolean)}
48 | *
49 | * return a {@link Checkable} widget associated with this ViewHolder
50 | */
51 | public abstract Checkable getCheckable();
52 | }
--------------------------------------------------------------------------------
/expandablecheckrecyclerview/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ExpandableCheckRecyclerView
3 |
4 |
--------------------------------------------------------------------------------
/expandablerecyclerview/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/expandablerecyclerview/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | //ext {
4 | // bintrayRepo = 'maven'
5 | // bintrayName = 'expandablerecyclerview'
6 | //
7 | // publishedGroupId = 'com.thoughtbot'
8 | // libraryName = 'expandablerecyclerview'
9 | // artifact = 'expandablerecyclerview'
10 | //
11 | // libraryDescription = 'Custom Android RecyclerViewAdapters that collapse and expand'
12 | //
13 | // siteUrl = 'https://github.com/thoughtbot/expandable-recycler-view'
14 | // gitUrl = 'https://github.com/thoughtbot/expandable-recycler-view.git'
15 | //
16 | // libraryVersion = '1.4'
17 | //
18 | // developerId = 'mandybess'
19 | // developerName = 'Amanda Hill'
20 | // developerEmail = 'amandabesshill@gmail.com'
21 | //
22 | // licenseName = 'The Apache Software License, Version 2.0'
23 | // licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
24 | // allLicenses = ["Apache-2.0"]
25 | //}
26 |
27 | android {
28 | compileSdkVersion 23
29 | buildToolsVersion "23.0.3"
30 |
31 | defaultConfig {
32 | minSdkVersion 16
33 | targetSdkVersion 23
34 | versionCode 2
35 | versionName "1.4"
36 | }
37 | buildTypes {
38 | release {
39 | minifyEnabled false
40 | }
41 | }
42 | }
43 |
44 | dependencies {
45 | //android
46 | compile 'com.android.support:appcompat-v7:23.4.0'
47 | compile 'com.android.support:recyclerview-v7:23.4.0'
48 |
49 | //unit tests
50 | testCompile 'junit:junit:4.12'
51 | testCompile 'org.robolectric:robolectric:3.2.1'
52 | testCompile 'org.mockito:mockito-core:1.10.5'
53 | }
54 | //apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle'
55 | //apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle'
56 |
--------------------------------------------------------------------------------
/expandablerecyclerview/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/expandablerecyclerview/src/main/java/com/thoughtbot/expandablerecyclerview/ExpandCollapseController.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview;
2 |
3 | import com.thoughtbot.expandablerecyclerview.listeners.ExpandCollapseListener;
4 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
5 | import com.thoughtbot.expandablerecyclerview.models.ExpandableList;
6 | import com.thoughtbot.expandablerecyclerview.models.ExpandableListPosition;
7 |
8 | /**
9 | * This class is the sits between the backing {@link ExpandableList} and the {@link
10 | * ExpandableRecyclerViewAdapter} and mediates the expanding and collapsing of {@link
11 | * ExpandableGroup}
12 | */
13 | public class ExpandCollapseController {
14 |
15 | private ExpandCollapseListener listener;
16 | private ExpandableList expandableList;
17 |
18 | public ExpandCollapseController(ExpandableList expandableList, ExpandCollapseListener listener) {
19 | this.expandableList = expandableList;
20 | this.listener = listener;
21 | }
22 |
23 | /**
24 | * Collapse a group
25 | *
26 | * @param listPosition position of the group to collapse
27 | */
28 | private void collapseGroup(ExpandableListPosition listPosition) {
29 | expandableList.expandedGroupIndexes[listPosition.groupPos] = false;
30 | if (listener != null) {
31 | listener.onGroupCollapsed(expandableList.getFlattenedGroupIndex(listPosition) + 1,
32 | expandableList.groups.get(listPosition.groupPos).getItemCount());
33 | }
34 | }
35 |
36 | /**
37 | * Expand a group
38 | *
39 | * @param listPosition the group to be expanded
40 | */
41 | private void expandGroup(ExpandableListPosition listPosition) {
42 | expandableList.expandedGroupIndexes[listPosition.groupPos] = true;
43 | if (listener != null) {
44 | listener.onGroupExpanded(expandableList.getFlattenedGroupIndex(listPosition) + 1,
45 | expandableList.groups.get(listPosition.groupPos).getItemCount());
46 | }
47 | }
48 |
49 | /**
50 | * @param group the {@link ExpandableGroup} being checked for its collapsed state
51 | * @return true if {@code group} is expanded, false if it is collapsed
52 | */
53 | public boolean isGroupExpanded(ExpandableGroup group) {
54 | int groupIndex = expandableList.groups.indexOf(group);
55 | return expandableList.expandedGroupIndexes[groupIndex];
56 | }
57 |
58 | /**
59 | * @param flatPos the flattened position of an item in the list
60 | * @return true if {@code group} is expanded, false if it is collapsed
61 | */
62 | public boolean isGroupExpanded(int flatPos) {
63 | ExpandableListPosition listPosition = expandableList.getUnflattenedPosition(flatPos);
64 | return expandableList.expandedGroupIndexes[listPosition.groupPos];
65 | }
66 |
67 | /**
68 | * @param flatPos The flat list position of the group
69 | * @return false if the group is expanded, *after* the toggle, true if the group is now collapsed
70 | */
71 | public boolean toggleGroup(int flatPos) {
72 | ExpandableListPosition listPos = expandableList.getUnflattenedPosition(flatPos);
73 | boolean expanded = expandableList.expandedGroupIndexes[listPos.groupPos];
74 | if (expanded) {
75 | collapseGroup(listPos);
76 | } else {
77 | expandGroup(listPos);
78 | }
79 | return expanded;
80 | }
81 |
82 | public boolean toggleGroup(ExpandableGroup group) {
83 | ExpandableListPosition listPos =
84 | expandableList.getUnflattenedPosition(expandableList.getFlattenedGroupIndex(group));
85 | boolean expanded = expandableList.expandedGroupIndexes[listPos.groupPos];
86 | if (expanded) {
87 | collapseGroup(listPos);
88 | } else {
89 | expandGroup(listPos);
90 | }
91 | return expanded;
92 | }
93 |
94 | /**
95 | * @param group the {@link ExpandableGroup} being expanded
96 | */
97 | void expandGroup(ExpandableGroup group) {
98 | ExpandableListPosition listPos =
99 | expandableList.getUnflattenedPosition(expandableList.getFlattenedGroupIndex(group));
100 | boolean isExpanded = expandableList.expandedGroupIndexes[listPos.groupPos];
101 | // No-op on repeating calls
102 | if (!isExpanded) {
103 | expandGroup(listPos);
104 | }
105 | }
106 |
107 | /**
108 | * @param flatPos The flat list position of the group
109 | */
110 | void expandGroup(int flatPos) {
111 | ExpandableListPosition listPos = expandableList.getUnflattenedPosition(flatPos);
112 | boolean isExpanded = expandableList.expandedGroupIndexes[listPos.groupPos];
113 | // No-op on repeating calls
114 | if (!isExpanded) {
115 | expandGroup(listPos);
116 | }
117 | }
118 |
119 | /**
120 | * @param group the {@link ExpandableGroup} being collapsed
121 | */
122 | void collapseGroup(ExpandableGroup group) {
123 | ExpandableListPosition listPos =
124 | expandableList.getUnflattenedPosition(expandableList.getFlattenedGroupIndex(group));
125 | boolean isExpanded = expandableList.expandedGroupIndexes[listPos.groupPos];
126 | // No-op on repeating calls
127 | if (isExpanded) {
128 | collapseGroup(listPos);
129 | }
130 | }
131 |
132 | /**
133 | * @param flatPos The flat list position of the group
134 | */
135 | void collapseGroup(int flatPos) {
136 | ExpandableListPosition listPos = expandableList.getUnflattenedPosition(flatPos);
137 | boolean isExpanded = expandableList.expandedGroupIndexes[listPos.groupPos];
138 | // No-op on repeating calls
139 | if (isExpanded) {
140 | collapseGroup(listPos);
141 | }
142 | }
143 | }
--------------------------------------------------------------------------------
/expandablerecyclerview/src/main/java/com/thoughtbot/expandablerecyclerview/ExpandableRecyclerViewAdapter.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.support.v7.widget.RecyclerView.ViewHolder;
7 | import android.view.ViewGroup;
8 | import com.thoughtbot.expandablerecyclerview.listeners.ExpandCollapseListener;
9 | import com.thoughtbot.expandablerecyclerview.listeners.GroupExpandCollapseListener;
10 | import com.thoughtbot.expandablerecyclerview.listeners.OnGroupClickListener;
11 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
12 | import com.thoughtbot.expandablerecyclerview.models.ExpandableList;
13 | import com.thoughtbot.expandablerecyclerview.models.ExpandableListPosition;
14 | import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder;
15 | import com.thoughtbot.expandablerecyclerview.viewholders.GroupViewHolder;
16 | import java.util.List;
17 |
18 | public abstract class ExpandableRecyclerViewAdapter
19 | extends RecyclerView.Adapter implements ExpandCollapseListener, OnGroupClickListener {
20 |
21 | private static final String EXPAND_STATE_MAP = "expandable_recyclerview_adapter_expand_state_map";
22 |
23 | protected ExpandableList expandableList;
24 | private ExpandCollapseController expandCollapseController;
25 |
26 | private OnGroupClickListener groupClickListener;
27 | private GroupExpandCollapseListener expandCollapseListener;
28 |
29 | public ExpandableRecyclerViewAdapter(List extends ExpandableGroup> groups) {
30 | this.expandableList = new ExpandableList(groups);
31 | this.expandCollapseController = new ExpandCollapseController(expandableList, this);
32 | }
33 |
34 | /**
35 | * Implementation of Adapter.onCreateViewHolder(ViewGroup, int)
36 | * that determines if the list item is a group or a child and calls through
37 | * to the appropriate implementation of either {@link #onCreateGroupViewHolder(ViewGroup, int)}
38 | * or {@link #onCreateChildViewHolder(ViewGroup, int)}}.
39 | *
40 | * @param parent The {@link ViewGroup} into which the new {@link android.view.View}
41 | * will be added after it is bound to an adapter position.
42 | * @param viewType The view type of the new {@code android.view.View}.
43 | * @return Either a new {@link GroupViewHolder} or a new {@link ChildViewHolder}
44 | * that holds a {@code android.view.View} of the given view type.
45 | */
46 | @Override
47 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
48 | switch (viewType) {
49 | case ExpandableListPosition.GROUP:
50 | GVH gvh = onCreateGroupViewHolder(parent, viewType);
51 | gvh.setOnGroupClickListener(this);
52 | return gvh;
53 | case ExpandableListPosition.CHILD:
54 | CVH cvh = onCreateChildViewHolder(parent, viewType);
55 | return cvh;
56 | default:
57 | throw new IllegalArgumentException("viewType is not valid");
58 | }
59 | }
60 |
61 | /**
62 | * Implementation of Adapter.onBindViewHolder(RecyclerView.ViewHolder, int)
63 | * that determines if the list item is a group or a child and calls through
64 | * to the appropriate implementation of either {@link #onBindGroupViewHolder(GroupViewHolder,
65 | * int,
66 | * ExpandableGroup)}
67 | * or {@link #onBindChildViewHolder(ChildViewHolder, int, ExpandableGroup, int)}.
68 | *
69 | * @param holder Either the GroupViewHolder or the ChildViewHolder to bind data to
70 | * @param position The flat position (or index in the list of {@link
71 | * ExpandableList#getVisibleItemCount()} in the list at which to bind
72 | */
73 | @Override
74 | public void onBindViewHolder(ViewHolder holder, int position) {
75 | ExpandableListPosition listPos = expandableList.getUnflattenedPosition(position);
76 | ExpandableGroup group = expandableList.getExpandableGroup(listPos);
77 | switch (listPos.type) {
78 | case ExpandableListPosition.GROUP:
79 | onBindGroupViewHolder((GVH) holder, position, group);
80 |
81 | if (isGroupExpanded(group)) {
82 | ((GVH) holder).expand();
83 | } else {
84 | ((GVH) holder).collapse();
85 | }
86 | break;
87 | case ExpandableListPosition.CHILD:
88 | onBindChildViewHolder((CVH) holder, position, group, listPos.childPos);
89 | break;
90 | }
91 | }
92 |
93 | /**
94 | * @return the number of group and child objects currently expanded
95 | * @see ExpandableList#getVisibleItemCount()
96 | */
97 | @Override
98 | public int getItemCount() {
99 | return expandableList.getVisibleItemCount();
100 | }
101 |
102 | /**
103 | * Gets the view type of the item at the given position.
104 | *
105 | * @param position The flat position in the list to get the view type of
106 | * @return {@value ExpandableListPosition#CHILD} or {@value ExpandableListPosition#GROUP}
107 | * @throws RuntimeException if the item at the given position in the list is not found
108 | */
109 | @Override
110 | public int getItemViewType(int position) {
111 | return expandableList.getUnflattenedPosition(position).type;
112 | }
113 |
114 | /**
115 | * Called when a group is expanded
116 | *
117 | * @param positionStart the flat position of the first child in the {@link ExpandableGroup}
118 | * @param itemCount the total number of children in the {@link ExpandableGroup}
119 | */
120 | @Override
121 | public void onGroupExpanded(int positionStart, int itemCount) {
122 | //update header
123 | int headerPosition = positionStart - 1;
124 | notifyItemChanged(headerPosition);
125 |
126 | // only insert if there items to insert
127 | if (itemCount > 0) {
128 | notifyItemRangeInserted(positionStart, itemCount);
129 | if (expandCollapseListener != null) {
130 | int groupIndex = expandableList.getUnflattenedPosition(positionStart).groupPos;
131 | expandCollapseListener.onGroupExpanded(getGroups().get(groupIndex));
132 | }
133 | }
134 | }
135 |
136 | /**
137 | * Called when a group is collapsed
138 | *
139 | * @param positionStart the flat position of the first child in the {@link ExpandableGroup}
140 | * @param itemCount the total number of children in the {@link ExpandableGroup}
141 | */
142 | @Override
143 | public void onGroupCollapsed(int positionStart, int itemCount) {
144 | //update header
145 | int headerPosition = positionStart - 1;
146 | notifyItemChanged(headerPosition);
147 |
148 | // only remote if there items to remove
149 | if (itemCount > 0) {
150 | notifyItemRangeRemoved(positionStart, itemCount);
151 | if (expandCollapseListener != null) {
152 | //minus one to return the position of the header, not first child
153 | int groupIndex = expandableList.getUnflattenedPosition(positionStart - 1).groupPos;
154 | expandCollapseListener.onGroupCollapsed(getGroups().get(groupIndex));
155 | }
156 | }
157 | }
158 |
159 | /**
160 | * Triggered by a click on a {@link GroupViewHolder}
161 | *
162 | * @param flatPos the flat position of the {@link GroupViewHolder} that was clicked
163 | * @return false if click expanded group, true if click collapsed group
164 | */
165 | @Override
166 | public boolean onGroupClick(int flatPos) {
167 | if (groupClickListener != null) {
168 | groupClickListener.onGroupClick(flatPos);
169 | }
170 | return expandCollapseController.toggleGroup(flatPos);
171 | }
172 |
173 | /**
174 | * @param flatPos The flat list position of the group
175 | * @return true if the group is expanded, *after* the toggle, false if the group is now collapsed
176 | */
177 | public boolean toggleGroup(int flatPos) {
178 | return expandCollapseController.toggleGroup(flatPos);
179 | }
180 |
181 | /**
182 | * @param group the {@link ExpandableGroup} being toggled
183 | * @return true if the group is expanded, *after* the toggle, false if the group is now collapsed
184 | */
185 | public boolean toggleGroup(ExpandableGroup group) {
186 | return expandCollapseController.toggleGroup(group);
187 | }
188 |
189 | /**
190 | * Explicitly expand a group. Expanding already expandend groups does nothing.
191 | *
192 | * @param group the {@link ExpandableGroup} being expanded
193 | */
194 | public void expandGroup(ExpandableGroup group) {
195 | expandCollapseController.expandGroup(group);
196 | }
197 |
198 | /**
199 | * Explicitly collapse a group. Collapsing already collapsed groups does nothing.
200 | *
201 | * @param group the {@link ExpandableGroup} being expanded
202 | */
203 | public void collapseGroup(ExpandableGroup group) {
204 | expandCollapseController.collapseGroup(group);
205 | }
206 |
207 | /**
208 | * @param flatPos the flattened position of an item in the list
209 | * @return true if {@code group} is expanded, false if it is collapsed
210 | */
211 | public boolean isGroupExpanded(int flatPos) {
212 | return expandCollapseController.isGroupExpanded(flatPos);
213 | }
214 |
215 | /**
216 | * @param group the {@link ExpandableGroup} being checked for its collapsed state
217 | * @return true if {@code group} is expanded, false if it is collapsed
218 | */
219 | public boolean isGroupExpanded(ExpandableGroup group) {
220 | return expandCollapseController.isGroupExpanded(group);
221 | }
222 |
223 | /**
224 | * Stores the expanded state map across state loss.
225 | *
226 | * Should be called from whatever {@link Activity} that hosts the RecyclerView that {@link
227 | * ExpandableRecyclerViewAdapter} is attached to.
228 | *
229 | * This will make sure to add the expanded state map as an extra to the
230 | * instance state bundle to be used in {@link #onRestoreInstanceState(Bundle)}.
231 | *
232 | * @param savedInstanceState The {@code Bundle} into which to store the
233 | * expanded state map
234 | */
235 | public void onSaveInstanceState(Bundle savedInstanceState) {
236 | savedInstanceState.putBooleanArray(EXPAND_STATE_MAP, expandableList.expandedGroupIndexes);
237 | }
238 |
239 | /**
240 | * Fetches the expandable state map from the saved instance state {@link Bundle}
241 | * and restores the expanded states of all of the list items.
242 | *
243 | * Should be called from {@link Activity#onRestoreInstanceState(Bundle)} in
244 | * the {@link Activity} that hosts the RecyclerView that this
245 | * {@link ExpandableRecyclerViewAdapter} is attached to.
246 | *
247 | *
248 | * @param savedInstanceState The {@code Bundle} from which the expanded
249 | * state map is loaded
250 | */
251 | public void onRestoreInstanceState(Bundle savedInstanceState) {
252 | if (savedInstanceState == null || !savedInstanceState.containsKey(EXPAND_STATE_MAP)) {
253 | return;
254 | }
255 | expandableList.expandedGroupIndexes = savedInstanceState.getBooleanArray(EXPAND_STATE_MAP);
256 | notifyDataSetChanged();
257 | }
258 |
259 | public void setOnGroupClickListener(OnGroupClickListener listener) {
260 | groupClickListener = listener;
261 | }
262 |
263 | public void setOnGroupExpandCollapseListener(GroupExpandCollapseListener listener) {
264 | expandCollapseListener = listener;
265 | }
266 |
267 | /**
268 | * The full list of {@link ExpandableGroup} backing this RecyclerView
269 | *
270 | * @return the list of {@link ExpandableGroup} that this object was instantiated with
271 | */
272 | public List extends ExpandableGroup> getGroups() {
273 | return expandableList.groups;
274 | }
275 |
276 | /**
277 | * Called from {@link #onCreateViewHolder(ViewGroup, int)} when the list item created is a group
278 | *
279 | * @param viewType an int returned by {@link ExpandableRecyclerViewAdapter#getItemViewType(int)}
280 | * @param parent the {@link ViewGroup} in the list for which a {@link GVH} is being created
281 | * @return A {@link GVH} corresponding to the group list item with the {@code ViewGroup} parent
282 | */
283 | public abstract GVH onCreateGroupViewHolder(ViewGroup parent, int viewType);
284 |
285 | /**
286 | * Called from {@link #onCreateViewHolder(ViewGroup, int)} when the list item created is a child
287 | *
288 | * @param viewType an int returned by {@link ExpandableRecyclerViewAdapter#getItemViewType(int)}
289 | * @param parent the {@link ViewGroup} in the list for which a {@link CVH} is being created
290 | * @return A {@link CVH} corresponding to child list item with the {@code ViewGroup} parent
291 | */
292 | public abstract CVH onCreateChildViewHolder(ViewGroup parent, int viewType);
293 |
294 | /**
295 | * Called from onBindViewHolder(RecyclerView.ViewHolder, int) when the list item
296 | * bound to is a child.
297 | *
298 | * Bind data to the {@link CVH} here.
299 | *
300 | * @param holder The {@code CVH} to bind data to
301 | * @param flatPosition the flat position (raw index) in the list at which to bind the child
302 | * @param group The {@link ExpandableGroup} that the the child list item belongs to
303 | * @param childIndex the index of this child within it's {@link ExpandableGroup}
304 | */
305 | public abstract void onBindChildViewHolder(CVH holder, int flatPosition, ExpandableGroup group,
306 | int childIndex);
307 |
308 | /**
309 | * Called from onBindViewHolder(RecyclerView.ViewHolder, int) when the list item bound to is a
310 | * group
311 | *
312 | * Bind data to the {@link GVH} here.
313 | *
314 | * @param holder The {@code GVH} to bind data to
315 | * @param flatPosition the flat position (raw index) in the list at which to bind the group
316 | * @param group The {@link ExpandableGroup} to be used to bind data to this {@link GVH}
317 | */
318 | public abstract void onBindGroupViewHolder(GVH holder, int flatPosition, ExpandableGroup group);
319 | }
320 |
--------------------------------------------------------------------------------
/expandablerecyclerview/src/main/java/com/thoughtbot/expandablerecyclerview/MultiTypeExpandableRecyclerViewAdapter.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview;
2 |
3 | import android.support.v7.widget.RecyclerView.ViewHolder;
4 | import android.view.ViewGroup;
5 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
6 | import com.thoughtbot.expandablerecyclerview.models.ExpandableList;
7 | import com.thoughtbot.expandablerecyclerview.models.ExpandableListPosition;
8 | import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder;
9 | import com.thoughtbot.expandablerecyclerview.viewholders.GroupViewHolder;
10 | import java.util.List;
11 |
12 | public abstract class MultiTypeExpandableRecyclerViewAdapter
13 | extends ExpandableRecyclerViewAdapter {
14 |
15 | public MultiTypeExpandableRecyclerViewAdapter(List extends ExpandableGroup> groups) {
16 | super(groups);
17 | }
18 |
19 | /**
20 | * Implementation of RecyclerView.Adapter.onCreateViewHolder(ViewGroup, int)
21 | * that determines if the list item is a group or a child and calls through
22 | * to the appropriate implementation of either {@link #onCreateGroupViewHolder(ViewGroup, int)}
23 | * or {@link #onCreateChildViewHolder(ViewGroup, int)}}.
24 | *
25 | * @param parent The {@link ViewGroup} into which the new {@link android.view.View}
26 | * will be added after it is bound to an adapter position.
27 | * @param viewType The view type of the new {@code android.view.View}.
28 | * @return Either a new {@link GroupViewHolder} or a new {@link ChildViewHolder}
29 | * that holds a {@code android.view.View} of the given view type.
30 | */
31 | @Override
32 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
33 | if (isGroup(viewType)) {
34 | GVH gvh = onCreateGroupViewHolder(parent, viewType);
35 | gvh.setOnGroupClickListener(this);
36 | return gvh;
37 | } else if (isChild(viewType)) {
38 | CVH cvh = onCreateChildViewHolder(parent, viewType);
39 | return cvh;
40 | }
41 | throw new IllegalArgumentException("viewType is not valid");
42 | }
43 |
44 | /**
45 | * Implementation of Adapter.onBindViewHolder(RecyclerView.ViewHolder, int)
46 | * that determines if the list item is a group or a child and calls through
47 | * to the appropriate implementation of either {@link #onBindGroupViewHolder(GroupViewHolder,
48 | * int,
49 | * ExpandableGroup)}
50 | * or {@link #onBindChildViewHolder(ChildViewHolder, int, ExpandableGroup, int)}.
51 | *
52 | * @param holder Either the GroupViewHolder or the ChildViewHolder to bind data to
53 | * @param position The flat position (or index in the list of {@link
54 | * ExpandableList#getVisibleItemCount()} in the list at which to bind
55 | */
56 | @Override
57 | public void onBindViewHolder(ViewHolder holder, int position) {
58 | ExpandableListPosition listPos = expandableList.getUnflattenedPosition(position);
59 | ExpandableGroup group = expandableList.getExpandableGroup(listPos);
60 | if (isGroup(getItemViewType(position))) {
61 | onBindGroupViewHolder((GVH) holder, position, group);
62 |
63 | if (isGroupExpanded(group)) {
64 | ((GVH) holder).expand();
65 | } else {
66 | ((GVH) holder).collapse();
67 | }
68 | } else if (isChild(getItemViewType(position))) {
69 | onBindChildViewHolder((CVH) holder, position, group, listPos.childPos);
70 | }
71 | }
72 |
73 | /**
74 | * Gets the view type of the item at the given position.
75 | *
76 | * @param position The flat position in the list to get the view type of
77 | * @return if the flat position corresponds to a child item, this will return the value returned
78 | * by {@code getChildViewType}. if the flat position refers to a group item this will return the
79 | * value returned by {@code getGroupViewType}
80 | */
81 | @Override
82 | public int getItemViewType(int position) {
83 | ExpandableListPosition listPosition = expandableList.getUnflattenedPosition(position);
84 | ExpandableGroup group = expandableList.getExpandableGroup(listPosition);
85 |
86 | int viewType = listPosition.type;
87 | switch (viewType) {
88 | case ExpandableListPosition.GROUP:
89 | return getGroupViewType(position, group);
90 | case ExpandableListPosition.CHILD:
91 | return getChildViewType(position, group, listPosition.childPos);
92 | default:
93 | return viewType;
94 | }
95 | }
96 |
97 | /**
98 | * Used to allow subclasses to have multiple view types for children
99 | *
100 | * @param position the flat position in the list
101 | * @param group the group that this child belongs to
102 | * @param childIndex the index of the child within the group
103 | * @return any int representing the viewType for a child within the {@code group} *EXCEPT*
104 | * for {@link ExpandableListPosition#CHILD} and {@link ExpandableListPosition#GROUP}.
105 | *
106 | * If you do *not* override this method, the default viewType for a group is {@link
107 | * ExpandableListPosition#CHILD}
108 | *
109 | *
110 | * A subclass may use any number *EXCEPT* for {@link ExpandableListPosition#CHILD} and {@link
111 | * ExpandableListPosition#GROUP} as those are already being used by the adapter
112 | *
113 | */
114 | public int getChildViewType(int position, ExpandableGroup group, int childIndex) {
115 | return super.getItemViewType(position);
116 | }
117 |
118 | /**
119 | * Used to allow subclasses to have multiple view types for groups
120 | *
121 | * @param position the flat position in the list
122 | * @param group the group at this position
123 | * @return any int representing the viewType for this {@code group} *EXCEPT*
124 | * for {@link ExpandableListPosition#CHILD} and {@link ExpandableListPosition#GROUP}.
125 | *
126 | * If you do not override this method, the default viewType for a group is {@link
127 | * ExpandableListPosition#GROUP}
128 | *
129 | *
130 | * A subclass may use any number *EXCEPT* for {@link ExpandableListPosition#CHILD} and {@link
131 | * ExpandableListPosition#GROUP} as those are already being used by the adapter
132 | *
133 | */
134 | public int getGroupViewType(int position, ExpandableGroup group) {
135 | return super.getItemViewType(position);
136 | }
137 |
138 | /**
139 | * @param viewType the int corresponding to the viewType of a {@code ExpandableGroup}
140 | * @return if a subclasses has *NOT* overridden {@code getGroupViewType} than the viewType for
141 | * the group is defaulted to {@link ExpandableListPosition#GROUP}
142 | */
143 | public boolean isGroup(int viewType) {
144 | return viewType == ExpandableListPosition.GROUP;
145 | }
146 |
147 | /**
148 | * @param viewType the int corresponding to the viewType of a child of a {@code ExpandableGroup}
149 | * @return if a subclasses has *NOT* overridden {@code getChildViewType} than the viewType for
150 | * the child is defaulted to {@link ExpandableListPosition#CHILD}
151 | */
152 | public boolean isChild(int viewType) {
153 | return viewType == ExpandableListPosition.CHILD;
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/expandablerecyclerview/src/main/java/com/thoughtbot/expandablerecyclerview/listeners/ExpandCollapseListener.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.listeners;
2 |
3 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
4 |
5 | public interface ExpandCollapseListener {
6 |
7 | /**
8 | * Called when a group is expanded
9 | *
10 | * @param positionStart the flat position of the first child in the {@link ExpandableGroup}
11 | * @param itemCount the total number of children in the {@link ExpandableGroup}
12 | */
13 | void onGroupExpanded(int positionStart, int itemCount);
14 |
15 | /**
16 | * Called when a group is collapsed
17 | *
18 | * @param positionStart the flat position of the first child in the {@link ExpandableGroup}
19 | * @param itemCount the total number of children in the {@link ExpandableGroup}
20 | */
21 | void onGroupCollapsed(int positionStart, int itemCount);
22 | }
23 |
--------------------------------------------------------------------------------
/expandablerecyclerview/src/main/java/com/thoughtbot/expandablerecyclerview/listeners/GroupExpandCollapseListener.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.listeners;
2 |
3 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
4 |
5 | public interface GroupExpandCollapseListener {
6 |
7 | /**
8 | * Called when a group is expanded
9 | * @param group the {@link ExpandableGroup} being expanded
10 | */
11 | void onGroupExpanded(ExpandableGroup group);
12 |
13 | /**
14 | * Called when a group is collapsed
15 | * @param group the {@link ExpandableGroup} being collapsed
16 | */
17 | void onGroupCollapsed(ExpandableGroup group);
18 | }
19 |
--------------------------------------------------------------------------------
/expandablerecyclerview/src/main/java/com/thoughtbot/expandablerecyclerview/listeners/OnGroupClickListener.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.listeners;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import com.thoughtbot.expandablerecyclerview.viewholders.GroupViewHolder;
5 |
6 | public interface OnGroupClickListener {
7 |
8 | /**
9 | * @param flatPos the flat position (raw index within the list of visible items in the
10 | * RecyclerView of a GroupViewHolder)
11 | * @return false if click expanded group, true if click collapsed group
12 | */
13 | boolean onGroupClick(int flatPos);
14 | }
--------------------------------------------------------------------------------
/expandablerecyclerview/src/main/java/com/thoughtbot/expandablerecyclerview/models/ExpandableGroup.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.models;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * The backing data object for an {@link ExpandableGroup}
10 | */
11 | public class ExpandableGroup implements Parcelable {
12 | private String title;
13 | private List items;
14 |
15 | public ExpandableGroup(String title, List items) {
16 | this.title = title;
17 | this.items = items;
18 | }
19 |
20 | public String getTitle() {
21 | return title;
22 | }
23 |
24 | public List getItems() {
25 | return items;
26 | }
27 |
28 | public int getItemCount() {
29 | return items == null ? 0 : items.size();
30 | }
31 |
32 | @Override
33 | public String toString() {
34 | return "ExpandableGroup{" +
35 | "title='" + title + '\'' +
36 | ", items=" + items +
37 | '}';
38 | }
39 |
40 | protected ExpandableGroup(Parcel in) {
41 | title = in.readString();
42 | byte hasItems = in.readByte();
43 | int size = in.readInt();
44 | if (hasItems == 0x01) {
45 | items = new ArrayList(size);
46 | Class> type = (Class>) in.readSerializable();
47 | in.readList(items, type.getClassLoader());
48 | } else {
49 | items = null;
50 | }
51 | }
52 |
53 | @Override
54 | public int describeContents() {
55 | return 0;
56 | }
57 |
58 | @Override
59 | public void writeToParcel(Parcel dest, int flags) {
60 | dest.writeString(title);
61 | if (items == null) {
62 | dest.writeByte((byte) (0x00));
63 | dest.writeInt(0);
64 | } else {
65 | dest.writeByte((byte) (0x01));
66 | dest.writeInt(items.size());
67 | if (items.size() > 0) {
68 | final Class> objectsType = items.get(0).getClass();
69 | dest.writeSerializable(objectsType);
70 | }
71 | dest.writeList(items);
72 | }
73 | }
74 |
75 | @SuppressWarnings("unused")
76 | public static final Creator CREATOR =
77 | new Creator() {
78 | @Override
79 | public ExpandableGroup createFromParcel(Parcel in) {
80 | return new ExpandableGroup(in);
81 | }
82 |
83 | @Override
84 | public ExpandableGroup[] newArray(int size) {
85 | return new ExpandableGroup[size];
86 | }
87 | };
88 | }
89 |
--------------------------------------------------------------------------------
/expandablerecyclerview/src/main/java/com/thoughtbot/expandablerecyclerview/models/ExpandableList.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.models;
2 |
3 | import java.util.List;
4 |
5 | /*
6 | * Terminology:
7 | *
flat position - Flat list position, the position of an item relative to all the
8 | * other *visible* items on the screen. For example, if you have a three groups, each with
9 | * 2 children and all are collapsed, the "flat position" of the last group would be 2. And if
10 | * the first of those three groups was expanded, the flat position of the last group would now be 4.
11 | *
12 | *
13 | * This class acts as a translator between the flat list position - i.e. what groups
14 | * and children you see on the screen - to and from the full backing list of groups & their children
15 | */
16 | public class ExpandableList {
17 |
18 | public List extends ExpandableGroup> groups;
19 | public boolean[] expandedGroupIndexes;
20 |
21 | public ExpandableList(List extends ExpandableGroup> groups) {
22 | this.groups = groups;
23 |
24 | expandedGroupIndexes = new boolean[groups.size()];
25 | for (int i = 0; i < groups.size(); i++) {
26 | expandedGroupIndexes[i] = false;
27 | }
28 | }
29 |
30 | /**
31 | * @param group the index of the {@link ExpandableGroup} in the full collection {@link #groups}
32 | * @return the number of visible row items for the particular group. If the group is collapsed,
33 | * return 1 for the group header. If the group is expanded return total number of children in the
34 | * group + 1 for the group header
35 | */
36 | private int numberOfVisibleItemsInGroup(int group) {
37 | if (expandedGroupIndexes[group]) {
38 | return groups.get(group).getItemCount() + 1;
39 | } else {
40 | return 1;
41 | }
42 | }
43 |
44 | /**
45 | * @return the total number visible rows
46 | */
47 | public int getVisibleItemCount() {
48 | int count = 0;
49 | for (int i = 0; i < groups.size(); i++) {
50 | count += numberOfVisibleItemsInGroup(i);
51 | }
52 | return count;
53 | }
54 |
55 | /**
56 | * Translates a flat list position (the raw position of an item (child or group) in the list) to
57 | * either a) group pos if the specified flat list position corresponds to a group, or b) child
58 | * pos if it corresponds to a child. Performs a binary search on the expanded
59 | * groups list to find the flat list pos if it is an exp group, otherwise
60 | * finds where the flat list pos fits in between the exp groups.
61 | *
62 | * @param flPos the flat list position to be translated
63 | * @return the group position or child position of the specified flat list
64 | * position encompassed in a {@link ExpandableListPosition} object
65 | * that contains additional useful info for insertion, etc.
66 | */
67 | public ExpandableListPosition getUnflattenedPosition(int flPos) {
68 | int groupItemCount;
69 | int adapted = flPos;
70 | for (int i = 0; i < groups.size(); i++) {
71 | groupItemCount = numberOfVisibleItemsInGroup(i);
72 | if (adapted == 0) {
73 | return ExpandableListPosition.obtain(ExpandableListPosition.GROUP, i, -1, flPos);
74 | } else if (adapted < groupItemCount) {
75 | return ExpandableListPosition.obtain(ExpandableListPosition.CHILD, i, adapted - 1, flPos);
76 | }
77 | adapted -= groupItemCount;
78 | }
79 | throw new RuntimeException("Unknown state");
80 | }
81 |
82 | /**
83 | * @param listPosition representing either a child or a group
84 | * @return the index of a group within the {@link #getVisibleItemCount()}
85 | */
86 | public int getFlattenedGroupIndex(ExpandableListPosition listPosition) {
87 | int groupIndex = listPosition.groupPos;
88 | int runningTotal = 0;
89 |
90 | for (int i = 0; i < groupIndex; i++) {
91 | runningTotal += numberOfVisibleItemsInGroup(i);
92 | }
93 | return runningTotal;
94 | }
95 |
96 | /**
97 | * @param groupIndex representing the index of a group within {@link #groups}
98 | * @return the index of a group within the {@link #getVisibleItemCount()}
99 | */
100 | public int getFlattenedGroupIndex(int groupIndex) {
101 | int runningTotal = 0;
102 |
103 | for (int i = 0; i < groupIndex; i++) {
104 | runningTotal += numberOfVisibleItemsInGroup(i);
105 | }
106 | return runningTotal;
107 | }
108 |
109 | /**
110 | * @param group an {@link ExpandableGroup} within {@link #groups}
111 | * @return the index of a group within the {@link #getVisibleItemCount()} or 0 if the
112 | * groups.indexOf cannot find the group
113 | */
114 | public int getFlattenedGroupIndex(ExpandableGroup group) {
115 | int groupIndex = groups.indexOf(group);
116 | int runningTotal = 0;
117 |
118 | for (int i = 0; i < groupIndex; i++) {
119 | runningTotal += numberOfVisibleItemsInGroup(i);
120 | }
121 | return runningTotal;
122 | }
123 |
124 | /**
125 | * Converts a child position to a flat list position.
126 | *
127 | * @param packedPosition The child positions to be converted in it's
128 | * packed position representation.
129 | * @return The flat list position for the given child
130 | */
131 | public int getFlattenedChildIndex(long packedPosition) {
132 | ExpandableListPosition listPosition = ExpandableListPosition.obtainPosition(packedPosition);
133 | return getFlattenedChildIndex(listPosition);
134 | }
135 |
136 | /**
137 | * Converts a child position to a flat list position.
138 | *
139 | * @param listPosition The child positions to be converted in it's
140 | * {@link ExpandableListPosition} representation.
141 | * @return The flat list position for the given child
142 | */
143 | public int getFlattenedChildIndex(ExpandableListPosition listPosition) {
144 | int groupIndex = listPosition.groupPos;
145 | int childIndex = listPosition.childPos;
146 | int runningTotal = 0;
147 |
148 | for (int i = 0; i < groupIndex; i++) {
149 | runningTotal += numberOfVisibleItemsInGroup(i);
150 | }
151 | return runningTotal + childIndex + 1;
152 | }
153 |
154 | /**
155 | * Converts the details of a child's position to a flat list position.
156 | *
157 | * @param groupIndex The index of a group within {@link #groups}
158 | * @param childIndex the index of a child within it's {@link ExpandableGroup}
159 | * @return The flat list position for the given child
160 | */
161 | public int getFlattenedChildIndex(int groupIndex, int childIndex) {
162 | int runningTotal = 0;
163 |
164 | for (int i = 0; i < groupIndex; i++) {
165 | runningTotal += numberOfVisibleItemsInGroup(i);
166 | }
167 | return runningTotal + childIndex + 1;
168 | }
169 |
170 | /**
171 | * @param groupIndex The index of a group within {@link #groups}
172 | * @return The flat list position for the first child in a group
173 | */
174 | public int getFlattenedFirstChildIndex(int groupIndex) {
175 | return getFlattenedGroupIndex(groupIndex) + 1;
176 | }
177 |
178 | /**
179 | * @param listPosition The child positions to be converted in it's
180 | * {@link ExpandableListPosition} representation.
181 | * @return The flat list position for the first child in a group
182 | */
183 | public int getFlattenedFirstChildIndex(ExpandableListPosition listPosition) {
184 | return getFlattenedGroupIndex(listPosition) + 1;
185 | }
186 |
187 | /**
188 | * @param listPosition An {@link ExpandableListPosition} representing either a child or group
189 | * @return the total number of children within the group associated with the @param listPosition
190 | */
191 | public int getExpandableGroupItemCount(ExpandableListPosition listPosition) {
192 | return groups.get(listPosition.groupPos).getItemCount();
193 | }
194 |
195 | /**
196 | * Translates either a group pos or a child pos to an {@link ExpandableGroup}.
197 | * If the {@link ExpandableListPosition} is a child position, it returns the {@link
198 | * ExpandableGroup} it belongs to
199 | *
200 | * @param listPosition a {@link ExpandableListPosition} representing either a group position
201 | * or child position
202 | * @return the {@link ExpandableGroup} object that contains the listPosition
203 | */
204 | public ExpandableGroup getExpandableGroup(ExpandableListPosition listPosition) {
205 | return groups.get(listPosition.groupPos);
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/expandablerecyclerview/src/main/java/com/thoughtbot/expandablerecyclerview/models/ExpandableListPosition.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.models;
2 |
3 | import android.widget.ExpandableListView;
4 | import java.util.ArrayList;
5 |
6 | /**
7 | * Exact copy of android.widget.ExpandableListPosition because
8 | * android.widget.ExpandableListPosition has package local scope
9 | *
10 | *
11 | * ExpandableListPosition can refer to either a group's position or a child's
12 | * position. Referring to a child's position requires both a group position (the
13 | * group containing the child) and a child position (the child's position within
14 | * that group). To create objects, use {@link #obtainChildPosition(int, int)} or
15 | * {@link #obtainGroupPosition(int)}.
16 | */
17 |
18 | public class ExpandableListPosition {
19 |
20 | private static final int MAX_POOL_SIZE = 5;
21 | private static ArrayList sPool =
22 | new ArrayList(MAX_POOL_SIZE);
23 |
24 | /**
25 | * This data type represents a child position
26 | */
27 | public final static int CHILD = 1;
28 |
29 | /**
30 | * This data type represents a group position
31 | */
32 | public final static int GROUP = 2;
33 |
34 | /**
35 | * The position of either the group being referred to, or the parent
36 | * group of the child being referred to
37 | */
38 | public int groupPos;
39 |
40 | /**
41 | * The position of the child within its parent group
42 | */
43 | public int childPos;
44 |
45 | /**
46 | * The position of the item in the flat list (optional, used internally when
47 | * the corresponding flat list position for the group or child is known)
48 | */
49 | int flatListPos;
50 |
51 | /**
52 | * What type of position this ExpandableListPosition represents
53 | */
54 | public int type;
55 |
56 | private void resetState() {
57 | groupPos = 0;
58 | childPos = 0;
59 | flatListPos = 0;
60 | type = 0;
61 | }
62 |
63 | private ExpandableListPosition() {
64 | }
65 |
66 | public long getPackedPosition() {
67 | if (type == CHILD) {
68 | return ExpandableListView.getPackedPositionForChild(groupPos, childPos);
69 | } else {
70 | return ExpandableListView.getPackedPositionForGroup(groupPos);
71 | }
72 | }
73 |
74 | static ExpandableListPosition obtainGroupPosition(int groupPosition) {
75 | return obtain(GROUP, groupPosition, 0, 0);
76 | }
77 |
78 | static ExpandableListPosition obtainChildPosition(int groupPosition, int childPosition) {
79 | return obtain(CHILD, groupPosition, childPosition, 0);
80 | }
81 |
82 | static ExpandableListPosition obtainPosition(long packedPosition) {
83 | if (packedPosition == ExpandableListView.PACKED_POSITION_VALUE_NULL) {
84 | return null;
85 | }
86 |
87 | ExpandableListPosition elp = getRecycledOrCreate();
88 | elp.groupPos = ExpandableListView.getPackedPositionGroup(packedPosition);
89 | if (ExpandableListView.getPackedPositionType(packedPosition) ==
90 | ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
91 | elp.type = CHILD;
92 | elp.childPos = ExpandableListView.getPackedPositionChild(packedPosition);
93 | } else {
94 | elp.type = GROUP;
95 | }
96 | return elp;
97 | }
98 |
99 | public static ExpandableListPosition obtain(int type, int groupPos, int childPos,
100 | int flatListPos) {
101 | ExpandableListPosition elp = getRecycledOrCreate();
102 | elp.type = type;
103 | elp.groupPos = groupPos;
104 | elp.childPos = childPos;
105 | elp.flatListPos = flatListPos;
106 | return elp;
107 | }
108 |
109 | private static ExpandableListPosition getRecycledOrCreate() {
110 | ExpandableListPosition elp;
111 | synchronized (sPool) {
112 | if (sPool.size() > 0) {
113 | elp = sPool.remove(0);
114 | } else {
115 | return new ExpandableListPosition();
116 | }
117 | }
118 | elp.resetState();
119 | return elp;
120 | }
121 |
122 | /**
123 | * Do not call this unless you obtained this via ExpandableListPosition.obtain().
124 | * PositionMetadata will handle recycling its own children.
125 | */
126 | public void recycle() {
127 | synchronized (sPool) {
128 | if (sPool.size() < MAX_POOL_SIZE) {
129 | sPool.add(this);
130 | }
131 | }
132 | }
133 |
134 | @Override
135 | public boolean equals(Object o) {
136 | if (this == o) return true;
137 | if (o == null || getClass() != o.getClass()) return false;
138 |
139 | ExpandableListPosition that = (ExpandableListPosition) o;
140 |
141 | if (groupPos != that.groupPos) return false;
142 | if (childPos != that.childPos) return false;
143 | if (flatListPos != that.flatListPos) return false;
144 | return type == that.type;
145 |
146 | }
147 |
148 | @Override
149 | public int hashCode() {
150 | int result = groupPos;
151 | result = 31 * result + childPos;
152 | result = 31 * result + flatListPos;
153 | result = 31 * result + type;
154 | return result;
155 | }
156 |
157 | @Override
158 | public String toString() {
159 | return "ExpandableListPosition{" +
160 | "groupPos=" + groupPos +
161 | ", childPos=" + childPos +
162 | ", flatListPos=" + flatListPos +
163 | ", type=" + type +
164 | '}';
165 | }
166 | }
167 |
168 |
--------------------------------------------------------------------------------
/expandablerecyclerview/src/main/java/com/thoughtbot/expandablerecyclerview/viewholders/ChildViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.viewholders;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.View;
5 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
6 |
7 | /**
8 | * ViewHolder for {@link ExpandableGroup#items}
9 | */
10 | public class ChildViewHolder extends RecyclerView.ViewHolder {
11 |
12 | public ChildViewHolder(View itemView) {
13 | super(itemView);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/expandablerecyclerview/src/main/java/com/thoughtbot/expandablerecyclerview/viewholders/GroupViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.viewholders;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.View;
5 | import android.view.View.OnClickListener;
6 | import com.thoughtbot.expandablerecyclerview.listeners.OnGroupClickListener;
7 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
8 |
9 | /**
10 | * ViewHolder for the {@link ExpandableGroup#title} in a {@link ExpandableGroup}
11 | *
12 | * The current implementation does now allow for sub {@link View} of the parent view to trigger
13 | * a collapse / expand. *Only* click events on the parent {@link View} will trigger a collapse or
14 | * expand
15 | */
16 | public abstract class GroupViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
17 |
18 | private OnGroupClickListener listener;
19 |
20 | public GroupViewHolder(View itemView) {
21 | super(itemView);
22 | itemView.setOnClickListener(this);
23 | }
24 |
25 | @Override
26 | public void onClick(View v) {
27 | if (listener != null) {
28 | listener.onGroupClick(getAdapterPosition());
29 | }
30 | }
31 |
32 | public void setOnGroupClickListener(OnGroupClickListener listener) {
33 | this.listener = listener;
34 | }
35 |
36 | public void expand() {}
37 |
38 | public void collapse() {}
39 | }
40 |
--------------------------------------------------------------------------------
/expandablerecyclerview/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ExpandableRecyclerView
3 |
4 |
--------------------------------------------------------------------------------
/expandablerecyclerview/src/test/java/com/thoughtbot/expandablerecyclerview/ExpandableListTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
6 | import com.thoughtbot.expandablerecyclerview.models.ExpandableList;
7 | import com.thoughtbot.expandablerecyclerview.models.ExpandableListPosition;
8 | import com.thoughtbot.expandablerecyclerview.testUtils.TestDataFactory;
9 | import java.util.List;
10 | import org.junit.Before;
11 | import org.junit.Test;
12 | import org.junit.runner.RunWith;
13 | import org.mockito.MockitoAnnotations;
14 | import org.robolectric.RobolectricTestRunner;
15 | import org.robolectric.RuntimeEnvironment;
16 | import org.robolectric.annotation.Config;
17 |
18 | import static com.thoughtbot.expandablerecyclerview.models.ExpandableListPosition.CHILD;
19 | import static com.thoughtbot.expandablerecyclerview.models.ExpandableListPosition.GROUP;
20 | import static com.thoughtbot.expandablerecyclerview.models.ExpandableListPosition.obtain;
21 | import static junit.framework.Assert.assertEquals;
22 | import static junit.framework.Assert.assertNotNull;
23 |
24 | @RunWith(RobolectricTestRunner.class)
25 | @Config(constants = BuildConfig.class, sdk = 21)
26 | public class ExpandableListTest {
27 |
28 | private Context context;
29 | private List groups;
30 |
31 | @Before
32 | public void setUp() throws Exception {
33 | MockitoAnnotations.initMocks(this);
34 | Application application = RuntimeEnvironment.application;
35 | assertNotNull(application);
36 |
37 | context = application;
38 | groups = TestDataFactory.makeGroups();
39 | }
40 |
41 | @Test
42 | public void test_getVisibleItemCount() {
43 | ExpandableList list = new ExpandableList(groups);
44 |
45 | //initial state
46 | int initialExpected = 6;
47 | int initialActual = list.getVisibleItemCount();
48 |
49 | assertEquals(initialExpected, initialActual);
50 |
51 | //expand first group
52 | list.expandedGroupIndexes[0] = true;
53 |
54 | //new state
55 | int newExpected = 9;
56 | int newActual = list.getVisibleItemCount();
57 |
58 | assertEquals(newExpected, newActual);
59 | }
60 |
61 | @Test
62 | public void test_getUnflattenedPosition() {
63 | ExpandableList list = new ExpandableList(groups);
64 | int flatPos = 3;
65 |
66 | //initial state
67 | //flatPos 3 == group at index 3
68 | ExpandableListPosition initialExpected = obtain(GROUP, 3, -1, 3);
69 | ExpandableListPosition initialActual = list.getUnflattenedPosition(flatPos);
70 |
71 | assertEquals(initialExpected, initialActual);
72 |
73 | //expand first group
74 | list.expandedGroupIndexes[0] = true;
75 |
76 | //flatPos 3 == child number 2 within group at index 0
77 | ExpandableListPosition newExpected = obtain(CHILD, 0, 2, 3);
78 | ExpandableListPosition newActual = list.getUnflattenedPosition(flatPos);
79 |
80 | assertEquals(newExpected, newActual);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/expandablerecyclerview/src/test/java/com/thoughtbot/expandablerecyclerview/testUtils/TestDataFactory.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.testUtils;
2 |
3 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
4 | import java.util.ArrayList;
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | public class TestDataFactory {
9 |
10 | public static List makeGroups() {
11 | ArrayList list = new ArrayList();
12 | for (int i = 0; i < 6; i++) {
13 | List items = Arrays.asList(i + ".0", i + ".1", i + ".2");
14 | list.add(new ExpandableGroup("Section " + i, items));
15 | }
16 | return list;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoughtbot/expandable-recycler-view/56e45355c069ac602301da021691ea09ea7d0287/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Sep 28 11:33:07 EDT 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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/sample/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.thoughtbot.expandablerecyclerview.sample"
9 | minSdkVersion 16
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | compile project(':expandablecheckrecyclerview')
26 |
27 | //android
28 | compile 'com.android.support:appcompat-v7:23.4.0'
29 | compile 'com.android.support:recyclerview-v7:23.4.0'
30 |
31 | //unit tests
32 | testCompile 'junit:junit:4.12'
33 | testCompile 'org.robolectric:robolectric:3.2.1'
34 | testCompile 'org.mockito:mockito-core:1.10.5'
35 |
36 | //automation tests
37 | androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
38 | androidTestCompile 'com.android.support.test:runner:0.5'
39 | androidTestCompile 'com.android.support:support-annotations:23.4.0'
40 | androidTestCompile 'com.android.support.test:rules:0.4.1'
41 | androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2') {
42 | // Necessary to avoid version conflicts
43 | exclude group: 'com.android.support', module: 'appcompat'
44 | exclude group: 'com.android.support', module: 'support-v4'
45 | exclude group: 'com.android.support', module: 'support-annotations'
46 | exclude module: 'recyclerview-v7'
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/sample/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/amanda/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 |
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/thoughtbot/expandablerecyclerview/sample/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/thoughtbot/expandablerecyclerview/sample/ExpandActivityTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample;
2 |
3 | import android.support.test.espresso.contrib.RecyclerViewActions;
4 | import android.support.test.rule.ActivityTestRule;
5 | import android.support.test.runner.AndroidJUnit4;
6 | import android.support.v7.widget.RecyclerView;
7 | import com.thoughtbot.expandablerecyclerview.sample.expand.GenreAdapter;
8 | import com.thoughtbot.expandablerecyclerview.sample.expand.ExpandActivity;
9 | import org.junit.Before;
10 | import org.junit.Rule;
11 | import org.junit.Test;
12 | import org.junit.runner.RunWith;
13 |
14 | import static android.support.test.espresso.Espresso.onView;
15 | import static android.support.test.espresso.action.ViewActions.click;
16 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
17 | import static junit.framework.Assert.assertTrue;
18 |
19 | @RunWith(AndroidJUnit4.class)
20 | public class ExpandActivityTest {
21 |
22 | @Rule
23 | public ActivityTestRule activityRule =
24 | new ActivityTestRule<>(ExpandActivity.class);
25 |
26 | private RecyclerView recyclerView;
27 | private GenreAdapter adapter;
28 |
29 | @Before
30 | public void setUp() {
31 | recyclerView =
32 | (RecyclerView) activityRule.getActivity().findViewById(R.id.recycler_view);
33 |
34 | adapter = activityRule.getActivity().adapter;
35 | }
36 |
37 | @Test
38 | public void testClickGroup() {
39 | onView(withId(R.id.recycler_view))
40 | .perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
41 |
42 | assertTrue(adapter.isGroupExpanded(0));
43 | }
44 |
45 | @Test
46 | public void testClickItem() {
47 | onView(withId(R.id.recycler_view))
48 | .perform(RecyclerViewActions.actionOnItemAtPosition(1, click()));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
27 |
28 |
31 |
32 |
35 |
36 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/Artist.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | public class Artist implements Parcelable {
7 |
8 | private String name;
9 | private boolean isFavorite;
10 |
11 | public Artist(String name, boolean isFavorite) {
12 | this.name = name;
13 | this.isFavorite = isFavorite;
14 | }
15 |
16 | protected Artist(Parcel in) {
17 | name = in.readString();
18 | }
19 |
20 | public String getName() {
21 | return name;
22 | }
23 |
24 | public boolean isFavorite() {
25 | return isFavorite;
26 | }
27 |
28 | @Override
29 | public boolean equals(Object o) {
30 | if (this == o) return true;
31 | if (!(o instanceof Artist)) return false;
32 |
33 | Artist artist = (Artist) o;
34 |
35 | if (isFavorite() != artist.isFavorite()) return false;
36 | return getName() != null ? getName().equals(artist.getName()) : artist.getName() == null;
37 |
38 | }
39 |
40 | @Override
41 | public int hashCode() {
42 | int result = getName() != null ? getName().hashCode() : 0;
43 | result = 31 * result + (isFavorite() ? 1 : 0);
44 | return result;
45 | }
46 |
47 | @Override
48 | public void writeToParcel(Parcel dest, int flags) {
49 | dest.writeString(name);
50 | }
51 |
52 | @Override
53 | public int describeContents() {
54 | return 0;
55 | }
56 |
57 | public static final Creator CREATOR = new Creator() {
58 | @Override
59 | public Artist createFromParcel(Parcel in) {
60 | return new Artist(in);
61 | }
62 |
63 | @Override
64 | public Artist[] newArray(int size) {
65 | return new Artist[size];
66 | }
67 | };
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/Genre.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample;
2 |
3 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
4 | import java.util.List;
5 |
6 | public class Genre extends ExpandableGroup {
7 |
8 | private int iconResId;
9 |
10 | public Genre(String title, List items, int iconResId) {
11 | super(title, items);
12 | this.iconResId = iconResId;
13 | }
14 |
15 | public int getIconResId() {
16 | return iconResId;
17 | }
18 |
19 | @Override
20 | public boolean equals(Object o) {
21 | if (this == o) return true;
22 | if (!(o instanceof Genre)) return false;
23 |
24 | Genre genre = (Genre) o;
25 |
26 | return getIconResId() == genre.getIconResId();
27 |
28 | }
29 |
30 | @Override
31 | public int hashCode() {
32 | return getIconResId();
33 | }
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/GenreDataFactory.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample;
2 |
3 | import com.thoughtbot.expandablerecyclerview.sample.multicheck.MultiCheckGenre;
4 | import com.thoughtbot.expandablerecyclerview.sample.singlecheck.SingleCheckGenre;
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | public class GenreDataFactory {
9 |
10 | public static List makeGenres() {
11 | return Arrays.asList(makeRockGenre(),
12 | makeJazzGenre(),
13 | makeClassicGenre(),
14 | makeSalsaGenre(),
15 | makeBluegrassGenre());
16 | }
17 |
18 | public static List makeMultiCheckGenres() {
19 | return Arrays.asList(makeMultiCheckRockGenre(),
20 | makeMultiCheckJazzGenre(),
21 | makeMultiCheckClassicGenre(),
22 | makeMultiCheckSalsaGenre(),
23 | makeMulitCheckBluegrassGenre());
24 | }
25 |
26 | public static List makeSingleCheckGenres() {
27 | return Arrays.asList(makeSingleCheckRockGenre(),
28 | makeSingleCheckJazzGenre(),
29 | makeSingleCheckClassicGenre(),
30 | makeSingleCheckSalsaGenre(),
31 | makeSingleCheckBluegrassGenre());
32 | }
33 |
34 | public static Genre makeRockGenre() {
35 | return new Genre("Rock", makeRockArtists(), R.drawable.ic_electric_guitar);
36 | }
37 |
38 | public static MultiCheckGenre makeMultiCheckRockGenre() {
39 | return new MultiCheckGenre("Rock", makeRockArtists(), R.drawable.ic_electric_guitar);
40 | }
41 |
42 | public static SingleCheckGenre makeSingleCheckRockGenre() {
43 | return new SingleCheckGenre("Rock", makeRockArtists(), R.drawable.ic_electric_guitar);
44 | }
45 |
46 | public static List makeRockArtists() {
47 | Artist queen = new Artist("Queen", true);
48 | Artist styx = new Artist("Styx", false);
49 | Artist reoSpeedwagon = new Artist("REO Speedwagon", false);
50 | Artist boston = new Artist("Boston", true);
51 |
52 | return Arrays.asList(queen, styx, reoSpeedwagon, boston);
53 | }
54 |
55 | public static Genre makeJazzGenre() {
56 | return new Genre("Jazz", makeJazzArtists(), R.drawable.ic_saxaphone);
57 | }
58 |
59 | public static MultiCheckGenre makeMultiCheckJazzGenre() {
60 | return new MultiCheckGenre("Jazz", makeJazzArtists(), R.drawable.ic_saxaphone);
61 | }
62 |
63 | public static SingleCheckGenre makeSingleCheckJazzGenre() {
64 | return new SingleCheckGenre("Jazz", makeJazzArtists(), R.drawable.ic_saxaphone);
65 | }
66 |
67 | public static List makeJazzArtists() {
68 | Artist milesDavis = new Artist("Miles Davis", true);
69 | Artist ellaFitzgerald = new Artist("Ella Fitzgerald", true);
70 | Artist billieHoliday = new Artist("Billie Holiday", false);
71 |
72 | return Arrays.asList(milesDavis, ellaFitzgerald, billieHoliday);
73 | }
74 |
75 | public static Genre makeClassicGenre() {
76 | return new Genre("Classic", makeClassicArtists(), R.drawable.ic_violin);
77 | }
78 |
79 | public static MultiCheckGenre makeMultiCheckClassicGenre() {
80 | return new MultiCheckGenre("Classic", makeClassicArtists(), R.drawable.ic_violin);
81 | }
82 |
83 | public static SingleCheckGenre makeSingleCheckClassicGenre() {
84 | return new SingleCheckGenre("Classic", makeClassicArtists(), R.drawable.ic_violin);
85 | }
86 |
87 | public static List makeClassicArtists() {
88 | Artist beethoven = new Artist("Ludwig van Beethoven", false);
89 | Artist bach = new Artist("Johann Sebastian Bach", true);
90 | Artist brahms = new Artist("Johannes Brahms", false);
91 | Artist puccini = new Artist("Giacomo Puccini", false);
92 |
93 | return Arrays.asList(beethoven, bach, brahms, puccini);
94 | }
95 |
96 | public static Genre makeSalsaGenre() {
97 | return new Genre("Salsa", makeSalsaArtists(), R.drawable.ic_maracas);
98 | }
99 |
100 | public static MultiCheckGenre makeMultiCheckSalsaGenre() {
101 | return new MultiCheckGenre("Salsa", makeSalsaArtists(), R.drawable.ic_maracas);
102 | }
103 |
104 | public static SingleCheckGenre makeSingleCheckSalsaGenre() {
105 | return new SingleCheckGenre("Salsa", makeSalsaArtists(), R.drawable.ic_maracas);
106 | }
107 |
108 | public static List makeSalsaArtists() {
109 | Artist hectorLavoe = new Artist("Hector Lavoe", true);
110 | Artist celiaCruz = new Artist("Celia Cruz", false);
111 | Artist willieColon = new Artist("Willie Colon", false);
112 | Artist marcAnthony = new Artist("Marc Anthony", false);
113 |
114 | return Arrays.asList(hectorLavoe, celiaCruz, willieColon, marcAnthony);
115 | }
116 |
117 | public static Genre makeBluegrassGenre() {
118 | return new Genre("Bluegrass", makeBluegrassArtists(), R.drawable.ic_banjo);
119 | }
120 |
121 | public static MultiCheckGenre makeMulitCheckBluegrassGenre() {
122 | return new MultiCheckGenre("Bluegrass", makeBluegrassArtists(), R.drawable.ic_banjo);
123 | }
124 |
125 | public static SingleCheckGenre makeSingleCheckBluegrassGenre() {
126 | return new SingleCheckGenre("Bluegrass", makeBluegrassArtists(), R.drawable.ic_banjo);
127 | }
128 |
129 | public static List makeBluegrassArtists() {
130 | Artist billMonroe = new Artist("Bill Monroe", false);
131 | Artist earlScruggs = new Artist("Earl Scruggs", false);
132 | Artist osborneBrothers = new Artist("Osborne Brothers", true);
133 | Artist johnHartford = new Artist("John Hartford", false);
134 |
135 | return Arrays.asList(billMonroe, earlScruggs, osborneBrothers, johnHartford);
136 | }
137 |
138 | }
139 |
140 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.View;
7 | import android.view.View.OnClickListener;
8 | import android.widget.Button;
9 | import com.thoughtbot.expandablerecyclerview.sample.expand.ExpandActivity;
10 | import com.thoughtbot.expandablerecyclerview.sample.multicheck.MultiCheckActivity;
11 | import com.thoughtbot.expandablerecyclerview.sample.multitype.MultiTypeActivity;
12 | import com.thoughtbot.expandablerecyclerview.sample.multitypeandcheck.MultiTypeCheckGenreActivity;
13 | import com.thoughtbot.expandablerecyclerview.sample.singlecheck.SingleCheckActivity;
14 |
15 | public class MainActivity extends Activity {
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.activity_main);
21 |
22 | Button expand = (Button) findViewById(R.id.expand_button);
23 | expand.setOnClickListener(navigateTo(ExpandActivity.class));
24 |
25 | Button multiSelect = (Button) findViewById(R.id.multi_check_button);
26 | multiSelect.setOnClickListener(navigateTo(MultiCheckActivity.class));
27 |
28 | Button singleSelect = (Button) findViewById(R.id.single_check_button);
29 | singleSelect.setOnClickListener(navigateTo(SingleCheckActivity.class));
30 |
31 | Button mixedSelect = (Button) findViewById(R.id.mixedtype_button);
32 | mixedSelect.setOnClickListener(navigateTo(MultiTypeActivity.class));
33 |
34 | Button mixedTypeAndCheck = (Button) findViewById(R.id.mixedtype_check_button);
35 | mixedTypeAndCheck.setOnClickListener(navigateTo(MultiTypeCheckGenreActivity.class));
36 | }
37 |
38 | public OnClickListener navigateTo(final Class> clazz) {
39 | return new OnClickListener() {
40 | @Override
41 | public void onClick(View v) {
42 | Intent intent = new Intent(MainActivity.this, clazz);
43 | startActivity(intent);
44 | }
45 | };
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/expand/ArtistViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.expand;
2 |
3 | import android.view.View;
4 | import android.widget.TextView;
5 | import com.thoughtbot.expandablerecyclerview.sample.R;
6 | import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder;
7 |
8 | public class ArtistViewHolder extends ChildViewHolder {
9 |
10 | private TextView childTextView;
11 |
12 | public ArtistViewHolder(View itemView) {
13 | super(itemView);
14 | childTextView = (TextView) itemView.findViewById(R.id.list_item_artist_name);
15 | }
16 |
17 | public void setArtistName(String name) {
18 | childTextView.setText(name);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/expand/ExpandActivity.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.expand;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.support.v7.widget.DefaultItemAnimator;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.support.v7.widget.RecyclerView;
9 | import android.view.View;
10 | import android.view.View.OnClickListener;
11 | import android.widget.Button;
12 | import com.thoughtbot.expandablerecyclerview.sample.R;
13 |
14 | import static com.thoughtbot.expandablerecyclerview.sample.GenreDataFactory.makeClassicGenre;
15 | import static com.thoughtbot.expandablerecyclerview.sample.GenreDataFactory.makeGenres;
16 |
17 | public class ExpandActivity extends AppCompatActivity {
18 |
19 | public GenreAdapter adapter;
20 |
21 | @Override
22 | protected void onCreate(@Nullable Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_expand);
25 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
26 | getSupportActionBar().setTitle(getClass().getSimpleName());
27 |
28 | RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
29 | LinearLayoutManager layoutManager = new LinearLayoutManager(this);
30 |
31 | // RecyclerView has some built in animations to it, using the DefaultItemAnimator.
32 | // Specifically when you call notifyItemChanged() it does a fade animation for the changing
33 | // of the data in the ViewHolder. If you would like to disable this you can use the following:
34 | RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator();
35 | if (animator instanceof DefaultItemAnimator) {
36 | ((DefaultItemAnimator) animator).setSupportsChangeAnimations(false);
37 | }
38 |
39 | adapter = new GenreAdapter(makeGenres());
40 | recyclerView.setLayoutManager(layoutManager);
41 | recyclerView.setAdapter(adapter);
42 |
43 | Button clear = (Button) findViewById(R.id.toggle_button);
44 | clear.setOnClickListener(new OnClickListener() {
45 | @Override
46 | public void onClick(View v) {
47 | adapter.toggleGroup(makeClassicGenre());
48 | }
49 | });
50 | }
51 |
52 | @Override
53 | protected void onSaveInstanceState(Bundle outState) {
54 | super.onSaveInstanceState(outState);
55 | adapter.onSaveInstanceState(outState);
56 | }
57 |
58 | @Override
59 | protected void onRestoreInstanceState(Bundle savedInstanceState) {
60 | super.onRestoreInstanceState(savedInstanceState);
61 | adapter.onRestoreInstanceState(savedInstanceState);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/expand/GenreAdapter.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.expand;
2 |
3 | import android.view.LayoutInflater;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import com.thoughtbot.expandablerecyclerview.ExpandableRecyclerViewAdapter;
7 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
8 | import com.thoughtbot.expandablerecyclerview.sample.Artist;
9 | import com.thoughtbot.expandablerecyclerview.sample.Genre;
10 | import com.thoughtbot.expandablerecyclerview.sample.R;
11 | import java.util.List;
12 |
13 | public class GenreAdapter extends ExpandableRecyclerViewAdapter {
14 |
15 | public GenreAdapter(List extends ExpandableGroup> groups) {
16 | super(groups);
17 | }
18 |
19 | @Override
20 | public GenreViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {
21 | View view = LayoutInflater.from(parent.getContext())
22 | .inflate(R.layout.list_item_genre, parent, false);
23 | return new GenreViewHolder(view);
24 | }
25 |
26 | @Override
27 | public ArtistViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {
28 | View view = LayoutInflater.from(parent.getContext())
29 | .inflate(R.layout.list_item_artist, parent, false);
30 | return new ArtistViewHolder(view);
31 | }
32 |
33 | @Override
34 | public void onBindChildViewHolder(ArtistViewHolder holder, int flatPosition,
35 | ExpandableGroup group, int childIndex) {
36 |
37 | final Artist artist = ((Genre) group).getItems().get(childIndex);
38 | holder.setArtistName(artist.getName());
39 | }
40 |
41 | @Override
42 | public void onBindGroupViewHolder(GenreViewHolder holder, int flatPosition,
43 | ExpandableGroup group) {
44 |
45 | holder.setGenreTitle(group);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/expand/GenreViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.expand;
2 |
3 | import android.view.View;
4 | import android.view.animation.RotateAnimation;
5 | import android.widget.ImageView;
6 | import android.widget.TextView;
7 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
8 | import com.thoughtbot.expandablerecyclerview.sample.Genre;
9 | import com.thoughtbot.expandablerecyclerview.sample.R;
10 | import com.thoughtbot.expandablerecyclerview.sample.multicheck.MultiCheckGenre;
11 | import com.thoughtbot.expandablerecyclerview.sample.singlecheck.SingleCheckGenre;
12 | import com.thoughtbot.expandablerecyclerview.viewholders.GroupViewHolder;
13 |
14 | import static android.view.animation.Animation.RELATIVE_TO_SELF;
15 |
16 | public class GenreViewHolder extends GroupViewHolder {
17 |
18 | private TextView genreName;
19 | private ImageView arrow;
20 | private ImageView icon;
21 |
22 | public GenreViewHolder(View itemView) {
23 | super(itemView);
24 | genreName = (TextView) itemView.findViewById(R.id.list_item_genre_name);
25 | arrow = (ImageView) itemView.findViewById(R.id.list_item_genre_arrow);
26 | icon = (ImageView) itemView.findViewById(R.id.list_item_genre_icon);
27 | }
28 |
29 | public void setGenreTitle(ExpandableGroup genre) {
30 | if (genre instanceof Genre) {
31 | genreName.setText(genre.getTitle());
32 | icon.setBackgroundResource(((Genre) genre).getIconResId());
33 | }
34 | if (genre instanceof MultiCheckGenre) {
35 | genreName.setText(genre.getTitle());
36 | icon.setBackgroundResource(((MultiCheckGenre) genre).getIconResId());
37 | }
38 | if (genre instanceof SingleCheckGenre) {
39 | genreName.setText(genre.getTitle());
40 | icon.setBackgroundResource(((SingleCheckGenre) genre).getIconResId());
41 | }
42 | }
43 |
44 | @Override
45 | public void expand() {
46 | animateExpand();
47 | }
48 |
49 | @Override
50 | public void collapse() {
51 | animateCollapse();
52 | }
53 |
54 | private void animateExpand() {
55 | RotateAnimation rotate =
56 | new RotateAnimation(360, 180, RELATIVE_TO_SELF, 0.5f, RELATIVE_TO_SELF, 0.5f);
57 | rotate.setDuration(300);
58 | rotate.setFillAfter(true);
59 | arrow.setAnimation(rotate);
60 | }
61 |
62 | private void animateCollapse() {
63 | RotateAnimation rotate =
64 | new RotateAnimation(180, 360, RELATIVE_TO_SELF, 0.5f, RELATIVE_TO_SELF, 0.5f);
65 | rotate.setDuration(300);
66 | rotate.setFillAfter(true);
67 | arrow.setAnimation(rotate);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/multicheck/MultiCheckActivity.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.multicheck;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.view.View;
9 | import android.view.View.OnClickListener;
10 | import android.widget.Button;
11 | import com.thoughtbot.expandablerecyclerview.sample.R;
12 |
13 | import static com.thoughtbot.expandablerecyclerview.sample.GenreDataFactory.makeMultiCheckGenres;
14 |
15 | public class MultiCheckActivity extends AppCompatActivity {
16 |
17 | private MultiCheckGenreAdapter adapter;
18 |
19 | @Override
20 | protected void onCreate(@Nullable Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | setContentView(R.layout.activity_multi_check);
23 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
24 | getSupportActionBar().setTitle(getClass().getSimpleName());
25 |
26 | RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
27 | LinearLayoutManager layoutManager = new LinearLayoutManager(this);
28 |
29 | adapter = new MultiCheckGenreAdapter(makeMultiCheckGenres());
30 | recyclerView.setLayoutManager(layoutManager);
31 | recyclerView.setAdapter(adapter);
32 |
33 | Button clear = (Button) findViewById(R.id.clear_button);
34 | clear.setOnClickListener(new OnClickListener() {
35 | @Override
36 | public void onClick(View v) {
37 | adapter.clearChoices();
38 | }
39 | });
40 |
41 | Button check = (Button) findViewById(R.id.check_first_child);
42 | check.setOnClickListener(new OnClickListener() {
43 | @Override
44 | public void onClick(View v) {
45 | adapter.checkChild(true, 0, 3);
46 | }
47 | });
48 | }
49 |
50 | @Override
51 | protected void onSaveInstanceState(Bundle outState) {
52 | super.onSaveInstanceState(outState);
53 | adapter.onSaveInstanceState(outState);
54 | }
55 |
56 | @Override
57 | protected void onRestoreInstanceState(Bundle savedInstanceState) {
58 | super.onRestoreInstanceState(savedInstanceState);
59 | adapter.onRestoreInstanceState(savedInstanceState);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/multicheck/MultiCheckArtistViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.multicheck;
2 |
3 | import android.view.View;
4 | import android.widget.Checkable;
5 | import android.widget.CheckedTextView;
6 | import com.thoughtbot.expandablecheckrecyclerview.viewholders.CheckableChildViewHolder;
7 | import com.thoughtbot.expandablerecyclerview.sample.R;
8 |
9 | public class MultiCheckArtistViewHolder extends CheckableChildViewHolder {
10 |
11 | private CheckedTextView childCheckedTextView;
12 |
13 | public MultiCheckArtistViewHolder(View itemView) {
14 | super(itemView);
15 | childCheckedTextView =
16 | (CheckedTextView) itemView.findViewById(R.id.list_item_multicheck_artist_name);
17 | }
18 |
19 | @Override
20 | public Checkable getCheckable() {
21 | return childCheckedTextView;
22 | }
23 |
24 | public void setArtistName(String artistName) {
25 | childCheckedTextView.setText(artistName);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/multicheck/MultiCheckGenre.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.multicheck;
2 |
3 | import com.thoughtbot.expandablecheckrecyclerview.models.MultiCheckExpandableGroup;
4 | import com.thoughtbot.expandablerecyclerview.sample.Artist;
5 | import java.util.List;
6 |
7 | public class MultiCheckGenre extends MultiCheckExpandableGroup {
8 |
9 | private int iconResId;
10 |
11 | public MultiCheckGenre(String title, List items, int iconResId) {
12 | super(title, items);
13 | this.iconResId = iconResId;
14 | }
15 |
16 | public int getIconResId() {
17 | return iconResId;
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/multicheck/MultiCheckGenreAdapter.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.multicheck;
2 |
3 | import android.view.LayoutInflater;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import com.thoughtbot.expandablecheckrecyclerview.CheckableChildRecyclerViewAdapter;
7 | import com.thoughtbot.expandablecheckrecyclerview.models.CheckedExpandableGroup;
8 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
9 | import com.thoughtbot.expandablerecyclerview.sample.Artist;
10 | import com.thoughtbot.expandablerecyclerview.sample.R;
11 | import com.thoughtbot.expandablerecyclerview.sample.expand.GenreViewHolder;
12 | import java.util.List;
13 |
14 | public class MultiCheckGenreAdapter extends
15 | CheckableChildRecyclerViewAdapter {
16 |
17 | public MultiCheckGenreAdapter(List groups) {
18 | super(groups);
19 | }
20 |
21 | @Override
22 | public MultiCheckArtistViewHolder onCreateCheckChildViewHolder(ViewGroup parent, int viewType) {
23 | View view = LayoutInflater.from(parent.getContext())
24 | .inflate(R.layout.list_item_multicheck_artist, parent, false);
25 | return new MultiCheckArtistViewHolder(view);
26 | }
27 |
28 | @Override
29 | public void onBindCheckChildViewHolder(MultiCheckArtistViewHolder holder, int position,
30 | CheckedExpandableGroup group, int childIndex) {
31 | final Artist artist = (Artist) group.getItems().get(childIndex);
32 | holder.setArtistName(artist.getName());
33 | }
34 |
35 | @Override
36 | public GenreViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {
37 | View view = LayoutInflater.from(parent.getContext())
38 | .inflate(R.layout.list_item_genre, parent, false);
39 | return new GenreViewHolder(view);
40 | }
41 |
42 | @Override
43 | public void onBindGroupViewHolder(GenreViewHolder holder, int flatPosition,
44 | ExpandableGroup group) {
45 | holder.setGenreTitle(group);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/multitype/FavoriteArtistViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.multitype;
2 |
3 | import android.view.View;
4 | import android.widget.TextView;
5 | import com.thoughtbot.expandablerecyclerview.sample.R;
6 | import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder;
7 |
8 | public class FavoriteArtistViewHolder extends ChildViewHolder {
9 |
10 | private TextView favoriteArtistName;
11 |
12 | public FavoriteArtistViewHolder(View itemView) {
13 | super(itemView);
14 | favoriteArtistName = (TextView) itemView.findViewById(R.id.list_item_favorite_artist_name);
15 | }
16 |
17 | public void setArtistName(String name) {
18 | favoriteArtistName.setText(name);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/multitype/MultiTypeActivity.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.multitype;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import com.thoughtbot.expandablerecyclerview.sample.R;
9 |
10 | import static com.thoughtbot.expandablerecyclerview.sample.GenreDataFactory.makeGenres;
11 |
12 | public class MultiTypeActivity extends AppCompatActivity {
13 |
14 | private MultiTypeGenreAdapter adapter;
15 |
16 | @Override
17 | protected void onCreate(@Nullable Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_multi_type);
20 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
21 | getSupportActionBar().setTitle(getClass().getSimpleName());
22 |
23 | RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
24 | LinearLayoutManager layoutManager = new LinearLayoutManager(this);
25 |
26 | adapter = new MultiTypeGenreAdapter(makeGenres());
27 | recyclerView.setLayoutManager(layoutManager);
28 | recyclerView.setAdapter(adapter);
29 | }
30 |
31 | @Override
32 | protected void onSaveInstanceState(Bundle outState) {
33 | super.onSaveInstanceState(outState);
34 | adapter.onSaveInstanceState(outState);
35 | }
36 |
37 | @Override
38 | protected void onRestoreInstanceState(Bundle savedInstanceState) {
39 | super.onRestoreInstanceState(savedInstanceState);
40 | adapter.onRestoreInstanceState(savedInstanceState);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/multitype/MultiTypeGenreAdapter.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.multitype;
2 |
3 | import android.view.View;
4 | import android.view.ViewGroup;
5 | import com.thoughtbot.expandablerecyclerview.MultiTypeExpandableRecyclerViewAdapter;
6 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
7 | import com.thoughtbot.expandablerecyclerview.models.ExpandableListPosition;
8 | import com.thoughtbot.expandablerecyclerview.sample.Artist;
9 | import com.thoughtbot.expandablerecyclerview.sample.Genre;
10 | import com.thoughtbot.expandablerecyclerview.sample.R;
11 | import com.thoughtbot.expandablerecyclerview.sample.expand.ArtistViewHolder;
12 | import com.thoughtbot.expandablerecyclerview.sample.expand.GenreViewHolder;
13 | import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder;
14 | import java.util.List;
15 |
16 | import static android.view.LayoutInflater.from;
17 |
18 | public class MultiTypeGenreAdapter
19 | extends MultiTypeExpandableRecyclerViewAdapter {
20 |
21 | public static final int FAVORITE_VIEW_TYPE = 3;
22 | public static final int ARTIST_VIEW_TYPE = 4;
23 |
24 | public MultiTypeGenreAdapter(List groups) {
25 | super(groups);
26 | }
27 |
28 | @Override
29 | public GenreViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {
30 | View view = from(parent.getContext())
31 | .inflate(R.layout.list_item_genre, parent, false);
32 | return new GenreViewHolder(view);
33 | }
34 |
35 | @Override
36 | public ChildViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {
37 | switch (viewType) {
38 | case ARTIST_VIEW_TYPE:
39 | View artist = from(parent.getContext()).inflate(R.layout.list_item_artist, parent, false);
40 | return new ArtistViewHolder(artist);
41 | case FAVORITE_VIEW_TYPE:
42 | View favorite =
43 | from(parent.getContext()).inflate(R.layout.list_item_favorite_artist, parent, false);
44 | return new FavoriteArtistViewHolder(favorite);
45 | default:
46 | throw new IllegalArgumentException("Invalid viewType");
47 | }
48 | }
49 |
50 | @Override
51 | public void onBindChildViewHolder(ChildViewHolder holder, int flatPosition, ExpandableGroup group,
52 | int childIndex) {
53 | int viewType = getItemViewType(flatPosition);
54 | Artist artist = ((Genre) group).getItems().get(childIndex);
55 | switch (viewType) {
56 | case ARTIST_VIEW_TYPE:
57 | ((ArtistViewHolder) holder).setArtistName(artist.getName());
58 | break;
59 | case FAVORITE_VIEW_TYPE:
60 | ((FavoriteArtistViewHolder) holder).setArtistName(artist.getName());
61 | }
62 | }
63 |
64 | @Override
65 | public void onBindGroupViewHolder(GenreViewHolder holder, int flatPosition,
66 | ExpandableGroup group) {
67 | holder.setGenreTitle(group);
68 | }
69 |
70 | @Override
71 | public int getChildViewType(int position, ExpandableGroup group, int childIndex) {
72 | if (((Genre) group).getItems().get(childIndex).isFavorite()) {
73 | return FAVORITE_VIEW_TYPE;
74 | } else {
75 | return ARTIST_VIEW_TYPE;
76 | }
77 | }
78 |
79 | @Override
80 | public boolean isGroup(int viewType) {
81 | return viewType == ExpandableListPosition.GROUP;
82 | }
83 |
84 | @Override
85 | public boolean isChild(int viewType) {
86 | return viewType == FAVORITE_VIEW_TYPE || viewType == ARTIST_VIEW_TYPE;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/multitypeandcheck/MultiTypeCheckGenreActivity.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.multitypeandcheck;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.view.View;
9 | import android.view.View.OnClickListener;
10 | import android.widget.Button;
11 | import com.thoughtbot.expandablerecyclerview.sample.R;
12 |
13 | import static com.thoughtbot.expandablerecyclerview.sample.GenreDataFactory.makeSingleCheckGenres;
14 |
15 | public class MultiTypeCheckGenreActivity extends AppCompatActivity {
16 |
17 | private MultiTypeCheckGenreAdapter adapter;
18 |
19 | @Override
20 | protected void onCreate(@Nullable Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | setContentView(R.layout.activity_multi_type_and_check);
23 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
24 | getSupportActionBar().setTitle(getClass().getSimpleName());
25 |
26 | RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
27 | LinearLayoutManager layoutManager = new LinearLayoutManager(this);
28 |
29 | adapter = new MultiTypeCheckGenreAdapter(makeSingleCheckGenres());
30 | recyclerView.setLayoutManager(layoutManager);
31 | recyclerView.setAdapter(adapter);
32 |
33 | Button clear = (Button) findViewById(R.id.clear_button);
34 | clear.setOnClickListener(new OnClickListener() {
35 | @Override
36 | public void onClick(View v) {
37 | adapter.clearChoices();
38 | }
39 | });
40 | }
41 |
42 | @Override
43 | protected void onSaveInstanceState(Bundle outState) {
44 | super.onSaveInstanceState(outState);
45 | adapter.onSaveInstanceState(outState);
46 | }
47 |
48 | @Override
49 | protected void onRestoreInstanceState(Bundle savedInstanceState) {
50 | super.onRestoreInstanceState(savedInstanceState);
51 | adapter.onRestoreInstanceState(savedInstanceState);
52 | }
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/multitypeandcheck/MultiTypeCheckGenreAdapter.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.multitypeandcheck;
2 |
3 | import android.os.Bundle;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import com.thoughtbot.expandablecheckrecyclerview.ChildCheckController;
8 | import com.thoughtbot.expandablecheckrecyclerview.listeners.OnCheckChildClickListener;
9 | import com.thoughtbot.expandablecheckrecyclerview.listeners.OnChildCheckChangedListener;
10 | import com.thoughtbot.expandablecheckrecyclerview.listeners.OnChildrenCheckStateChangedListener;
11 | import com.thoughtbot.expandablecheckrecyclerview.models.CheckedExpandableGroup;
12 | import com.thoughtbot.expandablerecyclerview.MultiTypeExpandableRecyclerViewAdapter;
13 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
14 | import com.thoughtbot.expandablerecyclerview.models.ExpandableListPosition;
15 | import com.thoughtbot.expandablerecyclerview.sample.Artist;
16 | import com.thoughtbot.expandablerecyclerview.sample.R;
17 | import com.thoughtbot.expandablerecyclerview.sample.expand.ArtistViewHolder;
18 | import com.thoughtbot.expandablerecyclerview.sample.expand.GenreViewHolder;
19 | import com.thoughtbot.expandablerecyclerview.sample.singlecheck.SingleCheckArtistViewHolder;
20 | import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder;
21 | import java.util.ArrayList;
22 | import java.util.List;
23 |
24 | import static android.view.LayoutInflater.from;
25 |
26 | public class MultiTypeCheckGenreAdapter
27 | extends MultiTypeExpandableRecyclerViewAdapter
28 | implements OnChildCheckChangedListener, OnChildrenCheckStateChangedListener {
29 |
30 | private static final String CHECKED_STATE_MAP = "child_check_controller_checked_state_map";
31 |
32 | public static final int FAVORITE_VIEW_TYPE = 3;
33 | public static final int ARTIST_VIEW_TYPE = 4;
34 |
35 | private ChildCheckController childCheckController;
36 | private OnCheckChildClickListener childClickListener;
37 |
38 | public MultiTypeCheckGenreAdapter(List extends ExpandableGroup> groups) {
39 | super(groups);
40 | childCheckController = new ChildCheckController(expandableList, this);
41 | }
42 |
43 | @Override
44 | public GenreViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {
45 | View view = from(parent.getContext())
46 | .inflate(R.layout.list_item_genre, parent, false);
47 | return new GenreViewHolder(view);
48 | }
49 |
50 | @Override
51 | public ChildViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {
52 | switch (viewType) {
53 | case ARTIST_VIEW_TYPE:
54 | View artist = from(parent.getContext()).inflate(R.layout.list_item_artist, parent, false);
55 | return new ArtistViewHolder(artist);
56 | case FAVORITE_VIEW_TYPE:
57 | View view = LayoutInflater.from(parent.getContext())
58 | .inflate(R.layout.list_item_singlecheck_arist, parent, false);
59 | SingleCheckArtistViewHolder holder = new SingleCheckArtistViewHolder(view);
60 | holder.setOnChildCheckedListener(this);
61 | return holder;
62 | default:
63 | throw new IllegalArgumentException(viewType + " is an Invalid viewType");
64 | }
65 | }
66 |
67 | @Override
68 | public void onBindChildViewHolder(ChildViewHolder holder, int flatPosition, ExpandableGroup group,
69 | int childIndex) {
70 | int viewType = getItemViewType(flatPosition);
71 | Artist artist = (Artist) group.getItems().get(childIndex);
72 | switch (viewType) {
73 | case ARTIST_VIEW_TYPE:
74 | ((ArtistViewHolder) holder).setArtistName(artist.getName());
75 | break;
76 | case FAVORITE_VIEW_TYPE:
77 | ExpandableListPosition listPosition = expandableList.getUnflattenedPosition(flatPosition);
78 | ((SingleCheckArtistViewHolder) holder)
79 | .onBindViewHolder(flatPosition, childCheckController.isChildChecked(listPosition));
80 | ((SingleCheckArtistViewHolder) holder).setArtistName(artist.getName());
81 | }
82 | }
83 |
84 | @Override
85 | public void onBindGroupViewHolder(GenreViewHolder holder, int flatPosition,
86 | ExpandableGroup group) {
87 | holder.setGenreTitle(group);
88 | }
89 |
90 | @Override
91 | public void onChildCheckChanged(View view, boolean checked, int flatPos) {
92 | ExpandableListPosition listPos = expandableList.getUnflattenedPosition(flatPos);
93 | childCheckController.onChildCheckChanged(checked, listPos);
94 | if (childClickListener != null) {
95 | childClickListener.onCheckChildCLick(view, checked,
96 | (CheckedExpandableGroup) expandableList.getExpandableGroup(listPos), listPos.childPos);
97 | }
98 | }
99 |
100 | @Override
101 | public void updateChildrenCheckState(int firstChildFlattenedIndex, int numChildren) {
102 | notifyItemRangeChanged(firstChildFlattenedIndex, numChildren);
103 | }
104 |
105 | @Override
106 | public void onSaveInstanceState(Bundle outState) {
107 | outState.putParcelableArrayList(CHECKED_STATE_MAP, new ArrayList(expandableList.groups));
108 | super.onSaveInstanceState(outState);
109 | }
110 |
111 | @Override
112 | public void onRestoreInstanceState(Bundle savedInstanceState) {
113 | if (savedInstanceState == null || !savedInstanceState.containsKey(CHECKED_STATE_MAP)) {
114 | return;
115 | }
116 | expandableList.groups = savedInstanceState.getParcelableArrayList(CHECKED_STATE_MAP);
117 | super.onRestoreInstanceState(savedInstanceState);
118 | }
119 |
120 | public void clearChoices() {
121 | childCheckController.clearCheckStates();
122 |
123 | //only update the child views that are visible (i.e. their group is expanded)
124 | for (int i = 0; i < getGroups().size(); i++) {
125 | ExpandableGroup group = getGroups().get(i);
126 | if (isGroupExpanded(group)) {
127 | notifyItemRangeChanged(expandableList.getFlattenedFirstChildIndex(i), group.getItemCount());
128 | }
129 | }
130 | }
131 |
132 | @Override
133 | public boolean isChild(int viewType) {
134 | return viewType == FAVORITE_VIEW_TYPE || viewType == ARTIST_VIEW_TYPE;
135 | }
136 |
137 | @Override
138 | public int getChildViewType(int position, ExpandableGroup group, int childIndex) {
139 | if (((Artist) (group).getItems().get(childIndex)).isFavorite()) {
140 | return FAVORITE_VIEW_TYPE;
141 | } else {
142 | return ARTIST_VIEW_TYPE;
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/singlecheck/SingleCheckActivity.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.singlecheck;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.view.View;
9 | import android.view.View.OnClickListener;
10 | import android.widget.Button;
11 | import com.thoughtbot.expandablerecyclerview.sample.R;
12 |
13 | import static com.thoughtbot.expandablerecyclerview.sample.GenreDataFactory.makeSingleCheckGenres;
14 |
15 | public class SingleCheckActivity extends AppCompatActivity {
16 |
17 | private SingleCheckGenreAdapter adapter;
18 |
19 | @Override
20 | protected void onCreate(@Nullable Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | setContentView(R.layout.activity_single_check);
23 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
24 | getSupportActionBar().setTitle(getClass().getSimpleName());
25 |
26 | RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
27 | LinearLayoutManager layoutManager = new LinearLayoutManager(this);
28 |
29 | adapter = new SingleCheckGenreAdapter(makeSingleCheckGenres());
30 | recyclerView.setLayoutManager(layoutManager);
31 | recyclerView.setAdapter(adapter);
32 |
33 | Button clear = (Button) findViewById(R.id.clear_button);
34 | clear.setOnClickListener(new OnClickListener() {
35 | @Override
36 | public void onClick(View v) {
37 | adapter.clearChoices();
38 | }
39 | });
40 | }
41 |
42 | @Override
43 | protected void onSaveInstanceState(Bundle outState) {
44 | super.onSaveInstanceState(outState);
45 | adapter.onSaveInstanceState(outState);
46 | }
47 |
48 | @Override
49 | protected void onRestoreInstanceState(Bundle savedInstanceState) {
50 | super.onRestoreInstanceState(savedInstanceState);
51 | adapter.onRestoreInstanceState(savedInstanceState);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/singlecheck/SingleCheckArtistViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.singlecheck;
2 |
3 | import android.view.View;
4 | import android.widget.Checkable;
5 | import android.widget.CheckedTextView;
6 | import com.thoughtbot.expandablecheckrecyclerview.viewholders.CheckableChildViewHolder;
7 | import com.thoughtbot.expandablerecyclerview.sample.R;
8 |
9 | public class SingleCheckArtistViewHolder extends CheckableChildViewHolder {
10 |
11 | private CheckedTextView childCheckedTextView;
12 |
13 | public SingleCheckArtistViewHolder(View itemView) {
14 | super(itemView);
15 | childCheckedTextView =
16 | (CheckedTextView) itemView.findViewById(R.id.list_item_singlecheck_artist_name);
17 | }
18 |
19 | @Override
20 | public Checkable getCheckable() {
21 | return childCheckedTextView;
22 | }
23 |
24 | public void setArtistName(String artistName) {
25 | childCheckedTextView.setText(artistName);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/singlecheck/SingleCheckGenre.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.singlecheck;
2 |
3 | import android.os.Parcel;
4 | import com.thoughtbot.expandablecheckrecyclerview.models.SingleCheckExpandableGroup;
5 | import java.util.List;
6 |
7 | public class SingleCheckGenre extends SingleCheckExpandableGroup {
8 |
9 | private int iconResId;
10 |
11 | public SingleCheckGenre(String title, List items, int iconResId) {
12 | super(title, items);
13 | this.iconResId = iconResId;
14 | }
15 |
16 | protected SingleCheckGenre(Parcel in) {
17 | super(in);
18 | iconResId = in.readInt();
19 | }
20 |
21 | public int getIconResId() {
22 | return iconResId;
23 | }
24 |
25 | @Override
26 | public void writeToParcel(Parcel dest, int flags) {
27 | super.writeToParcel(dest, flags);
28 | dest.writeInt(iconResId);
29 | }
30 |
31 | @Override
32 | public int describeContents() {
33 | return 0;
34 | }
35 |
36 | public static final Creator CREATOR = new Creator() {
37 | @Override
38 | public SingleCheckGenre createFromParcel(Parcel in) {
39 | return new SingleCheckGenre(in);
40 | }
41 |
42 | @Override
43 | public SingleCheckGenre[] newArray(int size) {
44 | return new SingleCheckGenre[size];
45 | }
46 | };
47 | }
48 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/thoughtbot/expandablerecyclerview/sample/singlecheck/SingleCheckGenreAdapter.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample.singlecheck;
2 |
3 | import android.view.LayoutInflater;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import com.thoughtbot.expandablecheckrecyclerview.CheckableChildRecyclerViewAdapter;
7 | import com.thoughtbot.expandablecheckrecyclerview.models.CheckedExpandableGroup;
8 | import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
9 | import com.thoughtbot.expandablerecyclerview.sample.Artist;
10 | import com.thoughtbot.expandablerecyclerview.sample.R;
11 | import com.thoughtbot.expandablerecyclerview.sample.expand.GenreViewHolder;
12 | import java.util.List;
13 |
14 | public class SingleCheckGenreAdapter extends
15 | CheckableChildRecyclerViewAdapter {
16 |
17 | public SingleCheckGenreAdapter(List groups) {
18 | super(groups);
19 | }
20 |
21 | @Override
22 | public SingleCheckArtistViewHolder onCreateCheckChildViewHolder(ViewGroup parent, int viewType) {
23 | View view = LayoutInflater.from(parent.getContext())
24 | .inflate(R.layout.list_item_singlecheck_arist, parent, false);
25 | return new SingleCheckArtistViewHolder(view);
26 | }
27 |
28 | @Override
29 | public void onBindCheckChildViewHolder(SingleCheckArtistViewHolder holder, int position,
30 | CheckedExpandableGroup group, int childIndex) {
31 | final Artist artist = (Artist) group.getItems().get(childIndex);
32 | holder.setArtistName(artist.getName());
33 | }
34 |
35 | @Override
36 | public GenreViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {
37 | View view = LayoutInflater.from(parent.getContext())
38 | .inflate(R.layout.list_item_genre, parent, false);
39 | return new GenreViewHolder(view);
40 | }
41 |
42 | @Override
43 | public void onBindGroupViewHolder(GenreViewHolder holder, int flatPosition,
44 | ExpandableGroup group) {
45 | holder.setGenreTitle(group);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_arrow_down.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
13 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_banjo.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
22 |
31 |
40 |
48 |
58 |
68 |
78 |
82 |
92 |
102 |
112 |
122 |
137 |
140 |
150 |
160 |
170 |
171 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_electric_guitar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
23 |
36 |
40 |
44 |
51 |
58 |
67 |
74 |
77 |
84 |
91 |
98 |
99 |
109 |
119 |
129 |
133 |
150 |
151 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_maracas.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
32 |
48 |
63 |
77 |
93 |
115 |
118 |
127 |
138 |
139 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_saxaphone.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
10 |
16 |
48 |
49 |
62 |
71 |
80 |
89 |
98 |
102 |
106 |
110 |
114 |
118 |
122 |
126 |
130 |
134 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_star.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
14 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_violin.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
10 |
14 |
21 |
27 |
31 |
32 |
49 |
62 |
66 |
75 |
87 |
102 |
105 |
111 |
116 |
117 |
121 |
130 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_expand.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
19 |
20 |
25 |
26 |
31 |
32 |
37 |
38 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_multi_check.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
19 |
20 |
25 |
26 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_multi_type.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_multi_type_and_check.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_single_check.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/list_item_artist.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/list_item_favorite_artist.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/list_item_genre.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
24 |
25 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/list_item_multicheck_artist.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/list_item_singlecheck_arist.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoughtbot/expandable-recycler-view/56e45355c069ac602301da021691ea09ea7d0287/sample/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoughtbot/expandable-recycler-view/56e45355c069ac602301da021691ea09ea7d0287/sample/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoughtbot/expandable-recycler-view/56e45355c069ac602301da021691ea09ea7d0287/sample/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoughtbot/expandable-recycler-view/56e45355c069ac602301da021691ea09ea7d0287/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoughtbot/expandable-recycler-view/56e45355c069ac602301da021691ea09ea7d0287/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #F44336
4 | #D32F2F
5 | #FFC107
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 48dp
4 | 16dp
5 | 72dp
6 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ExpandableRecyclerView
3 |
4 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sample/src/test/java/com/thoughtbot/expandablerecyclerview/sample/GenreAdapterTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.support.v7.widget.RecyclerView.ViewHolder;
6 | import android.widget.LinearLayout;
7 | import com.thoughtbot.expandablerecyclerview.models.ExpandableListPosition;
8 | import com.thoughtbot.expandablerecyclerview.sample.expand.ArtistViewHolder;
9 | import com.thoughtbot.expandablerecyclerview.sample.expand.GenreAdapter;
10 | import com.thoughtbot.expandablerecyclerview.sample.expand.GenreViewHolder;
11 | import java.util.List;
12 | import org.junit.Before;
13 | import org.junit.Test;
14 | import org.junit.runner.RunWith;
15 | import org.mockito.MockitoAnnotations;
16 | import org.robolectric.RobolectricTestRunner;
17 | import org.robolectric.RuntimeEnvironment;
18 | import org.robolectric.annotation.Config;
19 |
20 | import static junit.framework.Assert.assertEquals;
21 | import static junit.framework.Assert.assertNotNull;
22 | import static junit.framework.Assert.assertTrue;
23 |
24 | /**
25 | * Unit test for GenreAdapter
26 | */
27 |
28 | @RunWith(RobolectricTestRunner.class)
29 | @Config(constants = BuildConfig.class, sdk = 21)
30 | public class GenreAdapterTest {
31 |
32 | private Context context;
33 | private List groups;
34 |
35 | @Before
36 | public void setUp() throws Exception {
37 | MockitoAnnotations.initMocks(this);
38 | Application application = RuntimeEnvironment.application;
39 | assertNotNull(application);
40 |
41 | context = application;
42 | groups = GenreDataFactory.makeGenres();
43 | }
44 |
45 | @Test
46 | public void test_onCreateViewHolder() {
47 | GenreAdapter adapter = new GenreAdapter(groups);
48 | LinearLayout parent = new LinearLayout(context);
49 |
50 | //child view holder
51 | ViewHolder childViewHolder = adapter.onCreateViewHolder(parent, ExpandableListPosition.CHILD);
52 | assertTrue(childViewHolder instanceof ArtistViewHolder);
53 |
54 | //group view holder
55 | ViewHolder groupViewHolder = adapter.onCreateViewHolder(parent, ExpandableListPosition.GROUP);
56 | assertTrue(groupViewHolder instanceof GenreViewHolder);
57 | }
58 |
59 | @Test
60 | public void test_getItemCount() {
61 | GenreAdapter adapter = new GenreAdapter(groups);
62 |
63 | //initial state
64 | int initialExpected = 5;
65 | int initialActual = adapter.getItemCount();
66 |
67 | assertEquals(initialExpected, initialActual);
68 | }
69 |
70 | @Test
71 | public void test_getItemViewType() {
72 | GenreAdapter adapter = new GenreAdapter(groups);
73 |
74 | //initial state
75 | int initialExpected = ExpandableListPosition.GROUP;
76 | int initialActual = adapter.getItemViewType(3);
77 |
78 | assertEquals(initialExpected, initialActual);
79 | }
80 | }
--------------------------------------------------------------------------------
/sample/src/test/java/com/thoughtbot/expandablerecyclerview/sample/MultiTypeGenreAdapterTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtbot.expandablerecyclerview.sample;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import com.thoughtbot.expandablerecyclerview.models.ExpandableListPosition;
6 | import com.thoughtbot.expandablerecyclerview.sample.multitype.MultiTypeGenreAdapter;
7 | import java.util.List;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.mockito.MockitoAnnotations;
12 | import org.robolectric.RobolectricTestRunner;
13 | import org.robolectric.RuntimeEnvironment;
14 | import org.robolectric.annotation.Config;
15 |
16 | import static junit.framework.Assert.assertEquals;
17 | import static junit.framework.Assert.assertFalse;
18 | import static junit.framework.Assert.assertNotNull;
19 | import static junit.framework.Assert.assertTrue;
20 |
21 | /**
22 | * Unit test for MultiTypeGenreAdapter
23 | */
24 |
25 | @RunWith(RobolectricTestRunner.class)
26 | @Config(constants = BuildConfig.class, sdk = 21)
27 | public class MultiTypeGenreAdapterTest {
28 |
29 | private Context context;
30 | private List groups;
31 |
32 | @Before
33 | public void setUp() throws Exception {
34 | MockitoAnnotations.initMocks(this);
35 | Application application = RuntimeEnvironment.application;
36 | assertNotNull(application);
37 |
38 | context = application;
39 | groups = GenreDataFactory.makeGenres();
40 | }
41 |
42 | @Test
43 | public void test_getItemViewType() {
44 | MultiTypeGenreAdapter adapter = new MultiTypeGenreAdapter(groups);
45 |
46 | //initial state
47 | int initialExpected = ExpandableListPosition.GROUP;
48 | int initialActual = adapter.getItemViewType(3);
49 |
50 | assertEquals(initialExpected, initialActual);
51 |
52 | //expand first group
53 | adapter.toggleGroup(0);
54 | int newExpected = MultiTypeGenreAdapter.ARTIST_VIEW_TYPE;
55 | int newActual = adapter.getItemViewType(3);
56 |
57 | assertEquals(newExpected, newActual);
58 | }
59 |
60 | @Test
61 | public void test_isChild() {
62 | MultiTypeGenreAdapter adapter = new MultiTypeGenreAdapter(groups);
63 |
64 | int validChildViewType = MultiTypeGenreAdapter.ARTIST_VIEW_TYPE;
65 | int inValidChildViewType = ExpandableListPosition.GROUP;
66 |
67 | assertTrue(adapter.isChild(validChildViewType));
68 | assertFalse(adapter.isChild(inValidChildViewType));
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':sample', ':expandablerecyclerview', ':expandablecheckrecyclerview'
2 |
--------------------------------------------------------------------------------