├── .github ├── CONTRIBUTING.md └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── RELEASING.md ├── build.gradle ├── checkstyle.xml ├── findbugs.xml ├── gradle.properties ├── gradle ├── gradle-mvn-push.gradle ├── publish-docs.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── image ├── sample-one2many.png ├── screenshot-bilibili.png ├── screenshot-multigrid.png └── screenshot-normal.png ├── library ├── .gitignore ├── build.gradle ├── gradle.properties ├── module.md └── src │ ├── main │ ├── AndroidManifest.xml │ ├── kotlin │ │ └── com │ │ │ └── drakeet │ │ │ └── multitype │ │ │ ├── ClassLinkerBridge.kt │ │ │ ├── DefaultLinker.kt │ │ │ ├── DelegateNotFoundException.kt │ │ │ ├── ItemViewBinder.kt │ │ │ ├── ItemViewDelegate.kt │ │ │ ├── JavaClassLinker.kt │ │ │ ├── KotlinClassLinker.kt │ │ │ ├── Linker.kt │ │ │ ├── MultiTypeAdapter.kt │ │ │ ├── MutableTypes.kt │ │ │ ├── OneToManyBuilder.kt │ │ │ ├── OneToManyEndpoint.kt │ │ │ ├── OneToManyFlow.kt │ │ │ ├── Type.kt │ │ │ ├── Types.kt │ │ │ ├── ViewDelegate.kt │ │ │ ├── ViewHolderDelegate.kt │ │ │ └── ViewHolderInflater.kt │ └── res │ │ └── values │ │ └── ids.xml │ └── test │ └── kotlin │ └── com │ └── drakeet │ └── multitype │ ├── ItemViewDelegateTest.kt │ ├── MultiTypeAdapterTest.kt │ ├── MultiTypeTest.kt │ ├── MutableTypesTest.kt │ ├── OneToManyBuilderTest.kt │ ├── StringViewDelegate.kt │ ├── TestItem.kt │ └── TestItemViewDelegate.kt ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── kotlin │ │ └── com │ │ └── drakeet │ │ └── multitype │ │ └── sample │ │ ├── RecyclerViewMatcher.kt │ │ ├── SmokeTest.kt │ │ ├── bilibili │ │ └── BilibiliActivityDataSetADCTest.kt │ │ └── one2many │ │ ├── DuplicateTypesTest.kt │ │ └── OneToManyTest.kt │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── drakeet │ │ └── multitype │ │ └── sample │ │ ├── MenuBaseActivity.kt │ │ ├── Numbers.kt │ │ ├── Savable.kt │ │ ├── Test.java │ │ ├── bilibili │ │ ├── BilibiliActivity.kt │ │ ├── HorizontalPostsHolderInflater.kt │ │ ├── Post.kt │ │ ├── PostItemDecoration.kt │ │ ├── PostList.kt │ │ ├── PostViewBinder.kt │ │ └── PostsAdapter.kt │ │ ├── common │ │ ├── Category.kt │ │ └── CategoryHolderInflater.kt │ │ ├── communication │ │ ├── CommunicateWithBinderActivity.kt │ │ └── TextItemWithOutsizeDataViewBinder.kt │ │ ├── more │ │ └── MoreApisPlayground.kt │ │ ├── normal │ │ ├── ImageItem.kt │ │ ├── ImageItemViewBinder.kt │ │ ├── NormalActivity.kt │ │ ├── RichItem.kt │ │ ├── RichView.kt │ │ ├── RichViewDelegate.kt │ │ ├── TextItem.kt │ │ └── TextItemViewBinder.kt │ │ ├── one2many │ │ ├── Data.kt │ │ ├── DataType1ViewBinder.kt │ │ ├── DataType2ViewBinder.kt │ │ └── OneDataToManyActivity.kt │ │ ├── payload │ │ ├── HeavyItem.kt │ │ ├── HeavyItemViewBinder.kt │ │ └── TestPayloadActivity.kt │ │ ├── selectable │ │ ├── MultiSelectableActivity.kt │ │ ├── Square.kt │ │ └── SquareViewBinder.kt │ │ └── weibo │ │ ├── ContentHolder.kt │ │ ├── User.kt │ │ ├── Weibo.kt │ │ ├── WeiboActivity.kt │ │ ├── WeiboContent.kt │ │ ├── WeiboContentDeserializer.kt │ │ ├── WeiboFrameBinder.kt │ │ ├── WeiboJsonParser.kt │ │ └── content │ │ ├── SimpleImage.kt │ │ ├── SimpleImageViewBinder.kt │ │ ├── SimpleText.kt │ │ └── SimpleTextViewBinder.kt │ └── res │ ├── color │ └── square_number.xml │ ├── drawable-xxxhdpi │ ├── ic_fab_done.png │ ├── ic_right.png │ ├── img_00.webp │ ├── img_01.webp │ ├── img_10.webp │ └── img_11.jpg │ ├── drawable │ ├── avatar_drakeet.jpg │ ├── card.xml │ ├── shadow.xml │ ├── square_background.xml │ └── square_border.xml │ ├── layout │ ├── activity_list.xml │ ├── activity_more_apis_playground.xml │ ├── activity_multi_selectable.xml │ ├── item_category.xml │ ├── item_data_type1.xml │ ├── item_data_type2.xml │ ├── item_heavy.xml │ ├── item_horizontal_list.xml │ ├── item_horizontal_post.xml │ ├── item_image.xml │ ├── item_inflated_text.xml │ ├── item_post.xml │ ├── item_square.xml │ ├── item_text.xml │ ├── item_weibo_frame.xml │ ├── item_weibo_simple_image.xml │ └── item_weibo_simple_text.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | If you would like to contribute code to MultiType you can do so through GitHub by 4 | forking the repository and sending a pull request. 5 | 6 | When submitting code, please make every effort to follow existing conventions 7 | and style in order to keep the code as readable as possible. Please also make 8 | sure your code compiles by running `./gradlew clean build` and Checkstyle with 9 | `/checkstyle.xml`. 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | What kind of issue is this? 2 | 3 | - [ ] Question. This issue tracker is not the place for questions. If you want to ask how to do 4 | something, or to understand why something isn't working the way you expect it to, use Stack 5 | Overflow. https://stackoverflow.com/questions/tagged/MultiType 6 | 7 | - [ ] Bug report. If you’ve found a bug, spend the time to write a failing test. Please provide 8 |       your device name, device OS version, and describe the reproduction steps. 9 | 10 | - [ ] Feature Request. Start by telling us what problem you’re trying to solve. Often a solution 11 | already exists! Don’t send pull requests to implement new features without first getting our 12 | support. Sometimes we leave features out on purpose to keep the project small. 13 | 14 | ###### Info: 15 | 16 | - MultiType version: 17 | - Device OS version: 18 | - Device Name: 19 | 20 | ###### Description: 21 | 22 | ###### Reproduction Steps: 23 | 24 | ###### My thoughts: 25 | 26 | ###### What did I do: 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.ap_ 3 | 4 | # files for the dex VM 5 | *.dex 6 | 7 | # Java class files 8 | *.class 9 | 10 | # generated files 11 | bin/ 12 | gen/ 13 | 14 | # Local configuration file (sdk path, etc) 15 | local.properties 16 | 17 | # Proguard folder generated by Eclipse 18 | proguard/ 19 | 20 | # Ignore gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Eclipse project files 25 | .classpath 26 | .project 27 | .settings/ 28 | 29 | # Intellij project files 30 | *.iml 31 | *.ipr 32 | *.iws 33 | .idea/ 34 | 35 | # Mac system files 36 | .DS_Store 37 | 38 | *.keystore -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | 4 | env: 5 | global: 6 | - ANDROID_ABI=armeabi-v7a 7 | 8 | android: 9 | components: 10 | - tools 11 | - android-22 12 | - android-28 13 | - sys-img-armeabi-v7a-android-22 14 | - extra-android-m2repository 15 | - extra-android-support 16 | - extra 17 | licenses: 18 | - android-sdk-license-.+ 19 | 20 | before_install: 21 | # Install SDK license so Android Gradle plugin can install deps. 22 | - mkdir "$ANDROID_HOME/licenses" || true 23 | - echo "d56f5187479451eabf01fb78af6dfcb131a6481e" > "$ANDROID_HOME/licenses/android-sdk-license" 24 | - echo "24333f8a63b6825ea9c5514f83c2829b004d1fee" >> "$ANDROID_HOME/licenses/android-sdk-license" 25 | 26 | before_script: 27 | - chmod +x gradlew 28 | - echo no | android create avd --force -n test -t android-22 --abi $ANDROID_ABI 29 | - emulator -avd test -no-window & 30 | - android-wait-for-emulator 31 | - adb shell input keyevent 82 & 32 | 33 | script: 34 | - ./gradlew clean connectedAndroidTest 35 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # MultiType Releases 2 | 3 | ### Version 4.2.0 - Dec 24, 2019 4 | 5 | - Add `ViewDelegate` that does not require `ViewHolder` (#296) 6 | 7 | ### Version 4.1.1 - Dec 11, 2019 8 | 9 | - Fix binary incompatibility about `ItemViewBinder` (#294) 10 | 11 | ### Version 4.1.0 - Dec 9, 2019 12 | 13 | - Add `ItemViewDelegate` & `onCreateViewHolder(context, _)` (#292) 14 | - Kotlin 1.3.61 15 | 16 | ### Version 4.0.0 - Sep 7, 2019 17 | 18 | - Migrate to com.drakeet group (#267) 19 | - Kotlin 1.3.50 20 | - AndroidX Annotation 1.1.0 21 | - Fix duplicate library_release.kotlin_module (#284) 22 | 23 | ### Version 4.0.0-alpha3 - Mar 16, 2019 24 | 25 | - Change `TypePool` to `Types` 26 | - Rename `ArrayTypePool` to `MutableTypes` 27 | - Open `MultiTypeAdapter` & `MutableTypes` 28 | 29 | ### Version 4.0.0-alpha2 - Feb 06, 2019 30 | 31 | This migrates MultiType to Kotlin ([#253](https://github.com/drakeet/MultiType/pull/253)) 32 | 33 | #### Features 34 | 35 | - Add a new reified `MultiTypeAdapter#register(ItemViewBinder)` 36 | - Add a new class `Type` for `TypePool` to hold data 37 | - Add `withKotlinClassLinker` for `OneToManyEndpoint` 38 | - Add `ItemViewBinder#adapterItems` to get or set the items of the associated `MultiTypeAdapter` 39 | - Change `MultiTypeAdapter#items` from `List<*>` to `List` 40 | 41 | #### Breaking Changes 42 | 43 | - Change all protected methods of `ItemViewBinder` to public ([#245](https://github.com/drakeet/MultiType/issues/245)) 44 | - Change the `payloads` parameter of `ItemViewBinder#onBindViewHolder(holder, item, payloads)` to be of `List` type 45 | - Change the `clazz` parameter of `MultiTypeAdapter#register(...)` from `Class` to `Class` 46 | - ~~Change `MultiTypeAdapter` to `final`~~ 47 | - Remove `Items` class 48 | - Remove `Preconditions` class 49 | - Rename `MultiTypePool` to `ArrayTypePool` 50 | - Rename `KClassLinker` to `KotlinClassLinker` 51 | - Rename `ClassLinker` to `JavaClassLinker` 52 | - Rename `OneToManyEndpoint#withKClassLinker(...)` method to `withKotlinClassLinker` 53 | - Rename `OneToManyEndpoint#withClassLinker(...)` method to `withJavaClassLinker` 54 | 55 | ### Version 3.5.0 - Sep 22, 2018 56 | 57 | - Migrate to AndroidX 58 | - Rename Kotlin extension artifact to -ktx 59 | 60 | ### Version 3.4.4 - Feb 17, 2018 61 | 62 | Some minor changes: 63 | - Disable `BuildConfig` generation 64 | - Change some dependencies of libraries from `implementation` to `api` 65 | - Improve the tip of `BinderNotFoundException` 66 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to MultiType 2 | 3 | If you would like to contribute code you can do so through GitHub by forking the repository and sending a pull request. 4 | 5 | When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible. 6 | 7 | ## License 8 | 9 | By contributing your code, you agree to license your contribution under the terms of the APLv2: https://github.com/drakeet/MultiType/blob/4.x/LICENSE 10 | 11 | All files are released with the Apache 2.0 license. 12 | 13 | If you are adding a new file it should have a header like this: 14 | 15 | ``` 16 | /* 17 | * Licensed under the Apache License, Version 2.0 (the "License"); 18 | * you may not use this file except in compliance with the License. 19 | * You may obtain a copy of the License at 20 | * 21 | * http://www.apache.org/licenses/LICENSE-2.0 22 | * 23 | * Unless required by applicable law or agreed to in writing, software 24 | * distributed under the License is distributed on an "AS IS" BASIS, 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 | * See the License for the specific language governing permissions and 27 | * limitations under the License. 28 | */ 29 | ``` 30 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | Releasing 2 | ========= 3 | 4 | 1. Change the version in `gradle.properties` to a non-SNAPSHOT version. 5 | 2. Update the `CHANGELOG.md` for the impending release 6 | 3. Update the `README.md` with the new version. 7 | 4. `git commit -am "Prepare for release X.Y.Z"` (where X.Y.Z is the new version) 8 | 5. `git tag -a X.Y.Z -m "Version X.Y.Z"` (where X.Y.Z is the new version) 9 | 6. `./gradlew clean uploadArchives` 10 | 7. Update the `gradle.properties` to the next SNAPSHOT version. 11 | 8. `git commit -am "Prepare next development version"` 12 | 9. `git push && git push --tags` 13 | 10. Visit [Sonatype Nexus](https://oss.sonatype.org/) and promote the artifact. 14 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | buildscript { 18 | ext.kotlinVersion = '1.4.32' 19 | ext.dokkaVersion = '0.9.17' 20 | ext.recyclerviewVersion = '1.2.1' 21 | ext.annotationVersion = '1.2.0' 22 | ext.appcompatVersion = '1.3.0' 23 | ext.buildConfig = [ 24 | 'versionCode': 430, 25 | 'versionName': "4.3.0", 26 | 'compileSdkVersion': 30, 27 | 'minSdkVersion': 14, 28 | 'targetSdkVersion': 30 29 | ] 30 | 31 | repositories { 32 | google() 33 | mavenCentral() 34 | } 35 | dependencies { 36 | classpath 'com.android.tools.build:gradle:4.2.1' 37 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" 38 | classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:${dokkaVersion}" 39 | } 40 | } 41 | 42 | allprojects { 43 | repositories { 44 | google() 45 | mavenCentral() 46 | } 47 | task javadoc(type: Javadoc) { 48 | options.encoding = "utf-8" 49 | } 50 | } 51 | 52 | task clean(type: Delete) { 53 | delete rootProject.buildDir 54 | } 55 | -------------------------------------------------------------------------------- /findbugs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2016-present. Drakeet Xu 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | # Project-wide Gradle settings. 18 | 19 | # IDE (e.g. Android Studio) users: 20 | # Gradle settings configured through the IDE *will override* 21 | # any settings specified in this file. 22 | 23 | # For more details on how to configure your build environment visit 24 | # http://www.gradle.org/docs/current/userguide/build_environment.html 25 | 26 | # Specifies the JVM arguments used for the daemon process. 27 | # The setting is particularly useful for tweaking memory settings. 28 | android.useAndroidX=true 29 | org.gradle.jvmargs=-Xmx4096m 30 | 31 | # When configured, Gradle will run in incubating parallel mode. 32 | # This option should only be used with decoupled projects. More details, visit 33 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 34 | # org.gradle.parallel=true 35 | -------------------------------------------------------------------------------- /gradle/publish-docs.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018. Uber Technologies 3 | * Copyright (c) 2018 Drakeet Xu 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | configurations { 19 | osstrich 20 | } 21 | 22 | dependencies { 23 | osstrich 'com.squareup.osstrich:osstrich:1.3.0' 24 | } 25 | 26 | task publishDocs(type: JavaExec) { 27 | classpath = configurations.osstrich 28 | main = 'com.squareup.osstrich.JavadocPublisher' 29 | args = ['build/javadoc', 30 | 'https://github.com/drakeet/MultiType', 31 | 'com.drakeet.multitype'] 32 | } 33 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakeet/MultiType/84cd8fad89b0c6df661f32afae3b090cbf0cb0f5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /image/sample-one2many.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakeet/MultiType/84cd8fad89b0c6df661f32afae3b090cbf0cb0f5/image/sample-one2many.png -------------------------------------------------------------------------------- /image/screenshot-bilibili.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakeet/MultiType/84cd8fad89b0c6df661f32afae3b090cbf0cb0f5/image/screenshot-bilibili.png -------------------------------------------------------------------------------- /image/screenshot-multigrid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakeet/MultiType/84cd8fad89b0c6df661f32afae3b090cbf0cb0f5/image/screenshot-multigrid.png -------------------------------------------------------------------------------- /image/screenshot-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakeet/MultiType/84cd8fad89b0c6df661f32afae3b090cbf0cb0f5/image/screenshot-normal.png -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | apply plugin: 'com.android.library' 18 | apply plugin: 'kotlin-android' 19 | apply plugin: 'org.jetbrains.dokka-android' 20 | apply plugin: 'jacoco' 21 | apply from: rootProject.file('gradle/publish-docs.gradle') 22 | apply from: rootProject.file('gradle/gradle-mvn-push.gradle') 23 | 24 | android { 25 | compileSdkVersion buildConfig.compileSdkVersion 26 | 27 | defaultConfig { 28 | minSdkVersion buildConfig.minSdkVersion 29 | } 30 | 31 | compileOptions { 32 | kotlinOptions.freeCompilerArgs += ['-module-name', "multitype"] 33 | } 34 | 35 | // TODO replace with https://issuetracker.google.com/issues/72050365 once released. 36 | libraryVariants.all { variant -> 37 | variant.generateBuildConfigProvider.configure { 38 | it.enabled = false 39 | } 40 | } 41 | 42 | testOptions { 43 | unitTests { 44 | includeAndroidResources = true 45 | } 46 | } 47 | 48 | sourceSets { 49 | main.java.srcDirs += 'src/main/kotlin' 50 | test.java.srcDirs += 'src/test/kotlin' 51 | } 52 | } 53 | 54 | dependencies { 55 | testImplementation 'junit:junit:4.13' 56 | testImplementation "org.robolectric:robolectric:4.3.1" 57 | testImplementation "org.mockito:mockito-inline:2.18.0" 58 | testImplementation "androidx.recyclerview:recyclerview:$recyclerviewVersion" 59 | testImplementation "com.google.truth:truth:1.0" 60 | testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0" 61 | 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" 63 | api "androidx.annotation:annotation:$annotationVersion" 64 | api "androidx.recyclerview:recyclerview:$recyclerviewVersion" 65 | } 66 | -------------------------------------------------------------------------------- /library/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2016-present. Drakeet Xu 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | POM_NAME=MultiType 18 | POM_ARTIFACT_ID=multitype 19 | POM_PACKAGING=aar 20 | 21 | GROUP=com.drakeet.multitype 22 | 23 | POM_DESCRIPTION=Easier and more flexible to create multiple types for Android RecyclerView. 24 | POM_URL=https://github.com/drakeet/MultiType 25 | POM_SCM_URL=https://github.com/drakeet/MultiType 26 | POM_SCM_CONNECTION=scm:git@github.com:drakeet/MultiType.git 27 | POM_SCM_DEV_CONNECTION=scm:git@github.com:drakeet/MultiType.git 28 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 29 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 30 | POM_LICENCE_DIST=repo 31 | POM_DEVELOPER_ID=drakeet 32 | POM_DEVELOPER_NAME=Drakeet Xu 33 | POM_DEVELOPER_URL=https://drakeet.com 34 | -------------------------------------------------------------------------------- /library/module.md: -------------------------------------------------------------------------------- 1 | # Module multitype 2 | 3 | An Android library to create multiple item types list views easily and flexibly 4 | 5 | # Package me.drakeet.multitype 6 | 7 | An Android library to create multiple item types list views easily and flexibly 8 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/ClassLinkerBridge.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | internal class ClassLinkerBridge private constructor( 23 | private val javaClassLinker: JavaClassLinker, 24 | private val delegates: Array> 25 | ) : Linker { 26 | 27 | override fun index(position: Int, item: T): Int { 28 | val indexedClass = javaClassLinker.index(position, item) 29 | val index = delegates.indexOfFirst { it.javaClass == indexedClass } 30 | if (index != -1) return index 31 | throw IndexOutOfBoundsException( 32 | "The delegates'(${delegates.contentToString()}) you registered do not contain this ${indexedClass.name}." 33 | ) 34 | } 35 | 36 | companion object { 37 | fun toLinker( 38 | javaClassLinker: JavaClassLinker, 39 | delegates: Array> 40 | ): Linker { 41 | return ClassLinkerBridge(javaClassLinker, delegates) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/DefaultLinker.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | internal class DefaultLinker : Linker { 23 | override fun index(position: Int, item: T): Int = 0 24 | } 25 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/DelegateNotFoundException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | internal class DelegateNotFoundException(clazz: Class<*>) : RuntimeException( 23 | "Have you registered the ${clazz.name} type and its delegate or binder?" 24 | ) 25 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/ItemViewBinder.kt: -------------------------------------------------------------------------------- 1 | package com.drakeet.multitype 2 | 3 | import android.content.Context 4 | import android.view.LayoutInflater 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.RecyclerView 7 | 8 | /** 9 | * This is a compatible version of [ItemViewDelegate]. 10 | * @see ItemViewDelegate 11 | * @author Drakeet Xu 12 | */ 13 | abstract class ItemViewBinder : ItemViewDelegate() { 14 | 15 | final override fun onCreateViewHolder(context: Context, parent: ViewGroup): VH { 16 | return onCreateViewHolder(LayoutInflater.from(context), parent) 17 | } 18 | 19 | abstract fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): VH 20 | } 21 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/JavaClassLinker.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | /** 20 | * An interface to link the items and delegates by the classes of delegates. 21 | * 22 | * @author Drakeet Xu 23 | */ 24 | interface JavaClassLinker { 25 | 26 | /** 27 | * Returns the class of your registered delegates for your item. 28 | * 29 | * @param position The position in items 30 | * @param item The item 31 | * @return The index of your registered delegates 32 | * @see OneToManyEndpoint.withJavaClassLinker 33 | */ 34 | fun index(position: Int, item: T): Class> 35 | } 36 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/KotlinClassLinker.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | import kotlin.reflect.KClass 20 | 21 | /** 22 | * An interface to link the items and delegates by the classes of delegates. 23 | * 24 | * @author Drakeet Xu 25 | */ 26 | interface KotlinClassLinker { 27 | 28 | /** 29 | * Returns the class of your registered delegates for your item. 30 | * 31 | * @param position The position in items 32 | * @param item The item 33 | * @return The index of your registered delegates 34 | * @see OneToManyEndpoint.withJavaClassLinker 35 | */ 36 | fun index(position: Int, item: T): KClass> 37 | } 38 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/Linker.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | import androidx.annotation.IntRange 20 | 21 | /** 22 | * An interface to link the items and delegates by the array index. 23 | * 24 | * @author Drakeet Xu 25 | */ 26 | interface Linker { 27 | 28 | /** 29 | * Returns the index of your registered delegates for your item. The result should be in range of 30 | * `[0, one-to-multiple-delegates.length)`. 31 | * 32 | * Note: The argument of [OneToManyFlow.to] is the 33 | * one-to-multiple-delegates. 34 | * 35 | * @param position The position in items 36 | * @param item The data item 37 | * @return The index of your registered delegates 38 | * @see OneToManyFlow.to 39 | * @see OneToManyEndpoint.withLinker 40 | */ 41 | @IntRange(from = 0) 42 | fun index(position: Int, item: T): Int 43 | } 44 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/MutableTypes.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | /** 20 | * A TypePool implemented by an ArrayList. 21 | * 22 | * @author Drakeet Xu 23 | */ 24 | open class MutableTypes constructor( 25 | open val initialCapacity: Int = 0, 26 | open val types: MutableList> = ArrayList(initialCapacity) 27 | ) : Types { 28 | 29 | override val size: Int get() = types.size 30 | 31 | override fun register(type: Type) { 32 | types.add(type) 33 | } 34 | 35 | override fun unregister(clazz: Class<*>): Boolean { 36 | return types.removeAll { it.clazz == clazz } 37 | } 38 | 39 | @Suppress("UNCHECKED_CAST") 40 | override fun getType(index: Int): Type = types[index] as Type 41 | 42 | override fun firstIndexOf(clazz: Class<*>): Int { 43 | val index = types.indexOfFirst { it.clazz == clazz } 44 | if (index != -1) { 45 | return index 46 | } 47 | return types.indexOfFirst { it.clazz.isAssignableFrom(clazz) } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/OneToManyBuilder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | import androidx.annotation.CheckResult 20 | 21 | /** 22 | * @author Drakeet Xu 23 | */ 24 | internal class OneToManyBuilder( 25 | private val adapter: MultiTypeAdapter, 26 | private val clazz: Class 27 | ) : OneToManyFlow, OneToManyEndpoint { 28 | 29 | private var delegates: Array>? = null 30 | 31 | @SafeVarargs 32 | @CheckResult(suggest = "#withLinker(Linker)") 33 | override fun to(vararg delegates: ItemViewDelegate) = apply { 34 | @Suppress("UNCHECKED_CAST") 35 | this.delegates = delegates as Array> 36 | } 37 | 38 | @SafeVarargs 39 | @CheckResult(suggest = "#withLinker(Linker)") 40 | override fun to(vararg binders: ItemViewBinder) = apply { 41 | @Suppress("UNCHECKED_CAST") 42 | this.delegates = binders as Array> 43 | } 44 | 45 | override fun withLinker(linker: Linker) { 46 | doRegister(linker) 47 | } 48 | 49 | override fun withJavaClassLinker(javaClassLinker: JavaClassLinker) { 50 | withLinker(ClassLinkerBridge.toLinker(javaClassLinker, delegates!!)) 51 | } 52 | 53 | private fun doRegister(linker: Linker) { 54 | for (delegate in delegates!!) { 55 | adapter.register(Type(clazz, delegate, linker)) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/OneToManyEndpoint.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | import kotlin.reflect.KClass 20 | 21 | /** 22 | * End-operators for one-to-many. 23 | * 24 | * @author Drakeet Xu 25 | */ 26 | interface OneToManyEndpoint { 27 | 28 | /** 29 | * Sets a linker to link the items and delegates by array index. 30 | * 31 | * @param linker the row linker 32 | * @see Linker 33 | */ 34 | fun withLinker(linker: Linker) 35 | 36 | fun withLinker(linker: (position: Int, item: T) -> Int) { 37 | withLinker(object : Linker { 38 | override fun index(position: Int, item: T): Int { 39 | return linker(position, item) 40 | } 41 | }) 42 | } 43 | 44 | /** 45 | * Sets a class linker to link the items and delegates by the class instance of delegates. 46 | * 47 | * @param javaClassLinker the class linker 48 | * @see JavaClassLinker 49 | */ 50 | fun withJavaClassLinker(javaClassLinker: JavaClassLinker) 51 | 52 | private fun withJavaClassLinker(classLinker: (position: Int, item: T) -> Class>) { 53 | withJavaClassLinker(object : JavaClassLinker { 54 | override fun index(position: Int, item: T): Class> { 55 | return classLinker(position, item) 56 | } 57 | }) 58 | } 59 | 60 | fun withKotlinClassLinker(classLinker: KotlinClassLinker) { 61 | withJavaClassLinker { position, item -> classLinker.index(position, item).java } 62 | } 63 | 64 | fun withKotlinClassLinker(classLinker: (position: Int, item: T) -> KClass>) { 65 | withJavaClassLinker { position, item -> classLinker(position, item).java } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/OneToManyFlow.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | import androidx.annotation.CheckResult 20 | 21 | /** 22 | * Process and flow operators for one-to-many. 23 | * 24 | * @author Drakeet Xu 25 | */ 26 | interface OneToManyFlow { 27 | 28 | /** 29 | * Sets some item view delegates to the item type. 30 | * 31 | * @param delegates the item view delegates 32 | * @return end flow operator 33 | */ 34 | @CheckResult 35 | fun to(vararg delegates: ItemViewDelegate): OneToManyEndpoint 36 | 37 | @CheckResult 38 | fun to(vararg delegates: ItemViewBinder): OneToManyEndpoint 39 | } 40 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/Type.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | data class Type( 23 | val clazz: Class, 24 | val delegate: ItemViewDelegate, 25 | val linker: Linker 26 | ) 27 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/Types.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | /** 20 | * An ordered collection to hold the types, delegates and linkers. 21 | * 22 | * @author Drakeet Xu 23 | */ 24 | interface Types { 25 | 26 | /** 27 | * Returns the size of the [Types]. 28 | */ 29 | val size: Int 30 | 31 | fun register(type: Type) 32 | 33 | /** 34 | * Unregister all types indexed by the specified class. 35 | * 36 | * @param clazz the main class of a [Type] 37 | * @return true if any types are unregistered from this [Types] 38 | */ 39 | fun unregister(clazz: Class<*>): Boolean 40 | 41 | /** 42 | * Gets the type at the specified index. 43 | * 44 | * @param index the type index 45 | * @return the [Type] at the specified index 46 | * @throws IndexOutOfBoundsException if the index is out of range 47 | */ 48 | fun getType(index: Int): Type 49 | 50 | /** 51 | * For getting index of the type class. If the subclass is already registered, 52 | * the registered mapping is used. If the subclass is not registered, then look 53 | * for its parent class if is registered, if the parent class is registered, 54 | * the subclass is regarded as the parent class. 55 | * 56 | * @param clazz the type class. 57 | * @return The index of the first occurrence of the specified class in this [Types], 58 | * or -1 if this [Types] does not contain the class. 59 | */ 60 | fun firstIndexOf(clazz: Class<*>): Int 61 | } 62 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/ViewDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.drakeet.multitype 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.RecyclerView 7 | 8 | /** 9 | * This is a simple [ItemViewDelegate] that does not require to declare and provide a [RecyclerView.ViewHolder]. 10 | * @since v4.2.0 11 | * @author Drakeet Xu 12 | */ 13 | abstract class ViewDelegate : ItemViewDelegate>() { 14 | 15 | protected val View.holder: Holder get() { 16 | return holder(this) ?: throw IllegalAccessException("The view holder property can only be called after onCreateView()!") 17 | } 18 | 19 | protected val View.layoutPosition: Int get() = holder.layoutPosition 20 | 21 | protected val View.absoluteAdapterPosition: Int get() = holder.absoluteAdapterPosition 22 | 23 | protected val View.bindingAdapterPosition: Int get() = holder.bindingAdapterPosition 24 | 25 | abstract fun onCreateView(context: Context): V 26 | 27 | abstract fun onBindView(view: V, item: T) 28 | 29 | // Override this function if you need a parent ViewGroup 30 | open fun onCreateView(context: Context, parent: ViewGroup): V { 31 | return onCreateView(context) 32 | } 33 | 34 | // Override this function if you need a ViewHolder or positions 35 | open fun onBindView(holder: Holder, view: V, item: T) { 36 | view.setTag(R.id.tagViewHolder, holder) 37 | onBindView(view, item) 38 | } 39 | 40 | protected fun getRecyclerLayoutParams(view: View): RecyclerView.LayoutParams { 41 | return (view.layoutParams as RecyclerView.LayoutParams) 42 | } 43 | 44 | private fun holder(view: View): Holder? { 45 | @Suppress("UNCHECKED_CAST") 46 | return view.getTag(R.id.tagViewHolder) as? Holder 47 | } 48 | 49 | override fun onCreateViewHolder(context: Context, parent: ViewGroup): Holder { 50 | return Holder(onCreateView(context, parent)) 51 | } 52 | 53 | override fun onBindViewHolder(holder: Holder, item: T) = onBindView(holder, holder.view, item) 54 | 55 | class Holder(val view: V) : RecyclerView.ViewHolder(view) 56 | } 57 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/ViewHolderDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.drakeet.multitype 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | 5 | /** 6 | * @author Drakeet Xu 7 | */ 8 | abstract class ViewHolderDelegate: ItemViewDelegate() 9 | -------------------------------------------------------------------------------- /library/src/main/kotlin/com/drakeet/multitype/ViewHolderInflater.kt: -------------------------------------------------------------------------------- 1 | package com.drakeet.multitype 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | 5 | /** 6 | * @author Drakeet Xu 7 | */ 8 | abstract class ViewHolderInflater : ItemViewBinder() 9 | -------------------------------------------------------------------------------- /library/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/drakeet/multitype/ItemViewDelegateTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | import com.google.common.truth.Truth.assertThat 20 | import org.junit.Test 21 | 22 | /** 23 | * @author Drakeet Xu 24 | */ 25 | class ItemViewDelegateTest { 26 | 27 | @Test 28 | fun shouldGetNonNullAdapter() { 29 | var exception: Exception? = null 30 | val adapter = MultiTypeAdapter() 31 | val empty = arrayListOf() 32 | adapter.items = empty 33 | 34 | val delegate = TestItemViewDelegate() 35 | adapter.register(TestItem::class.java, delegate) 36 | 37 | empty.add(TestItem("ItemViewDelegateTest")) 38 | try { 39 | delegate.notifyTestItemAdded() 40 | } catch (e: Exception) { 41 | e.printStackTrace() 42 | exception = e 43 | } 44 | 45 | assertThat(exception).isNull() 46 | } 47 | 48 | @Test(expected = IllegalStateException::class) 49 | fun shouldThrowIllegalStateException() { 50 | val adapter = MultiTypeAdapter() 51 | val empty = ArrayList() 52 | adapter.items = empty 53 | 54 | val delegate = TestItemViewDelegate() 55 | 56 | empty.add(TestItem("ItemViewDelegateTest")) 57 | delegate.notifyTestItemAdded() 58 | 59 | adapter.register(delegate) 60 | } 61 | 62 | class TestItemViewDelegate : com.drakeet.multitype.TestItemViewDelegate() { 63 | fun notifyTestItemAdded() { 64 | assertThat(adapter).isNotNull() 65 | assertThat(adapter.toString()).isNotNull() 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/drakeet/multitype/MultiTypeAdapterTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | import android.content.Context 20 | import android.view.LayoutInflater 21 | import android.view.ViewGroup 22 | import com.google.common.truth.Truth.assertThat 23 | import com.nhaarman.mockitokotlin2.* 24 | import org.junit.Assert.assertEquals 25 | import org.junit.Before 26 | import org.junit.Test 27 | import org.junit.runner.RunWith 28 | import org.mockito.ArgumentMatchers.anyList 29 | import org.mockito.ArgumentMatchers.anyString 30 | import org.robolectric.RobolectricTestRunner 31 | import java.util.* 32 | 33 | /** 34 | * @author Drakeet Xu 35 | */ 36 | @RunWith(RobolectricTestRunner::class) 37 | class MultiTypeAdapterTest { 38 | 39 | private val parent: ViewGroup = mock() 40 | private val context: Context = mock() 41 | private val mockedItemViewDelegate: TestItemViewDelegate = mock() 42 | 43 | private val itemViewDelegate = TestItemViewDelegate() 44 | 45 | @Before 46 | @Throws(Exception::class) 47 | fun setUp() { 48 | whenever(parent.context).thenReturn(context) 49 | } 50 | 51 | @Test 52 | fun shouldReturnOriginalItems() { 53 | val list = ArrayList() 54 | val adapter = MultiTypeAdapter(list) 55 | assertEquals(list, adapter.items) 56 | } 57 | 58 | @Test 59 | fun shouldReturnEmptyItemsWithDefaultConstructor() { 60 | val adapter = MultiTypeAdapter() 61 | assertThat(adapter.items).isEmpty() 62 | } 63 | 64 | @Test 65 | fun shouldOverrideRegisteredDelegate() { 66 | val adapter = MultiTypeAdapter() 67 | adapter.register(TestItem::class, itemViewDelegate) 68 | assertThat(adapter.types.size).isEqualTo(1) 69 | assertThat(itemViewDelegate).isEqualTo(adapter.types.getType(0).delegate) 70 | 71 | val newDelegate = TestItemViewDelegate() 72 | adapter.register(TestItem::class, newDelegate) 73 | assertThat(newDelegate).isEqualTo(adapter.types.getType(0).delegate) 74 | } 75 | 76 | @Test 77 | fun shouldNotOverrideRegisteredDelegateWhenToMany() { 78 | val adapter = MultiTypeAdapter() 79 | val delegate2 = TestItemViewDelegate() 80 | adapter.register(TestItem::class) 81 | .to(itemViewDelegate, delegate2) 82 | .withLinker { _, _ -> -1 } 83 | assertThat(adapter.types.getType(0).clazz).isEqualTo(TestItem::class.java) 84 | assertThat(adapter.types.getType(1).clazz).isEqualTo(TestItem::class.java) 85 | 86 | assertThat(itemViewDelegate).isEqualTo(adapter.types.getType(0).delegate) 87 | assertThat(delegate2).isEqualTo(adapter.types.getType(1).delegate) 88 | } 89 | 90 | @Test 91 | fun testOnCreateViewHolder() { 92 | val adapter = MultiTypeAdapter() 93 | adapter.register(TestItem::class, mockedItemViewDelegate) 94 | val item = TestItem("testOnCreateViewHolder") 95 | adapter.items = listOf(item) 96 | val type = adapter.getItemViewType(0) 97 | 98 | adapter.onCreateViewHolder(parent, type) 99 | verify(mockedItemViewDelegate).onCreateViewHolder(context, parent) 100 | } 101 | 102 | @Test 103 | fun testOnBindViewHolder() { 104 | val adapter = MultiTypeAdapter() 105 | adapter.register(TestItem::class, mockedItemViewDelegate) 106 | val item = TestItem("testOnCreateViewHolder") 107 | adapter.items = listOf(item) 108 | 109 | val holder: TestItemViewDelegate.ViewHolder = mock() 110 | whenever(holder.itemViewType).thenReturn(adapter.getItemViewType(0)) 111 | adapter.onBindViewHolder(holder, 0) 112 | verify(mockedItemViewDelegate).onBindViewHolder(eq(holder), eq(item), anyList()) 113 | 114 | val payloads = emptyList() 115 | adapter.onBindViewHolder(holder, 0, payloads) 116 | verify(mockedItemViewDelegate, times(2)).onBindViewHolder(holder, item, payloads) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/drakeet/multitype/MultiTypeTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | import com.google.common.truth.Truth.assertThat 20 | import org.junit.Test 21 | import org.junit.runner.RunWith 22 | import org.junit.runners.JUnit4 23 | 24 | /** 25 | * @author Drakeet Xu 26 | */ 27 | @RunWith(JUnit4::class) 28 | class MultiTypeTest { 29 | 30 | private val adapter = MultiTypeAdapter() 31 | private val simpleLinker = object : Linker { 32 | override fun index(position: Int, item: String): Int = 0 33 | } 34 | 35 | @Test 36 | fun shouldEqualToRegisteredKClass() { 37 | adapter.register(String::class, StringViewDelegate()) 38 | assertThat(adapter.types.getType(0).clazz).isEqualTo(String::class.java) 39 | } 40 | 41 | @Test 42 | fun shouldEqualToRegisteredKClass_Reified() { 43 | adapter.register(StringViewDelegate()) 44 | assertThat(adapter.types.getType(0).clazz).isEqualTo(String::class.java) 45 | } 46 | 47 | @Test 48 | fun shouldEqualToRegisteredOneToManyKClass() { 49 | adapter.register(String::class) 50 | .to(StringViewDelegate()) 51 | .withLinker(simpleLinker) 52 | assertThat(adapter.types.getType(0).clazz).isEqualTo(String::class.java) 53 | } 54 | 55 | @Test 56 | fun shouldEqualToRegisteredKClass_TypePool() { 57 | adapter.types.register(Type(String::class.java, StringViewDelegate(), simpleLinker)) 58 | assertThat(adapter.types.getType(0).clazz).isEqualTo(String::class.java) 59 | } 60 | 61 | @Test 62 | fun shouldUnregisterKClass_TypePool() { 63 | adapter.types.register(Type(String::class.java, StringViewDelegate(), simpleLinker)) 64 | assertThat(adapter.types.unregister(String::class.java)).isTrue() 65 | } 66 | 67 | @Test 68 | fun shouldEqualToRegisteredFirstKClass_TypePool() { 69 | adapter.types.register(Type(String::class.java, StringViewDelegate(), simpleLinker)) 70 | assertThat(adapter.types.firstIndexOf(String::class.java)).isEqualTo(0) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/drakeet/multitype/MutableTypesTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | import com.google.common.truth.Truth.assertThat 20 | import org.junit.Before 21 | import org.junit.Test 22 | 23 | /** 24 | * @author Drakeet Xu 25 | */ 26 | class MutableTypesTest { 27 | 28 | private lateinit var types: MutableTypes 29 | 30 | private inner class SubClass(text: String) : TestItem(text) 31 | 32 | private inner class RegisteredSubClass(text: String) : TestItem(text) 33 | 34 | @Before 35 | fun register() { 36 | types = MutableTypes() 37 | types.register(Type(TestItem::class.java, TestItemViewDelegate(), DefaultLinker())) 38 | types.register(Type(RegisteredSubClass::class.java, TestItemViewDelegate(), DefaultLinker())) 39 | } 40 | 41 | @Test 42 | fun testFirstIndexOf() { 43 | assertThat(types.firstIndexOf(TestItem::class.java)).isEqualTo(0) 44 | assertThat(types.firstIndexOf(SubClass::class.java)).isEqualTo(0) 45 | assertThat(types.firstIndexOf(RegisteredSubClass::class.java)).isEqualTo(1) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/drakeet/multitype/OneToManyBuilderTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | import androidx.recyclerview.widget.RecyclerView 20 | import com.google.common.truth.Truth.assertThat 21 | import com.nhaarman.mockitokotlin2.check 22 | import com.nhaarman.mockitokotlin2.mock 23 | import com.nhaarman.mockitokotlin2.times 24 | import com.nhaarman.mockitokotlin2.verify 25 | import org.junit.Test 26 | import kotlin.reflect.KClass 27 | 28 | /** 29 | * @author Drakeet Xu 30 | */ 31 | class OneToManyBuilderTest { 32 | 33 | private class Data 34 | 35 | private val adapter: MultiTypeAdapter = mock() 36 | 37 | private val oneToManyBuilder = OneToManyBuilder(adapter, Data::class.java) 38 | 39 | @Test 40 | fun testWithLinker() { 41 | oneToManyBuilder.to(mock(), mock()) 42 | oneToManyBuilder.withLinker(object : Linker { 43 | override fun index(position: Int, item: Data): Int { 44 | return position 45 | } 46 | }) 47 | verify(adapter, times(2)).register(check> { 48 | assertThat(it.clazz).isEqualTo(Data::class.java) 49 | assertThat(it.linker.index(1, Data())).isEqualTo(1) 50 | }) 51 | 52 | oneToManyBuilder.withLinker { position, item -> position } 53 | verify(adapter, times(4)).register(check> { 54 | assertThat(it.clazz).isEqualTo(Data::class.java) 55 | assertThat(it.linker.index(2, Data())).isEqualTo(2) 56 | }) 57 | } 58 | 59 | private abstract class DataItemViewDelegate : ItemViewDelegate() 60 | 61 | @Test 62 | fun testWithJavaClassLinker() { 63 | val itemViewDelegate1 = mock>() 64 | val itemViewDelegate2 = mock() 65 | oneToManyBuilder.to(itemViewDelegate1, itemViewDelegate2) 66 | oneToManyBuilder.withJavaClassLinker(object : JavaClassLinker { 67 | override fun index(position: Int, item: Data): Class> { 68 | return if (position == 3) itemViewDelegate1.javaClass else itemViewDelegate2.javaClass 69 | } 70 | }) 71 | verify(adapter, times(2)).register(check> { 72 | assertThat(it.clazz).isEqualTo(Data::class.java) 73 | assertThat(it.linker.index(3, Data())).isEqualTo(0) 74 | assertThat(it.linker.index(5, Data())).isEqualTo(1) 75 | }) 76 | 77 | oneToManyBuilder.withKotlinClassLinker { position, item -> 78 | if (position == 3) itemViewDelegate1::class else itemViewDelegate2::class 79 | } 80 | verify(adapter, times(4)).register(check> { 81 | assertThat(it.clazz).isEqualTo(Data::class.java) 82 | assertThat(it.linker.index(3, Data())).isEqualTo(0) 83 | assertThat(it.linker.index(5, Data())).isEqualTo(1) 84 | }) 85 | oneToManyBuilder.withKotlinClassLinker(object : KotlinClassLinker { 86 | override fun index(position: Int, item: Data): KClass> { 87 | return if (position == 3) itemViewDelegate1::class else itemViewDelegate2::class 88 | } 89 | }) 90 | verify(adapter, times(6)).register(check> { 91 | assertThat(it.clazz).isEqualTo(Data::class.java) 92 | assertThat(it.linker.index(3, Data())).isEqualTo(0) 93 | assertThat(it.linker.index(5, Data())).isEqualTo(1) 94 | }) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/drakeet/multitype/StringViewDelegate.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | import android.R 20 | import android.content.Context 21 | import android.view.LayoutInflater 22 | import android.view.View 23 | import android.view.ViewGroup 24 | import androidx.recyclerview.widget.RecyclerView 25 | 26 | /** 27 | * @author Drakeet Xu 28 | */ 29 | class StringViewDelegate : ItemViewDelegate() { 30 | 31 | override fun onCreateViewHolder(context: Context, parent: ViewGroup): ViewHolder { 32 | return ViewHolder(LayoutInflater.from(context).inflate(R.layout.test_list_item, parent, false)) 33 | } 34 | 35 | override fun onBindViewHolder(holder: ViewHolder, item: String) {} 36 | 37 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) 38 | } 39 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/drakeet/multitype/TestItem.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | open class TestItem(val text: String) 23 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/drakeet/multitype/TestItemViewDelegate.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype 18 | 19 | import android.content.Context 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import androidx.recyclerview.widget.RecyclerView 23 | 24 | /** 25 | * @author Drakeet Xu 26 | */ 27 | open class TestItemViewDelegate : ItemViewDelegate() { 28 | 29 | override fun onCreateViewHolder(context: Context, parent: ViewGroup): ViewHolder { 30 | throw NotImplementedError() 31 | } 32 | 33 | override fun onBindViewHolder(holder: ViewHolder, item: TestItem) {} 34 | 35 | inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) 36 | } 37 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | apply plugin: 'com.android.application' 18 | apply plugin: 'kotlin-android' 19 | apply plugin: 'kotlin-android-extensions' 20 | 21 | android { 22 | compileSdkVersion buildConfig.compileSdkVersion 23 | 24 | defaultConfig { 25 | applicationId "me.drakeet.multitype.sample" 26 | minSdkVersion buildConfig.minSdkVersion 27 | targetSdkVersion buildConfig.targetSdkVersion 28 | versionCode buildConfig.versionCode 29 | versionName buildConfig.versionName 30 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 31 | } 32 | 33 | buildTypes { 34 | release { 35 | minifyEnabled false 36 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 37 | } 38 | } 39 | 40 | compileOptions { 41 | sourceCompatibility JavaVersion.VERSION_1_8 42 | targetCompatibility JavaVersion.VERSION_1_8 43 | } 44 | 45 | sourceSets { 46 | main.java.srcDirs += 'src/main/kotlin' 47 | androidTest.java.srcDirs += 'src/androidTest/kotlin' 48 | } 49 | } 50 | 51 | dependencies { 52 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" 53 | implementation project(':library') 54 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 55 | androidTestImplementation 'androidx.test:rules:1.3.0' 56 | androidTestImplementation "androidx.annotation:annotation:$annotationVersion" 57 | androidTestImplementation "com.google.truth:truth:1.0" 58 | implementation "androidx.appcompat:appcompat:$appcompatVersion" 59 | implementation "androidx.recyclerview:recyclerview:$recyclerviewVersion" 60 | implementation 'com.google.code.gson:gson:2.8.6' 61 | } 62 | -------------------------------------------------------------------------------- /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/drakeet/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/kotlin/com/drakeet/multitype/sample/RecyclerViewMatcher.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample 18 | 19 | import android.content.res.Resources 20 | import android.view.View 21 | import androidx.recyclerview.widget.RecyclerView 22 | import org.hamcrest.Description 23 | import org.hamcrest.Matcher 24 | import org.hamcrest.TypeSafeMatcher 25 | 26 | /** 27 | * @author dannyroa 28 | * @author Drakeet Xu 29 | */ 30 | class RecyclerViewMatcher private constructor(private val recyclerViewId: Int) { 31 | 32 | fun atPosition(position: Int): Matcher { 33 | return atPositionOnView(position, -1) 34 | } 35 | 36 | fun atPositionOnView(position: Int, targetViewId: Int): Matcher { 37 | 38 | return object : TypeSafeMatcher() { 39 | var resources: Resources? = null 40 | var childView: View? = null 41 | 42 | override fun describeTo(description: Description) { 43 | var idDescription = Integer.toString(recyclerViewId) 44 | if (this.resources != null) { 45 | try { 46 | idDescription = this.resources!!.getResourceName(recyclerViewId) 47 | } catch (var4: Resources.NotFoundException) { 48 | idDescription = String.format( 49 | "%s (resource name not found)", 50 | recyclerViewId 51 | ) 52 | } 53 | 54 | } 55 | 56 | description.appendText("with id: $idDescription") 57 | } 58 | 59 | public override fun matchesSafely(view: View): Boolean { 60 | 61 | this.resources = view.resources 62 | 63 | if (childView == null) { 64 | val recyclerView = view.rootView.findViewById(recyclerViewId) 65 | if (recyclerView != null && recyclerView.id == recyclerViewId) { 66 | childView = recyclerView.findViewHolderForAdapterPosition( 67 | position 68 | )!!.itemView 69 | } else { 70 | return false 71 | } 72 | } 73 | 74 | if (targetViewId == -1) { 75 | return view === childView 76 | } else { 77 | val targetView = childView!!.findViewById(targetViewId) 78 | return view === targetView 79 | } 80 | } 81 | } 82 | } 83 | 84 | companion object { 85 | fun withRecyclerView(recyclerViewId: Int): RecyclerViewMatcher { 86 | return RecyclerViewMatcher(recyclerViewId) 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /sample/src/androidTest/kotlin/com/drakeet/multitype/sample/SmokeTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample 18 | 19 | import android.view.View 20 | import androidx.test.InstrumentationRegistry.getInstrumentation 21 | import androidx.test.espresso.Espresso 22 | import androidx.test.espresso.Espresso.onView 23 | import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu 24 | import androidx.test.espresso.action.ViewActions.click 25 | import androidx.test.espresso.matcher.ViewMatchers.* 26 | import androidx.test.filters.LargeTest 27 | import androidx.test.rule.ActivityTestRule 28 | import androidx.test.runner.AndroidJUnit4 29 | import com.drakeet.multitype.sample.bilibili.BilibiliActivity 30 | import org.hamcrest.Matchers.allOf 31 | import org.junit.Rule 32 | import org.junit.Test 33 | import org.junit.runner.RunWith 34 | 35 | /** 36 | * @author Drakeet Xu 37 | */ 38 | @LargeTest 39 | @RunWith(AndroidJUnit4::class) 40 | class SmokeTest { 41 | 42 | @get:Rule 43 | var rule = ActivityTestRule(BilibiliActivity::class.java) 44 | 45 | private val menus = arrayOf( 46 | "NormalActivity", 47 | "MultiSelectableActivity", 48 | "communicate with binder", 49 | "BilibiliActivity", 50 | "WeiboActivity", 51 | "OneDataToManyActivity", 52 | "TestPayloadActivity", 53 | "MoreApisPlayground" 54 | ) 55 | 56 | @Test 57 | fun smokeTest() { 58 | Espresso.closeSoftKeyboard() 59 | menus.forEach { 60 | openActionBarOverflowOrOptionsMenu(getInstrumentation().targetContext) 61 | onView(allOf(withId(R.id.title), withText(it), isDisplayed())).perform(click()) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /sample/src/androidTest/kotlin/com/drakeet/multitype/sample/bilibili/BilibiliActivityDataSetADCTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.bilibili 18 | 19 | import androidx.test.espresso.Espresso.onView 20 | import androidx.test.espresso.assertion.ViewAssertions.matches 21 | import androidx.test.espresso.matcher.ViewMatchers.withText 22 | import androidx.test.rule.ActivityTestRule 23 | import androidx.test.runner.AndroidJUnit4 24 | import com.drakeet.multitype.MultiTypeAdapter 25 | import com.drakeet.multitype.sample.R 26 | import com.drakeet.multitype.sample.RecyclerViewMatcher.Companion.withRecyclerView 27 | import com.drakeet.multitype.sample.common.Category 28 | import org.junit.Before 29 | import org.junit.Rule 30 | import org.junit.Test 31 | import org.junit.runner.RunWith 32 | 33 | /** 34 | * @author Drakeet Xu 35 | */ 36 | @RunWith(AndroidJUnit4::class) 37 | class BilibiliActivityDataSetADCTest { 38 | 39 | private val testTitle = "testTitle" 40 | 41 | private lateinit var items: MutableList 42 | private lateinit var adapter: MultiTypeAdapter 43 | 44 | @get:Rule 45 | var rule = ActivityTestRule(BilibiliActivity::class.java) 46 | 47 | @Before 48 | fun setup() { 49 | items = rule.activity.items 50 | adapter = rule.activity.adapter 51 | } 52 | 53 | @Test 54 | @Throws(Throwable::class) 55 | fun shouldNotFailWhenAddSingleTop() { 56 | val originalFirst = items[0] 57 | rule.runOnUiThread { 58 | items.add(originalFirst) 59 | adapter.notifyItemInserted(0) 60 | } 61 | } 62 | 63 | @Test 64 | @Throws(Throwable::class) 65 | fun shouldNotFailWhenAddMultiTop() { 66 | val originalFirst = items[0] 67 | rule.runOnUiThread { 68 | for (i in 0..29) { 69 | items.add(originalFirst) 70 | } 71 | adapter.notifyItemRangeInserted(0, 29) 72 | } 73 | } 74 | 75 | @Test 76 | @Throws(Throwable::class) 77 | fun shouldNotFailWhenDeleteFirst() { 78 | rule.runOnUiThread { 79 | items.removeAt(0) 80 | adapter.notifyItemRemoved(0) 81 | } 82 | } 83 | 84 | @Test 85 | @Throws(Throwable::class) 86 | fun shouldNotFailWhenDeleteEnd() { 87 | rule.runOnUiThread { 88 | val endIndex = items.size - 1 89 | items.removeAt(endIndex) 90 | adapter.notifyItemRemoved(endIndex) 91 | } 92 | } 93 | 94 | @Test 95 | @Throws(Throwable::class) 96 | fun shouldNotFailWhenDeleteAll() { 97 | rule.runOnUiThread { 98 | items.clear() 99 | adapter.notifyDataSetChanged() 100 | } 101 | } 102 | 103 | @Test 104 | @Throws(Throwable::class) 105 | fun shouldNotFailWhenChangeFirst() { 106 | rule.runOnUiThread { 107 | val category = items[0] as Category 108 | category.title = testTitle 109 | adapter.notifyItemChanged(0) 110 | } 111 | 112 | onView(withRecyclerView(R.id.list).atPositionOnView(0, R.id.title)) 113 | .check(matches(withText(testTitle))) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /sample/src/androidTest/kotlin/com/drakeet/multitype/sample/one2many/DuplicateTypesTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.one2many 18 | 19 | import androidx.test.filters.LargeTest 20 | import androidx.test.rule.ActivityTestRule 21 | import androidx.test.runner.AndroidJUnit4 22 | import com.google.common.truth.Truth.assertThat 23 | import com.drakeet.multitype.Linker 24 | import com.drakeet.multitype.MultiTypeAdapter 25 | import org.junit.Assert.assertEquals 26 | import org.junit.Assert.assertSame 27 | import org.junit.Before 28 | import org.junit.Rule 29 | import org.junit.Test 30 | import org.junit.runner.RunWith 31 | 32 | @LargeTest 33 | @RunWith(AndroidJUnit4::class) 34 | class DuplicateTypesTest { 35 | 36 | private lateinit var adapter: MultiTypeAdapter 37 | 38 | @get:Rule 39 | var rule = ActivityTestRule(OneDataToManyActivity::class.java) 40 | 41 | @Before 42 | @Throws(Throwable::class) 43 | fun setup() { 44 | adapter = rule.activity.adapter 45 | } 46 | 47 | private fun resetRecyclerViewState() { 48 | // reset and clear the recycler view types to recreate view holder 49 | rule.activity.recyclerView.adapter = adapter 50 | } 51 | 52 | @Test 53 | @Throws(Throwable::class) 54 | fun shouldOneOverrideMany() { 55 | rule.runOnUiThread { 56 | resetRecyclerViewState() 57 | adapter.register(DataType1ViewBinder()) 58 | adapter.notifyDataSetChanged() 59 | } 60 | assertThat(adapter.types.size).isEqualTo(1) 61 | assertThat(adapter.types.getType(0).clazz).isEqualTo(Data::class.java) 62 | assertThat(adapter.types.getType(0).delegate.javaClass).isEqualTo(DataType1ViewBinder::class.java) 63 | } 64 | 65 | @Test 66 | @Throws(Throwable::class) 67 | fun shouldOneOverrideOne() { 68 | rule.runOnUiThread { 69 | resetRecyclerViewState() 70 | adapter.register(DataType1ViewBinder()) 71 | adapter.register(DataType2ViewBinder()) 72 | adapter.notifyDataSetChanged() 73 | } 74 | assertThat(adapter.types.size).isEqualTo(1) 75 | assertThat(adapter.types.getType(0).clazz).isEqualTo(Data::class.java) 76 | assertThat(adapter.types.getType(0).delegate.javaClass).isEqualTo(DataType2ViewBinder::class.java) 77 | } 78 | 79 | @Test 80 | @Throws(Throwable::class) 81 | fun shouldManyOverrideOne() { 82 | val linker = object : Linker { 83 | override fun index(position: Int, item: Data): Int { 84 | return if (item.type == Data.TYPE_1) 1 else 0 85 | } 86 | } 87 | rule.runOnUiThread { 88 | resetRecyclerViewState() 89 | adapter.register(DataType1ViewBinder()) 90 | 91 | adapter.register(Data::class).to( 92 | DataType2ViewBinder(), 93 | DataType1ViewBinder() 94 | ).withLinker(linker) 95 | adapter.notifyDataSetChanged() 96 | } 97 | assertThat(adapter.types.size).isEqualTo(2) 98 | 99 | assertEquals(Data::class.java, adapter.types.getType(0).clazz) 100 | assertEquals(Data::class.java, adapter.types.getType(1).clazz) 101 | 102 | assertEquals( 103 | DataType2ViewBinder::class.java, 104 | adapter.types.getType(0).delegate::class.java 105 | ) 106 | assertEquals( 107 | DataType1ViewBinder::class.java, 108 | adapter.types.getType(1).delegate::class.java 109 | ) 110 | 111 | assertSame(linker, adapter.types.getType(0).linker) 112 | assertSame(linker, adapter.types.getType(1).linker) 113 | } 114 | 115 | @Test 116 | @Throws(Throwable::class) 117 | fun shouldManyOverrideMany() { 118 | val linker = object : Linker { 119 | override fun index(position: Int, item: Data): Int { 120 | return if (item.type == Data.TYPE_1) 1 else 0 121 | } 122 | } 123 | rule.runOnUiThread { 124 | resetRecyclerViewState() 125 | adapter.register(Data::class).to( 126 | DataType2ViewBinder(), 127 | DataType1ViewBinder() 128 | ).withLinker(linker) 129 | 130 | adapter.register(Data::class).to( 131 | DataType1ViewBinder(), 132 | DataType2ViewBinder() 133 | ).withLinker(linker) 134 | adapter.notifyDataSetChanged() 135 | } 136 | assertThat(adapter.types.size).isEqualTo(2) 137 | 138 | assertEquals(Data::class.java, adapter.types.getType(0).clazz) 139 | assertEquals(Data::class.java, adapter.types.getType(1).clazz) 140 | 141 | assertEquals( 142 | DataType1ViewBinder::class.java, 143 | adapter.types.getType(0).delegate::class.java 144 | ) 145 | assertEquals( 146 | DataType2ViewBinder::class.java, 147 | adapter.types.getType(1).delegate::class.java 148 | ) 149 | 150 | assertSame(linker, adapter.types.getType(0).linker) 151 | assertSame(linker, adapter.types.getType(1).linker) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /sample/src/androidTest/kotlin/com/drakeet/multitype/sample/one2many/OneToManyTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.one2many 18 | 19 | import android.os.SystemClock 20 | import androidx.test.espresso.Espresso.onView 21 | import androidx.test.espresso.assertion.ViewAssertions.matches 22 | import androidx.test.espresso.matcher.ViewMatchers.withHint 23 | import androidx.test.rule.ActivityTestRule 24 | import androidx.test.runner.AndroidJUnit4 25 | import com.drakeet.multitype.MultiTypeAdapter 26 | import com.drakeet.multitype.sample.R 27 | import com.drakeet.multitype.sample.RecyclerViewMatcher.Companion.withRecyclerView 28 | import org.junit.Before 29 | import org.junit.Rule 30 | import org.junit.Test 31 | import org.junit.runner.RunWith 32 | 33 | /** 34 | * @author Drakeet Xu 35 | */ 36 | @RunWith(AndroidJUnit4::class) 37 | class OneToManyTest { 38 | 39 | private val testTitle = "testTitle" 40 | 41 | private lateinit var items: List 42 | private lateinit var adapter: MultiTypeAdapter 43 | 44 | @get:Rule 45 | var rule = ActivityTestRule( 46 | OneDataToManyActivity::class.java 47 | ) 48 | 49 | @Before 50 | fun setup() { 51 | items = rule.activity.adapter.items 52 | adapter = rule.activity.adapter 53 | } 54 | 55 | @Test 56 | @Throws(Throwable::class) 57 | fun shouldRefreshTypeChanged() { 58 | val originalFirst = items[0] as Data 59 | for (i in 0..1) { 60 | rule.runOnUiThread { 61 | originalFirst.type = Data.TYPE_2 62 | adapter.notifyItemChanged(0) 63 | } 64 | onView( 65 | withRecyclerView(R.id.list).atPositionOnView(0, android.R.id.title) 66 | ) 67 | .check(matches(withHint("right"))) 68 | SystemClock.sleep(2000) 69 | rule.runOnUiThread { rule.activity.recyclerView.smoothScrollToPosition(items.size - 1) } 70 | SystemClock.sleep(2000) 71 | rule.runOnUiThread { 72 | rule.activity.recyclerView.smoothScrollToPosition(0) 73 | originalFirst.type = Data.TYPE_1 74 | adapter.notifyItemChanged(0) 75 | } 76 | SystemClock.sleep(2000) 77 | onView( 78 | withRecyclerView(R.id.list).atPositionOnView(0, android.R.id.title) 79 | ) 80 | .check(matches(withHint("left"))) 81 | SystemClock.sleep(2000) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 21 | 22 | 29 | 30 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/MenuBaseActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample 18 | 19 | import android.annotation.SuppressLint 20 | import android.content.Intent 21 | import android.view.Menu 22 | import android.view.MenuItem 23 | import androidx.appcompat.app.AppCompatActivity 24 | import com.drakeet.multitype.sample.bilibili.BilibiliActivity 25 | import com.drakeet.multitype.sample.communication.CommunicateWithBinderActivity 26 | import com.drakeet.multitype.sample.more.MoreApisPlayground 27 | import com.drakeet.multitype.sample.normal.NormalActivity 28 | import com.drakeet.multitype.sample.one2many.OneDataToManyActivity 29 | import com.drakeet.multitype.sample.payload.TestPayloadActivity 30 | import com.drakeet.multitype.sample.selectable.MultiSelectableActivity 31 | import com.drakeet.multitype.sample.weibo.WeiboActivity 32 | 33 | /** 34 | * @author Drakeet Xu 35 | */ 36 | @SuppressLint("Registered") 37 | open class MenuBaseActivity : AppCompatActivity() { 38 | 39 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 40 | super.onCreateOptionsMenu(menu) 41 | menuInflater.inflate(R.menu.menu_main, menu) 42 | return true 43 | } 44 | 45 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 46 | val intent = Intent() 47 | when (item.itemId) { 48 | R.id.NormalActivity -> intent.setClass(this, NormalActivity::class.java) 49 | R.id.MultiSelectableActivity -> intent.setClass(this, MultiSelectableActivity::class.java) 50 | R.id.CommunicateWithBinderActivity -> intent.setClass(this, CommunicateWithBinderActivity::class.java) 51 | R.id.BilibiliActivity -> intent.setClass(this, BilibiliActivity::class.java) 52 | R.id.WeiboActivity -> intent.setClass(this, WeiboActivity::class.java) 53 | R.id.OneDataToManyActivity -> intent.setClass(this, OneDataToManyActivity::class.java) 54 | R.id.TestPayloadActivity -> intent.setClass(this, TestPayloadActivity::class.java) 55 | R.id.MoreApisPlayground -> intent.setClass(this, MoreApisPlayground::class.java) 56 | else -> return false 57 | } 58 | startActivity(intent) 59 | this.finish() 60 | return true 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/Numbers.kt: -------------------------------------------------------------------------------- 1 | package com.drakeet.multitype.sample 2 | 3 | import android.content.res.Resources 4 | 5 | /** 6 | * @author Drakeet Xu 7 | */ 8 | val Number.dp: Int get() = (toInt() * Resources.getSystem().displayMetrics.density).toInt() 9 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/Savable.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | interface Savable { 23 | 24 | fun init(data: ByteArray) 25 | 26 | fun toBytes(): ByteArray 27 | 28 | fun describe(): String 29 | } 30 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/Test.java: -------------------------------------------------------------------------------- 1 | package com.drakeet.multitype.sample; 2 | 3 | import com.drakeet.multitype.MultiTypeAdapter; 4 | 5 | /** 6 | * @author Drakeet Xu 7 | */ 8 | class Test { 9 | MultiTypeAdapter adapter = new MultiTypeAdapter(); 10 | } 11 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/bilibili/BilibiliActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.bilibili 18 | 19 | import android.os.Bundle 20 | import android.view.Menu 21 | import androidx.annotation.VisibleForTesting 22 | import androidx.recyclerview.widget.GridLayoutManager 23 | import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup 24 | import androidx.recyclerview.widget.RecyclerView 25 | import com.drakeet.multitype.MultiTypeAdapter 26 | import com.drakeet.multitype.sample.MenuBaseActivity 27 | import com.drakeet.multitype.sample.R 28 | import com.drakeet.multitype.sample.common.Category 29 | import com.drakeet.multitype.sample.common.CategoryHolderInflater 30 | import java.util.* 31 | 32 | /** 33 | * @author Drakeet Xu 34 | */ 35 | class BilibiliActivity : MenuBaseActivity() { 36 | 37 | @VisibleForTesting 38 | internal lateinit var items: MutableList 39 | @VisibleForTesting 40 | internal lateinit var adapter: MultiTypeAdapter 41 | 42 | private class JsonData { 43 | 44 | private val post00 = Post(R.drawable.img_00, PREFIX + "post00") 45 | private val post01 = Post(R.drawable.img_01, PREFIX + "post01") 46 | private val post10 = Post(R.drawable.img_10, PREFIX + "post10") 47 | private val post11 = Post(R.drawable.img_11, PREFIX + "post11") 48 | 49 | internal var category0 = Category("title0") 50 | internal var postArray = arrayOf(post00, post01, post10, post11) 51 | 52 | internal var postList: MutableList = ArrayList() 53 | 54 | init { 55 | postList.add(post00) 56 | postList.add(post00) 57 | postList.add(post00) 58 | postList.add(post00) 59 | postList.add(post00) 60 | postList.add(post00) 61 | } 62 | 63 | companion object { 64 | private const val PREFIX = "这是一条长长的达到两行的标题文字" 65 | } 66 | } 67 | 68 | override fun onCreate(savedInstanceState: Bundle?) { 69 | super.onCreate(savedInstanceState) 70 | setContentView(R.layout.activity_list) 71 | 72 | adapter = MultiTypeAdapter() 73 | adapter.register(CategoryHolderInflater()) 74 | 75 | adapter.register(PostViewBinder()) 76 | adapter.register(HorizontalPostsHolderInflater()) 77 | 78 | val recyclerView = findViewById(R.id.list) 79 | 80 | val layoutManager = GridLayoutManager(this, SPAN_COUNT) 81 | val spanSizeLookup = object : SpanSizeLookup() { 82 | override fun getSpanSize(position: Int): Int { 83 | val item = items[position] 84 | return if (item is PostList || item is Category) SPAN_COUNT else 1 85 | } 86 | } 87 | layoutManager.spanSizeLookup = spanSizeLookup 88 | recyclerView.layoutManager = layoutManager 89 | val space = resources.getDimensionPixelSize(R.dimen.normal_space) 90 | recyclerView.addItemDecoration(PostItemDecoration(space, spanSizeLookup)) 91 | 92 | recyclerView.adapter = adapter 93 | 94 | val data = JsonData() 95 | items = ArrayList() 96 | for (i in 0..9) { 97 | /* You also could use Category as your CategoryItemContent directly */ 98 | items.add(data.category0) 99 | items.add(data.postArray[0]) 100 | items.add(data.postArray[1]) 101 | items.add(data.postArray[2]) 102 | items.add(data.postArray[3]) 103 | items.add(data.postArray[0]) 104 | items.add(data.postArray[1]) 105 | items.add(PostList(data.postList)) 106 | } 107 | adapter.items = items 108 | adapter.notifyDataSetChanged() 109 | } 110 | 111 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 112 | val inflater = menuInflater 113 | inflater.inflate(R.menu.menu_main, menu) 114 | return true 115 | } 116 | 117 | companion object { 118 | private const val SPAN_COUNT = 2 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/bilibili/HorizontalPostsHolderInflater.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.bilibili 18 | 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import androidx.recyclerview.widget.LinearLayoutManager 23 | import androidx.recyclerview.widget.LinearSnapHelper 24 | import androidx.recyclerview.widget.RecyclerView 25 | import com.drakeet.multitype.ViewHolderInflater 26 | import com.drakeet.multitype.sample.R 27 | 28 | /** 29 | * @author Drakeet Xu 30 | */ 31 | class HorizontalPostsHolderInflater : ViewHolderInflater() { 32 | 33 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder { 34 | return ViewHolder(inflater.inflate(R.layout.item_horizontal_list, parent, false)) 35 | } 36 | 37 | override fun onBindViewHolder(holder: ViewHolder, item: PostList) { 38 | holder.setPosts(item.posts) 39 | } 40 | 41 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 42 | 43 | private val adapter: PostsAdapter = PostsAdapter() 44 | private val recyclerView: RecyclerView = itemView.findViewById(R.id.post_list) 45 | 46 | init { 47 | val layoutManager = LinearLayoutManager(itemView.context) 48 | layoutManager.orientation = LinearLayoutManager.HORIZONTAL 49 | recyclerView.layoutManager = layoutManager 50 | LinearSnapHelper().attachToRecyclerView(recyclerView) 51 | recyclerView.adapter = adapter 52 | } 53 | 54 | fun setPosts(posts: List) { 55 | adapter.setPosts(posts) 56 | adapter.notifyDataSetChanged() 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/bilibili/Post.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.bilibili 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | class Post(var coverResId: Int, var title: String) 23 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/bilibili/PostItemDecoration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.bilibili 18 | 19 | import android.graphics.Rect 20 | import android.view.View 21 | import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup 22 | import androidx.recyclerview.widget.RecyclerView 23 | 24 | /** 25 | * @author Drakeet Xu 26 | */ 27 | class PostItemDecoration(private val space: Int, private val spanSizeLookup: SpanSizeLookup) : RecyclerView.ItemDecoration() { 28 | 29 | override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { 30 | val position = parent.getChildLayoutPosition(view) 31 | if (spanSizeLookup.getSpanSize(position) == 1) { 32 | outRect.left = space 33 | if (position % 2 == 0) { 34 | outRect.right = space 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/bilibili/PostList.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.bilibili 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | class PostList(internal val posts: List) 23 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/bilibili/PostViewBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.bilibili 18 | 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import android.widget.ImageView 23 | import android.widget.TextView 24 | import androidx.recyclerview.widget.RecyclerView 25 | import com.drakeet.multitype.ItemViewBinder 26 | import com.drakeet.multitype.sample.R 27 | 28 | /** 29 | * @author Drakeet Xu 30 | */ 31 | class PostViewBinder : ItemViewBinder() { 32 | 33 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder { 34 | return ViewHolder(inflater.inflate(R.layout.item_post, parent, false)) 35 | } 36 | 37 | override fun onBindViewHolder(holder: ViewHolder, item: Post) { 38 | holder.setData(item) 39 | } 40 | 41 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 42 | 43 | private val cover: ImageView = itemView.findViewById(R.id.cover) 44 | private val title: TextView = itemView.findViewById(R.id.title) 45 | 46 | fun setData(post: Post) { 47 | cover.setImageResource(post.coverResId) 48 | title.text = post.title 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/bilibili/PostsAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.bilibili 18 | 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import android.widget.ImageView 23 | import android.widget.TextView 24 | import android.widget.Toast 25 | import androidx.recyclerview.widget.RecyclerView 26 | import com.drakeet.multitype.sample.R 27 | 28 | /** 29 | * @author Drakeet Xu 30 | */ 31 | class PostsAdapter : RecyclerView.Adapter() { 32 | 33 | private var posts = emptyList() 34 | 35 | fun setPosts(posts: List) { 36 | this.posts = posts 37 | } 38 | 39 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 40 | return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_horizontal_post, parent, false)) 41 | } 42 | 43 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 44 | val post = posts[position] 45 | holder.cover.setImageResource(post.coverResId) 46 | holder.title.text = post.title 47 | } 48 | 49 | override fun getItemCount(): Int { 50 | return posts.size 51 | } 52 | 53 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 54 | 55 | var cover: ImageView = itemView.findViewById(R.id.cover) 56 | var title: TextView = itemView.findViewById(R.id.title) 57 | 58 | init { 59 | itemView.setOnClickListener { v -> Toast.makeText(v.context, absoluteAdapterPosition.toString(), Toast.LENGTH_SHORT).show() } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/common/Category.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.common 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | class Category(var title: String) 23 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/common/CategoryHolderInflater.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.common 18 | 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import android.widget.TextView 23 | import androidx.recyclerview.widget.RecyclerView 24 | import com.drakeet.multitype.ViewHolderInflater 25 | import com.drakeet.multitype.sample.R 26 | 27 | /** 28 | * @author Drakeet Xu 29 | */ 30 | class CategoryHolderInflater : ViewHolderInflater() { 31 | 32 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder { 33 | return ViewHolder(inflater.inflate(R.layout.item_category, parent, false)) 34 | } 35 | 36 | override fun onBindViewHolder(holder: ViewHolder, item: Category) { 37 | holder.title.text = item.title 38 | } 39 | 40 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 41 | val title: TextView = itemView.findViewById(R.id.title) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/communication/CommunicateWithBinderActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.communication 18 | 19 | import android.os.Bundle 20 | import androidx.recyclerview.widget.RecyclerView 21 | import com.drakeet.multitype.MultiTypeAdapter 22 | import com.drakeet.multitype.sample.MenuBaseActivity 23 | import com.drakeet.multitype.sample.R 24 | import com.drakeet.multitype.sample.normal.TextItem 25 | import java.util.* 26 | 27 | /** 28 | * @author Drakeet Xu 29 | */ 30 | class CommunicateWithBinderActivity : MenuBaseActivity() { 31 | 32 | private val aFieldValue = "aFieldValue of SimpleActivity" 33 | private var adapter: MultiTypeAdapter = MultiTypeAdapter() 34 | 35 | override fun onCreate(savedInstanceState: Bundle?) { 36 | super.onCreate(savedInstanceState) 37 | setContentView(R.layout.activity_list) 38 | val recyclerView = findViewById(R.id.list) 39 | 40 | val items = ArrayList() 41 | adapter.register(TextItemWithOutsizeDataViewBinder(aFieldValue)) 42 | recyclerView.adapter = adapter 43 | 44 | for (i in 0..19) { 45 | items.add(TextItem(i.toString())) 46 | } 47 | adapter.items = items 48 | adapter.notifyDataSetChanged() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/communication/TextItemWithOutsizeDataViewBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.communication 18 | 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import android.widget.TextView 23 | import android.widget.Toast 24 | import androidx.recyclerview.widget.RecyclerView 25 | import com.drakeet.multitype.ItemViewBinder 26 | import com.drakeet.multitype.sample.R 27 | import com.drakeet.multitype.sample.normal.TextItem 28 | 29 | /** 30 | * @author Drakeet Xu 31 | */ 32 | class TextItemWithOutsizeDataViewBinder(var aValueFromOutside: String) : ItemViewBinder() { 33 | 34 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder { 35 | return ViewHolder(inflater.inflate(R.layout.item_text, parent, false)) 36 | } 37 | 38 | override fun onBindViewHolder(holder: ViewHolder, item: TextItem) { 39 | holder.setData(item) 40 | } 41 | 42 | inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 43 | 44 | private var textView: TextView = itemView.findViewById(R.id.text) 45 | private lateinit var value: String 46 | 47 | init { 48 | textView.setOnClickListener { v -> 49 | Toast.makeText( 50 | v.context, 51 | "item's value: $value, aValueFromOutside: $aValueFromOutside", Toast.LENGTH_SHORT 52 | ).show() 53 | } 54 | } 55 | 56 | fun setData(textItem: TextItem) { 57 | textView.text = textItem.text 58 | this.value = textItem.text 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/more/MoreApisPlayground.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.more 18 | 19 | import android.annotation.SuppressLint 20 | import android.os.Bundle 21 | import android.os.SystemClock.currentThreadTimeMillis 22 | import android.view.LayoutInflater 23 | import android.view.View 24 | import android.view.ViewGroup 25 | import android.widget.TextView 26 | import androidx.annotation.VisibleForTesting 27 | import androidx.recyclerview.widget.RecyclerView 28 | import com.drakeet.multitype.ItemViewBinder 29 | import com.drakeet.multitype.MultiTypeAdapter 30 | import com.drakeet.multitype.sample.MenuBaseActivity 31 | import com.drakeet.multitype.sample.R 32 | import com.drakeet.multitype.sample.normal.TextItem 33 | import java.util.* 34 | 35 | /** 36 | * @author Drakeet Xu 37 | */ 38 | @SuppressLint("SetTextI18n") 39 | class MoreApisPlayground : MenuBaseActivity() { 40 | 41 | private lateinit var terminal: TextView 42 | private lateinit var recyclerView: RecyclerView 43 | 44 | @VisibleForTesting 45 | lateinit var adapter: MultiTypeAdapter 46 | @VisibleForTesting 47 | lateinit var items: MutableList 48 | 49 | override fun onCreate(savedInstanceState: Bundle?) { 50 | super.onCreate(savedInstanceState) 51 | setContentView(R.layout.activity_more_apis_playground) 52 | recyclerView = findViewById(R.id.list) 53 | terminal = findViewById(R.id.terminal) 54 | terminal.text = TERMINAL_DEFAULT_TEXT 55 | 56 | items = ArrayList() 57 | adapter = MultiTypeAdapter() 58 | 59 | adapter.register(ObservableTextItemViewBinder()) 60 | recyclerView.adapter = adapter 61 | 62 | for (i in 0..199) { 63 | items.add(TextItem(i.toString())) 64 | } 65 | adapter.items = items 66 | adapter.notifyDataSetChanged() 67 | } 68 | 69 | fun onAdd(view: View) { 70 | val bottom = items.size - 1 71 | items.add(TextItem(currentThreadTimeMillis().toString())) 72 | adapter.notifyItemInserted(bottom + 1) 73 | recyclerView.scrollToPosition(bottom + 1) 74 | } 75 | 76 | fun onRemove(view: View) { 77 | val bottom = items.size - 1 78 | recyclerView.scrollToPosition(bottom) 79 | items.removeAt(bottom) 80 | adapter.notifyItemRemoved(bottom) 81 | } 82 | 83 | fun onClear(view: View) { 84 | items.clear() 85 | adapter.notifyDataSetChanged() 86 | } 87 | 88 | private inner class ObservableTextItemViewBinder : ItemViewBinder() { 89 | 90 | private var buffer = 0 91 | 92 | inner class TextHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 93 | val text: TextView = itemView.findViewById(R.id.text) 94 | } 95 | 96 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): TextHolder { 97 | val root = inflater.inflate(R.layout.item_text, parent, false) 98 | return TextHolder(root) 99 | } 100 | 101 | override fun onBindViewHolder(holder: TextHolder, item: TextItem) { 102 | holder.text.text = "observable item(" + item.text + ")" 103 | } 104 | 105 | override fun onViewRecycled(holder: TextHolder) { 106 | appendTerminalLine("onViewRecycled: " + holder.text.text) 107 | } 108 | 109 | override fun onFailedToRecycleView(holder: TextHolder): Boolean { 110 | appendTerminalLine("onFailedToRecycleView: " + holder.text.text) 111 | return true 112 | } 113 | 114 | override fun onViewAttachedToWindow(holder: TextHolder) { 115 | appendTerminalLine("onViewAttachedToWindow: " + holder.text.text) 116 | } 117 | 118 | override fun onViewDetachedFromWindow(holder: TextHolder) { 119 | appendTerminalLine("onViewDetachedFromWindow: " + holder.text.text) 120 | } 121 | 122 | private fun appendTerminalLine(line: String) { 123 | if (buffer == 5) { 124 | terminal.text = TERMINAL_DEFAULT_TEXT 125 | buffer = 0 126 | } 127 | terminal.append("\n" + line) 128 | buffer++ 129 | } 130 | } 131 | 132 | companion object { 133 | 134 | private const val TERMINAL_DEFAULT_TEXT = "ObservableTextItemViewBinder: " 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/normal/ImageItem.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.normal 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | class ImageItem(val resId: Int) 23 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/normal/ImageItemViewBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.normal 18 | 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import android.widget.ImageView 23 | import androidx.recyclerview.widget.RecyclerView 24 | import com.drakeet.multitype.ItemViewBinder 25 | import com.drakeet.multitype.sample.R 26 | 27 | /** 28 | * @author Drakeet Xu 29 | */ 30 | class ImageItemViewBinder : ItemViewBinder() { 31 | 32 | inner class ImageHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 33 | val image: ImageView = itemView.findViewById(R.id.image) 34 | 35 | } 36 | 37 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ImageHolder { 38 | return ImageHolder(inflater.inflate(R.layout.item_image, parent, false)) 39 | } 40 | 41 | override fun onBindViewHolder(holder: ImageHolder, item: ImageItem) { 42 | holder.image.setImageResource(item.resId) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/normal/NormalActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.normal 18 | 19 | import android.annotation.SuppressLint 20 | import android.content.Context 21 | import android.os.Bundle 22 | import android.widget.TextView 23 | import androidx.recyclerview.widget.RecyclerView 24 | import com.drakeet.multitype.MultiTypeAdapter 25 | import com.drakeet.multitype.ViewDelegate 26 | import com.drakeet.multitype.sample.MenuBaseActivity 27 | import com.drakeet.multitype.sample.R 28 | import java.util.* 29 | 30 | /** 31 | * @author Drakeet Xu 32 | */ 33 | class NormalActivity : MenuBaseActivity() { 34 | 35 | private val adapter = MultiTypeAdapter() 36 | private val items = ArrayList() 37 | 38 | override fun onCreate(savedInstanceState: Bundle?) { 39 | super.onCreate(savedInstanceState) 40 | setContentView(R.layout.activity_list) 41 | val recyclerView = findViewById(R.id.list) 42 | 43 | adapter.register(TextItemViewBinder()) 44 | adapter.register(ImageItemViewBinder()) 45 | // ✨✨✨ 46 | adapter.register(RichViewDelegate()) 47 | 48 | // Test https://github.com/drakeet/MultiType/issues/302 49 | // Kotlin will reify and inline this to Integer.class 50 | adapter.register(Int::class, IntViewDelegate()) 51 | 52 | recyclerView.adapter = adapter 53 | 54 | val textItem = TextItem("world") 55 | val imageItem = ImageItem(R.mipmap.ic_launcher) 56 | val richItem = RichItem("小艾大人赛高", R.drawable.img_11) 57 | 58 | for (i in 0..19) { 59 | items.add(textItem) 60 | items.add(imageItem) 61 | items.add(richItem) 62 | } 63 | items.add(Integer.valueOf(999)) 64 | items.add(666) 65 | items.add(333) 66 | adapter.items = items 67 | adapter.notifyDataSetChanged() 68 | } 69 | 70 | private class IntViewDelegate : ViewDelegate() { 71 | 72 | override fun onCreateView(context: Context): TextView { 73 | return TextView(context) 74 | } 75 | 76 | @SuppressLint("SetTextI18n") 77 | override fun onBindView(view: TextView, item: Int) { 78 | view.text = "IntViewItem: $item" 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/normal/RichItem.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.normal 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | class RichItem(var text: String, var imageResId: Int) 23 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/normal/RichView.kt: -------------------------------------------------------------------------------- 1 | package com.drakeet.multitype.sample.normal 2 | 3 | import android.content.Context 4 | import android.graphics.Color 5 | import android.view.Gravity 6 | import android.view.ViewGroup.LayoutParams.WRAP_CONTENT 7 | import android.widget.LinearLayout 8 | import androidx.appcompat.widget.AppCompatImageView 9 | import androidx.appcompat.widget.AppCompatTextView 10 | import com.drakeet.multitype.sample.R 11 | import com.drakeet.multitype.sample.dp 12 | 13 | /** 14 | * @author Drakeet Xu 15 | */ 16 | class RichView(context: Context) : LinearLayout(context) { 17 | 18 | val imageView = AppCompatImageView(context).apply { 19 | addView(this, LayoutParams(72.dp, 72.dp)) 20 | } 21 | 22 | val textView = AppCompatTextView(context).apply { 23 | gravity = Gravity.CENTER 24 | setTextColor(Color.BLACK) 25 | addView(this, LayoutParams(WRAP_CONTENT, WRAP_CONTENT)) 26 | } 27 | 28 | init { 29 | orientation = VERTICAL 30 | gravity = Gravity.CENTER 31 | setPadding(16.dp, 16.dp, 16.dp, 16.dp) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/normal/RichViewDelegate.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.normal 18 | 19 | import android.annotation.SuppressLint 20 | import android.content.Context 21 | import android.view.Gravity 22 | import android.view.ViewGroup 23 | import android.view.ViewGroup.LayoutParams.MATCH_PARENT 24 | import android.view.ViewGroup.LayoutParams.WRAP_CONTENT 25 | import androidx.recyclerview.widget.RecyclerView 26 | import androidx.recyclerview.widget.RecyclerView.LayoutParams 27 | import com.drakeet.multitype.ViewDelegate 28 | import com.drakeet.multitype.sample.dp 29 | 30 | /** 31 | * @author Drakeet Xu 32 | */ 33 | class RichViewDelegate : ViewDelegate() { 34 | 35 | override fun onCreateView(context: Context): RichView { 36 | return RichView(context).apply { layoutParams = LayoutParams(MATCH_PARENT, WRAP_CONTENT) } 37 | } 38 | 39 | @SuppressLint("SetTextI18n") 40 | override fun onBindView(view: RichView, item: RichItem) { 41 | view.imageView.setImageResource(item.imageResId) 42 | view.textView.text = """ 43 | |${item.text} 44 | |layoutPosition: ${view.layoutPosition} 45 | |absoluteAdapterPosition: ${view.absoluteAdapterPosition} 46 | |bindingAdapterPosition: ${view.bindingAdapterPosition} 47 | """.trimMargin() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/normal/TextItem.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.normal 18 | 19 | import com.google.gson.Gson 20 | import com.drakeet.multitype.sample.Savable 21 | import java.nio.charset.Charset 22 | 23 | /** 24 | * @author Drakeet Xu 25 | */ 26 | class TextItem : Savable { 27 | 28 | lateinit var text: String 29 | 30 | constructor(text: String) { 31 | this.text = text 32 | } 33 | 34 | constructor(data: ByteArray) { 35 | init(data) 36 | } 37 | 38 | override fun init(data: ByteArray) { 39 | val json = String(data, UTF_8) 40 | this.text = Gson().fromJson(json, TextItem::class.java).text 41 | } 42 | 43 | override fun toBytes(): ByteArray { 44 | return Gson().toJson(this).toByteArray(UTF_8) 45 | } 46 | 47 | override fun describe(): String { 48 | return "Text" 49 | } 50 | 51 | companion object { 52 | private val UTF_8 = Charset.forName("UTF-8") 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/normal/TextItemViewBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.normal 18 | 19 | import android.annotation.SuppressLint 20 | import android.view.LayoutInflater 21 | import android.view.View 22 | import android.view.ViewGroup 23 | import android.view.animation.AnimationUtils 24 | import android.widget.TextView 25 | import androidx.recyclerview.widget.RecyclerView 26 | import com.drakeet.multitype.ItemViewBinder 27 | import com.drakeet.multitype.sample.R 28 | 29 | /** 30 | * @author Drakeet Xu 31 | */ 32 | class TextItemViewBinder : ItemViewBinder() { 33 | 34 | private var lastShownAnimationPosition: Int = 0 35 | 36 | class TextHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 37 | val text: TextView = itemView.findViewById(R.id.text) 38 | } 39 | 40 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): TextHolder { 41 | return TextHolder(inflater.inflate(R.layout.item_text, parent, false)) 42 | } 43 | 44 | @SuppressLint("SetTextI18n") 45 | override fun onBindViewHolder(holder: TextHolder, item: TextItem) { 46 | holder.text.text = "hello: " + item.text 47 | // should show animation, ref: https://github.com/drakeet/MultiType/issues/149 48 | setAnimation(holder.itemView, holder.absoluteAdapterPosition) 49 | } 50 | 51 | private fun setAnimation(viewToAnimate: View, position: Int) { 52 | if (position > lastShownAnimationPosition) { 53 | viewToAnimate.startAnimation(AnimationUtils.loadAnimation(viewToAnimate.context, android.R.anim.slide_in_left)) 54 | lastShownAnimationPosition = position 55 | } 56 | } 57 | 58 | override fun onViewDetachedFromWindow(holder: TextHolder) { 59 | holder.itemView.clearAnimation() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/one2many/Data.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.one2many 18 | 19 | import com.google.gson.annotations.SerializedName 20 | 21 | /** 22 | * @author Drakeet Xu 23 | */ 24 | class Data( 25 | @field:SerializedName("title") var title: String, 26 | @field:SerializedName("type") var type: Int 27 | ) { 28 | 29 | companion object { 30 | const val TYPE_1 = 1 31 | const val TYPE_2 = 2 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/one2many/DataType1ViewBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.one2many 18 | 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import android.widget.TextView 23 | import androidx.recyclerview.widget.RecyclerView 24 | import com.drakeet.multitype.ItemViewBinder 25 | import com.drakeet.multitype.sample.R 26 | 27 | /** 28 | * Note: Data - DataType1ViewBinder 29 | * 30 | * @author Drakeet Xu 31 | */ 32 | class DataType1ViewBinder : ItemViewBinder() { 33 | 34 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder { 35 | return ViewHolder(inflater.inflate(R.layout.item_data_type1, parent, false)) 36 | } 37 | 38 | override fun onBindViewHolder(holder: ViewHolder, item: Data) { 39 | holder.setTitle(item.title) 40 | } 41 | 42 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 43 | 44 | private var titleView: TextView = itemView.findViewById(android.R.id.title) 45 | 46 | fun setTitle(title: String) { 47 | titleView.text = title 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/one2many/DataType2ViewBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.one2many 18 | 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import android.widget.TextView 23 | import androidx.recyclerview.widget.RecyclerView 24 | import com.drakeet.multitype.ItemViewBinder 25 | import com.drakeet.multitype.sample.R 26 | 27 | /** 28 | * Note: Data - DataType2ViewBinder 29 | * 30 | * @author Drakeet Xu 31 | */ 32 | class DataType2ViewBinder : ItemViewBinder() { 33 | 34 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder { 35 | return ViewHolder(inflater.inflate(R.layout.item_data_type2, parent, false)) 36 | } 37 | 38 | override fun onBindViewHolder(holder: DataType2ViewBinder.ViewHolder, item: Data) { 39 | holder.setTitle(item.title) 40 | } 41 | 42 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 43 | 44 | private var titleView: TextView = itemView.findViewById(android.R.id.title) 45 | 46 | fun setTitle(title: String) { 47 | titleView.text = title 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/one2many/OneDataToManyActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.one2many 18 | 19 | import android.os.Bundle 20 | import androidx.annotation.VisibleForTesting 21 | import androidx.recyclerview.widget.RecyclerView 22 | import com.drakeet.multitype.MultiTypeAdapter 23 | import com.drakeet.multitype.sample.MenuBaseActivity 24 | import com.drakeet.multitype.sample.R 25 | import java.util.* 26 | 27 | /** 28 | * @author Drakeet Xu 29 | */ 30 | class OneDataToManyActivity : MenuBaseActivity() { 31 | 32 | @VisibleForTesting 33 | lateinit var recyclerView: RecyclerView 34 | @VisibleForTesting 35 | lateinit var adapter: MultiTypeAdapter 36 | 37 | private val dataFromService: List 38 | @VisibleForTesting 39 | get() { 40 | val list = ArrayList() 41 | var i = 0 42 | while (i < 30) { 43 | list.add(Data("title: $i", Data.TYPE_1)) 44 | list.add(Data("title: ${ i + 1 }", Data.TYPE_2)) 45 | i += 2 46 | } 47 | return list 48 | } 49 | 50 | override fun onCreate(savedInstanceState: Bundle?) { 51 | super.onCreate(savedInstanceState) 52 | setContentView(R.layout.activity_list) 53 | recyclerView = findViewById(R.id.list) 54 | adapter = MultiTypeAdapter() 55 | 56 | adapter.register(Data::class).to( 57 | DataType1ViewBinder(), 58 | DataType2ViewBinder() 59 | ).withKotlinClassLinker { _, data -> 60 | when (data.type) { 61 | Data.TYPE_2 -> DataType2ViewBinder::class 62 | else -> DataType1ViewBinder::class 63 | } 64 | } 65 | 66 | adapter.items = dataFromService 67 | adapter.notifyDataSetChanged() 68 | recyclerView.adapter = adapter 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/payload/HeavyItem.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.payload 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | class HeavyItem(var text: String) 23 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/payload/HeavyItemViewBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.payload 18 | 19 | import android.annotation.SuppressLint 20 | import android.view.LayoutInflater 21 | import android.view.View 22 | import android.view.ViewGroup 23 | import android.widget.TextView 24 | import android.widget.Toast 25 | import androidx.recyclerview.widget.RecyclerView 26 | import com.drakeet.multitype.ItemViewBinder 27 | import com.drakeet.multitype.sample.R 28 | 29 | /** 30 | * @author Drakeet Xu 31 | */ 32 | internal class HeavyItemViewBinder : ItemViewBinder() { 33 | 34 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder { 35 | return ViewHolder(inflater.inflate(R.layout.item_heavy, parent, false)) 36 | } 37 | 38 | @SuppressLint("SetTextI18n") 39 | override fun onBindViewHolder(holder: ViewHolder, item: HeavyItem) { 40 | holder.firstText.text = item.text 41 | holder.endText.text = "currentTimeMillis: " + System.currentTimeMillis() 42 | holder.item = item 43 | } 44 | 45 | @SuppressLint("SetTextI18n") 46 | override fun onBindViewHolder(holder: ViewHolder, item: HeavyItem, payloads: List) { 47 | if (payloads.isEmpty()) { 48 | super.onBindViewHolder(holder, item, payloads) 49 | } else { 50 | holder.firstText.text = "Just update the first text: " + payloads[0] 51 | } 52 | } 53 | 54 | inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener, View.OnLongClickListener { 55 | 56 | var firstText: TextView = itemView.findViewById(R.id.first_text) 57 | var endText: TextView = itemView.findViewById(R.id.end_text) 58 | var item: HeavyItem? = null 59 | 60 | init { 61 | itemView.setOnClickListener(this) 62 | itemView.setOnLongClickListener(this) 63 | } 64 | 65 | override fun onClick(v: View) { 66 | Toast.makeText(v.context, "Update with a payload", Toast.LENGTH_SHORT).show() 67 | adapter.notifyItemChanged(bindingAdapterPosition, "la la la (payload)") 68 | } 69 | 70 | override fun onLongClick(v: View): Boolean { 71 | Toast.makeText(v.context, "Full update", Toast.LENGTH_SHORT).show() 72 | item!!.text = "full full full" 73 | adapter.notifyItemChanged(bindingAdapterPosition) 74 | return true 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/payload/TestPayloadActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.payload 18 | 19 | import android.os.Bundle 20 | import android.widget.Toast 21 | import androidx.recyclerview.widget.DividerItemDecoration 22 | import androidx.recyclerview.widget.DividerItemDecoration.VERTICAL 23 | import androidx.recyclerview.widget.RecyclerView 24 | import com.drakeet.multitype.MultiTypeAdapter 25 | import com.drakeet.multitype.sample.MenuBaseActivity 26 | import com.drakeet.multitype.sample.R 27 | import java.util.* 28 | 29 | class TestPayloadActivity : MenuBaseActivity() { 30 | 31 | override fun onCreate(savedInstanceState: Bundle?) { 32 | super.onCreate(savedInstanceState) 33 | setContentView(R.layout.activity_list) 34 | val recyclerView = findViewById(R.id.list) 35 | recyclerView.addItemDecoration(DividerItemDecoration(this, VERTICAL)) 36 | val adapter = MultiTypeAdapter() 37 | recyclerView.adapter = adapter 38 | 39 | adapter.register(HeavyItemViewBinder()) 40 | 41 | val items = ArrayList() 42 | for (i in 0..29) { 43 | items.add(HeavyItem("1000$i")) 44 | } 45 | adapter.items = items 46 | adapter.notifyDataSetChanged() 47 | 48 | Toast.makeText(this, "Try to click or long click items", Toast.LENGTH_SHORT).show() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/selectable/MultiSelectableActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.selectable 18 | 19 | import android.os.Bundle 20 | import android.widget.Button 21 | import android.widget.Toast 22 | import androidx.recyclerview.widget.GridLayoutManager 23 | import androidx.recyclerview.widget.RecyclerView 24 | import com.drakeet.multitype.MultiTypeAdapter 25 | import com.drakeet.multitype.sample.MenuBaseActivity 26 | import com.drakeet.multitype.sample.R 27 | import com.drakeet.multitype.sample.common.Category 28 | import com.drakeet.multitype.sample.common.CategoryHolderInflater 29 | import java.util.* 30 | 31 | class MultiSelectableActivity : MenuBaseActivity() { 32 | 33 | var items: MutableList = ArrayList() 34 | var adapter = MultiTypeAdapter() 35 | private lateinit var fab: Button 36 | private lateinit var selectedSet: TreeSet 37 | 38 | override fun onCreate(savedInstanceState: Bundle?) { 39 | super.onCreate(savedInstanceState) 40 | setContentView(R.layout.activity_multi_selectable) 41 | val recyclerView = findViewById(R.id.list) 42 | val layoutManager = GridLayoutManager(this, SPAN_COUNT) 43 | layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { 44 | override fun getSpanSize(position: Int): Int { 45 | return if (items[position] is Category) SPAN_COUNT else 1 46 | } 47 | } 48 | 49 | selectedSet = TreeSet() 50 | 51 | recyclerView.layoutManager = layoutManager 52 | adapter.register(CategoryHolderInflater()) 53 | adapter.register(SquareViewBinder(selectedSet)) 54 | 55 | loadData() 56 | recyclerView.adapter = adapter 57 | 58 | setupFAB() 59 | } 60 | 61 | private fun loadData() { 62 | val spacialCategory = Category("特别篇") 63 | items.add(spacialCategory) 64 | for (i in 0..6) { 65 | items.add(Square(i + 1)) 66 | } 67 | val currentCategory = Category("本篇") 68 | items.add(currentCategory) 69 | for (i in 0..999) { 70 | items.add(Square(i + 1)) 71 | } 72 | adapter.items = items 73 | adapter.notifyDataSetChanged() 74 | } 75 | 76 | private fun setupFAB() { 77 | fab = findViewById(R.id.fab) 78 | fab.setOnClickListener { v -> 79 | val content = StringBuilder() 80 | for (number in selectedSet) { 81 | content.append(number).append(" ") 82 | } 83 | Toast.makeText(v.context, "Selected items: $content", Toast.LENGTH_SHORT).show() 84 | } 85 | } 86 | 87 | companion object { 88 | private const val SPAN_COUNT = 5 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/selectable/Square.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.selectable 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | class Square(val number: Int) { 23 | var isSelected: Boolean = false 24 | } 25 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/selectable/SquareViewBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.selectable 18 | 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import android.widget.TextView 23 | import androidx.recyclerview.widget.RecyclerView 24 | import com.drakeet.multitype.ItemViewBinder 25 | import com.drakeet.multitype.sample.R 26 | 27 | /** 28 | * @author Drakeet Xu 29 | */ 30 | class SquareViewBinder(val selectedSet: MutableSet) : ItemViewBinder() { 31 | 32 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder { 33 | return ViewHolder(inflater.inflate(R.layout.item_square, parent, false)) 34 | } 35 | 36 | override fun onBindViewHolder(holder: ViewHolder, item: Square) { 37 | holder.square = item 38 | holder.squareView.text = item.number.toString() 39 | holder.squareView.isSelected = item.isSelected 40 | } 41 | 42 | inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 43 | 44 | val squareView: TextView = itemView.findViewById(R.id.square) 45 | lateinit var square: Square 46 | 47 | init { 48 | itemView.setOnClickListener { 49 | square.apply { 50 | squareView.isSelected = !isSelected 51 | this.isSelected = !isSelected 52 | } 53 | if (square.isSelected) { 54 | selectedSet.add(square.number) 55 | } else { 56 | selectedSet.remove(square.number) 57 | } 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/weibo/ContentHolder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.weibo 18 | 19 | import android.view.View 20 | 21 | /** 22 | * @author Drakeet Xu 23 | */ 24 | open class ContentHolder(val itemView: View) { 25 | 26 | lateinit var parent: WeiboFrameBinder.FrameHolder 27 | 28 | val absoluteAdapterPosition: Int 29 | get() = parent.absoluteAdapterPosition 30 | 31 | val layoutPosition: Int 32 | get() = parent.layoutPosition 33 | 34 | val oldPosition: Int 35 | get() = parent.oldPosition 36 | 37 | var isRecyclable: Boolean 38 | get() = parent.isRecyclable 39 | set(recyclable) = parent.setIsRecyclable(recyclable) 40 | } 41 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/weibo/User.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.weibo 18 | 19 | import androidx.annotation.DrawableRes 20 | 21 | /** 22 | * @author Drakeet Xu 23 | */ 24 | class User(var name: String, @field:DrawableRes var avatar: Int) 25 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/weibo/Weibo.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.weibo 18 | 19 | /** 20 | * @author Drakeet Xu 21 | */ 22 | class Weibo(var user: User, var content: WeiboContent) { 23 | 24 | var createTime: String = "Just now" 25 | 26 | override fun toString(): String { 27 | return "content: " + content.javaClass.simpleName 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/weibo/WeiboActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.weibo 18 | 19 | import android.os.Bundle 20 | import androidx.recyclerview.widget.RecyclerView 21 | import com.drakeet.multitype.MultiTypeAdapter 22 | import com.drakeet.multitype.sample.MenuBaseActivity 23 | import com.drakeet.multitype.sample.R 24 | import com.drakeet.multitype.sample.weibo.content.SimpleImage 25 | import com.drakeet.multitype.sample.weibo.content.SimpleImageViewBinder 26 | import com.drakeet.multitype.sample.weibo.content.SimpleText 27 | import com.drakeet.multitype.sample.weibo.content.SimpleTextViewBinder 28 | import java.util.* 29 | 30 | /** 31 | * @author Drakeet Xu 32 | */ 33 | class WeiboActivity : MenuBaseActivity() { 34 | 35 | private lateinit var adapter: MultiTypeAdapter 36 | private lateinit var items: MutableList 37 | 38 | override fun onCreate(savedInstanceState: Bundle?) { 39 | super.onCreate(savedInstanceState) 40 | setContentView(R.layout.activity_list) 41 | val recyclerView = findViewById(R.id.list) 42 | 43 | adapter = MultiTypeAdapter() 44 | 45 | adapter.register(Weibo::class).to( 46 | SimpleTextViewBinder(), 47 | SimpleImageViewBinder() 48 | ).withLinker { _, weibo -> 49 | when (weibo.content) { 50 | is SimpleText -> 0 51 | is SimpleImage -> 1 52 | else -> 0 53 | } 54 | } 55 | 56 | recyclerView.adapter = adapter 57 | 58 | items = ArrayList() 59 | 60 | val user = User("drakeet", R.drawable.avatar_drakeet) 61 | val simpleText = SimpleText("A simple text Weibo: Hello World.") 62 | val simpleImage = SimpleImage(R.drawable.img_10) 63 | for (i in 0..19) { 64 | items.add(Weibo(user, simpleText)) 65 | items.add(Weibo(user, simpleImage)) 66 | } 67 | adapter.items = items 68 | adapter.notifyDataSetChanged() 69 | 70 | loadRemoteData() 71 | } 72 | 73 | private fun loadRemoteData() { 74 | val weiboList = WeiboJsonParser.fromJson( 75 | JSON_FROM_SERVICE 76 | .replace("\$avatar", "" + R.drawable.avatar_drakeet) 77 | .replace("\$content", "" + R.drawable.img_00) 78 | ) 79 | items = ArrayList(items) 80 | items.addAll(0, weiboList) 81 | adapter.items = items 82 | adapter.notifyDataSetChanged() 83 | } 84 | 85 | companion object { 86 | private const val JSON_FROM_SERVICE = """[ 87 | { 88 | "content":{ 89 | "text":"A simple text Weibo: JSON_FROM_SERVICE.", 90 | "content_type":"simple_text" 91 | }, 92 | "createTime":"Just now", 93 | "user":{ 94 | "avatar":${"$"}avatar, 95 | "name":"drakeet" 96 | } 97 | }, 98 | { 99 | "content":{ 100 | "resId":${"$"}content, 101 | "content_type":"simple_image" 102 | }, 103 | "createTime":"Just now(JSON_FROM_SERVICE)", 104 | "user":{ 105 | "avatar":${"$"}avatar, 106 | "name":"drakeet" 107 | } 108 | } 109 | ]""" 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/weibo/WeiboContent.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.weibo 18 | 19 | import com.google.gson.annotations.SerializedName 20 | 21 | /** 22 | * @author Drakeet Xu 23 | */ 24 | abstract class WeiboContent protected constructor( 25 | @field:SerializedName("content_type") 26 | val contentType: String 27 | ) 28 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/weibo/WeiboContentDeserializer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.weibo 18 | 19 | import com.google.gson.* 20 | import com.drakeet.multitype.sample.weibo.content.SimpleImage 21 | import com.drakeet.multitype.sample.weibo.content.SimpleText 22 | import java.lang.reflect.Type 23 | 24 | /** 25 | * @author Drakeet Xu 26 | */ 27 | class WeiboContentDeserializer : JsonDeserializer { 28 | 29 | @Throws(JsonParseException::class) 30 | override fun deserialize(json: JsonElement, type: Type, context: JsonDeserializationContext): WeiboContent? { 31 | val gson = WeiboJsonParser.GSON 32 | val jsonObject = json as JsonObject 33 | val contentType = stringOrEmpty(jsonObject.get("content_type")) 34 | var content: WeiboContent? = null 35 | 36 | if (SimpleText.TYPE == contentType) { 37 | content = gson.fromJson(json, SimpleText::class.java) 38 | } else if (SimpleImage.TYPE == contentType) { 39 | content = gson.fromJson(json, SimpleImage::class.java) 40 | } 41 | return content 42 | } 43 | 44 | private fun stringOrEmpty(jsonElement: JsonElement): String { 45 | return if (jsonElement.isJsonNull) "" else jsonElement.asString 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/weibo/WeiboFrameBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.weibo 18 | 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import android.widget.FrameLayout 23 | import android.widget.ImageView 24 | import android.widget.TextView 25 | import android.widget.Toast 26 | import android.widget.Toast.LENGTH_SHORT 27 | import androidx.recyclerview.widget.RecyclerView 28 | import com.drakeet.multitype.ItemViewBinder 29 | import com.drakeet.multitype.sample.R 30 | 31 | /** 32 | * @author Drakeet Xu 33 | */ 34 | abstract class WeiboFrameBinder : ItemViewBinder() { 35 | 36 | protected abstract fun onCreateContentViewHolder(inflater: LayoutInflater, parent: ViewGroup): ContentHolder 37 | 38 | protected abstract fun onBindContentViewHolder(holder: SubViewHolder, content: Content) 39 | 40 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): FrameHolder { 41 | val root = inflater.inflate(R.layout.item_weibo_frame, parent, false) 42 | val subViewHolder = onCreateContentViewHolder(inflater, parent) 43 | return FrameHolder(root, subViewHolder, this) 44 | } 45 | 46 | override fun onBindViewHolder(holder: FrameHolder, item: Weibo) { 47 | holder.avatar.setImageResource(item.user.avatar) 48 | holder.username.text = item.user.name 49 | holder.createTime.text = item.createTime 50 | val weiboContent = item.content 51 | @Suppress("UNCHECKED_CAST") 52 | onBindContentViewHolder(holder.subViewHolder as SubViewHolder, weiboContent as Content) 53 | } 54 | 55 | class FrameHolder(itemView: View, val subViewHolder: ContentHolder, binder: WeiboFrameBinder<*, *>) : RecyclerView.ViewHolder(itemView) { 56 | 57 | val avatar: ImageView = itemView.findViewById(R.id.avatar) 58 | val username: TextView = itemView.findViewById(R.id.username) 59 | val createTime: TextView = itemView.findViewById(R.id.create_time) 60 | private val container: FrameLayout = itemView.findViewById(R.id.container) 61 | private val close: TextView = itemView.findViewById(R.id.close) 62 | 63 | init { 64 | container.addView(subViewHolder.itemView) 65 | this.subViewHolder.parent = this 66 | 67 | itemView.setOnClickListener { v -> Toast.makeText(v.context, "Position: $bindingAdapterPosition", LENGTH_SHORT).show() } 68 | close.setOnClickListener { 69 | val position = bindingAdapterPosition 70 | if (position != RecyclerView.NO_POSITION) { 71 | binder.adapter.items.toMutableList() 72 | .apply { 73 | removeAt(position) 74 | binder.adapter.items = this 75 | } 76 | binder.adapter.notifyItemRemoved(position) 77 | } 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/weibo/WeiboJsonParser.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.weibo 18 | 19 | import com.google.gson.GsonBuilder 20 | import com.google.gson.reflect.TypeToken 21 | import java.util.* 22 | 23 | /** 24 | * @author Drakeet Xu 25 | */ 26 | internal object WeiboJsonParser { 27 | 28 | val GSON = GsonBuilder() 29 | .registerTypeAdapter(WeiboContent::class.java, WeiboContentDeserializer()) 30 | .create()!! 31 | 32 | fun fromJson(json: String): List { 33 | return GSON.fromJson(json, object : TypeToken>() {}.type) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/weibo/content/SimpleImage.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.weibo.content 18 | 19 | import androidx.annotation.DrawableRes 20 | import com.drakeet.multitype.sample.weibo.WeiboContent 21 | 22 | /** 23 | * @author Drakeet Xu 24 | */ 25 | class SimpleImage( 26 | @param:DrawableRes @field:DrawableRes var resId: Int 27 | ) : WeiboContent(TYPE) { 28 | 29 | companion object { 30 | const val TYPE = "simple_image" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/weibo/content/SimpleImageViewBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.weibo.content 18 | 19 | import android.util.Log 20 | import android.view.LayoutInflater 21 | import android.view.View 22 | import android.view.ViewGroup 23 | import android.widget.ImageView 24 | import com.drakeet.multitype.sample.R 25 | import com.drakeet.multitype.sample.weibo.ContentHolder 26 | import com.drakeet.multitype.sample.weibo.WeiboFrameBinder 27 | 28 | /** 29 | * @author Drakeet Xu 30 | */ 31 | class SimpleImageViewBinder : WeiboFrameBinder() { 32 | 33 | override fun onCreateContentViewHolder(inflater: LayoutInflater, parent: ViewGroup): ContentHolder { 34 | return ViewHolder(inflater.inflate(R.layout.item_weibo_simple_image, parent, false)) 35 | } 36 | 37 | override fun onBindContentViewHolder(holder: ViewHolder, content: SimpleImage) { 38 | Log.d("weibo", "absoluteAdapterPosition: " + holder.absoluteAdapterPosition) 39 | Log.d("weibo", "getLayoutPosition: " + holder.layoutPosition) 40 | Log.d("weibo", "getOldPosition: " + holder.oldPosition) 41 | Log.d("weibo", "isRecyclable: " + holder.isRecyclable) 42 | holder.simpleImage.setImageResource(content.resId) 43 | } 44 | 45 | class ViewHolder(itemView: View) : ContentHolder(itemView) { 46 | val simpleImage: ImageView = itemView.findViewById(R.id.simple_image) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/weibo/content/SimpleText.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.weibo.content 18 | 19 | import com.drakeet.multitype.sample.weibo.WeiboContent 20 | 21 | /** 22 | * @author Drakeet Xu 23 | */ 24 | class SimpleText(var text: String) : WeiboContent(TYPE) { 25 | companion object { 26 | const val TYPE = "simple_text" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/drakeet/multitype/sample/weibo/content/SimpleTextViewBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present. Drakeet Xu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.drakeet.multitype.sample.weibo.content 18 | 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import android.widget.TextView 23 | import com.drakeet.multitype.sample.R 24 | import com.drakeet.multitype.sample.weibo.ContentHolder 25 | import com.drakeet.multitype.sample.weibo.WeiboFrameBinder 26 | 27 | /** 28 | * @author Drakeet Xu 29 | */ 30 | class SimpleTextViewBinder : WeiboFrameBinder() { 31 | 32 | override fun onCreateContentViewHolder(inflater: LayoutInflater, parent: ViewGroup): ContentHolder { 33 | return ViewHolder(inflater.inflate(R.layout.item_weibo_simple_text, parent, false)) 34 | } 35 | 36 | override fun onBindContentViewHolder(holder: ViewHolder, content: SimpleText) { 37 | holder.simpleText.text = content.text 38 | } 39 | 40 | class ViewHolder(itemView: View) : ContentHolder(itemView) { 41 | val simpleText: TextView = itemView.findViewById(R.id.simple_text) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sample/src/main/res/color/square_number.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 21 | 22 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxxhdpi/ic_fab_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakeet/MultiType/84cd8fad89b0c6df661f32afae3b090cbf0cb0f5/sample/src/main/res/drawable-xxxhdpi/ic_fab_done.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxxhdpi/ic_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakeet/MultiType/84cd8fad89b0c6df661f32afae3b090cbf0cb0f5/sample/src/main/res/drawable-xxxhdpi/ic_right.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxxhdpi/img_00.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakeet/MultiType/84cd8fad89b0c6df661f32afae3b090cbf0cb0f5/sample/src/main/res/drawable-xxxhdpi/img_00.webp -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxxhdpi/img_01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakeet/MultiType/84cd8fad89b0c6df661f32afae3b090cbf0cb0f5/sample/src/main/res/drawable-xxxhdpi/img_01.webp -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxxhdpi/img_10.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakeet/MultiType/84cd8fad89b0c6df661f32afae3b090cbf0cb0f5/sample/src/main/res/drawable-xxxhdpi/img_10.webp -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxxhdpi/img_11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakeet/MultiType/84cd8fad89b0c6df661f32afae3b090cbf0cb0f5/sample/src/main/res/drawable-xxxhdpi/img_11.jpg -------------------------------------------------------------------------------- /sample/src/main/res/drawable/avatar_drakeet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakeet/MultiType/84cd8fad89b0c6df661f32afae3b090cbf0cb0f5/sample/src/main/res/drawable/avatar_drakeet.jpg -------------------------------------------------------------------------------- /sample/src/main/res/drawable/card.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/shadow.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/square_background.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/square_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 25 | 26 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_more_apis_playground.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 27 | 28 | 33 | 34 |