Repository: mikepenz/MaterialDrawer
Branch: develop
Commit: c6e3f78e0e0f
Files: 219
Total size: 639.4 KB
Directory structure:
gitextract_to5pn2i8/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE.md
│ ├── ci-gradle.properties
│ ├── config/
│ │ └── configuration.json
│ ├── dependabot.yml
│ └── workflows/
│ ├── ci.yml
│ └── gradle-dependency-submission.yml
├── .gitignore
├── Dangerfile
├── FAQ/
│ ├── accountheader_single_profile_without_dropdown.md
│ ├── howto_modify_add_custom_draweritems.md
│ ├── howto_use_different_sub_library_version.md
│ ├── opening-drawer-from-espresso.md
│ └── when_to_use_this_library.md
├── FAQ.md
├── Gemfile
├── LICENSE
├── MIGRATION.md
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── gradle.properties
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── mikepenz/
│ │ └── materialdrawer/
│ │ └── app/
│ │ ├── ActionBarActivity.kt
│ │ ├── AdvancedActivity.kt
│ │ ├── CollapsingToolbarActivity.kt
│ │ ├── CompactHeaderDrawerActivity.kt
│ │ ├── CrossfadeDrawerLayoutActvitiy.kt
│ │ ├── CustomApplication.kt
│ │ ├── DrawerActivity.kt
│ │ ├── EmbeddedDrawerActivity.kt
│ │ ├── FragmentActivity.kt
│ │ ├── FullscreenDrawerActivity.kt
│ │ ├── MenuDrawerActivity.kt
│ │ ├── MiniDrawerActivity.kt
│ │ ├── MultiDrawerActivity.kt
│ │ ├── NavControllerActivity.kt
│ │ ├── PersistentDrawerActivity.kt
│ │ ├── drawerItems/
│ │ │ ├── AccountDividerDrawerItem.kt
│ │ │ ├── CustomBaseViewHolder.kt
│ │ │ ├── CustomCenteredPrimaryDrawerItem.kt
│ │ │ ├── CustomPrimaryDrawerItem.kt
│ │ │ ├── CustomUrlBasePrimaryDrawerItem.kt
│ │ │ ├── CustomUrlPrimaryDrawerItem.kt
│ │ │ ├── GmailDrawerItem.kt
│ │ │ ├── IconDrawerItem.kt
│ │ │ └── OverflowMenuDrawerItem.kt
│ │ ├── fragment/
│ │ │ ├── DemoFragment.kt
│ │ │ ├── DemoMessageFragment.kt
│ │ │ ├── DrawerFragment.kt
│ │ │ └── SecondDrawerFragment.kt
│ │ ├── utils/
│ │ │ ├── CrossfadeWrapper.kt
│ │ │ ├── SystemUtils.kt
│ │ │ └── UIUtils.kt
│ │ └── widget/
│ │ └── CrossfadeDrawerLayout.kt
│ └── res/
│ ├── layout/
│ │ ├── activity_embedded.xml
│ │ ├── activity_mini_drawer.xml
│ │ ├── activity_multi_sample.xml
│ │ ├── activity_persistent_drawer.xml
│ │ ├── activity_sample.xml
│ │ ├── activity_sample_actionbar.xml
│ │ ├── activity_sample_collapsing_toolbar.xml
│ │ ├── activity_sample_crossfader.xml
│ │ ├── activity_sample_fragment.xml
│ │ ├── activity_sample_fullscreen.xml
│ │ ├── activity_sample_nav.xml
│ │ ├── footer.xml
│ │ ├── fragment_message_sample.xml
│ │ ├── fragment_sample.xml
│ │ ├── fragment_simple_sample.xml
│ │ ├── header.xml
│ │ ├── material_drawer_compact_persistent_header.xml
│ │ ├── material_drawer_item_icon_only.xml
│ │ ├── material_drawer_item_overflow_menu_primary.xml
│ │ └── material_drawer_item_primary_centered.xml
│ ├── menu/
│ │ ├── cab.xml
│ │ ├── embedded.xml
│ │ ├── example_menu.xml
│ │ ├── fragment_menu.xml
│ │ └── main.xml
│ ├── mipmap-anydpi-v26/
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ ├── navigation/
│ │ └── navigation.xml
│ └── values/
│ ├── colors.xml
│ ├── dimens.xml
│ ├── ic_launcher_background.xml
│ ├── ids.xml
│ ├── strings.xml
│ ├── styles.xml
│ └── themes.xml
├── build.gradle.kts
├── gradle/
│ ├── libs.versions.toml
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── materialdrawer/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── gradle.properties
│ ├── proguard-rules.txt
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── mikepenz/
│ │ └── materialdrawer/
│ │ ├── holder/
│ │ │ ├── BadgeStyle.kt
│ │ │ ├── ColorHolder.kt
│ │ │ ├── DimenHolder.kt
│ │ │ ├── ImageHolder.kt
│ │ │ └── StringHolder.kt
│ │ ├── interfaces/
│ │ │ ├── ICrossfader.kt
│ │ │ ├── OnCheckedChangeListener.kt
│ │ │ └── OnPostBindViewListener.kt
│ │ ├── model/
│ │ │ ├── AbstractBadgeableDrawerItem.kt
│ │ │ ├── AbstractDrawerItem.kt
│ │ │ ├── AbstractSwitchableDrawerItem.kt
│ │ │ ├── AbstractToggleableDrawerItem.kt
│ │ │ ├── BaseDescribeableDrawerItem.kt
│ │ │ ├── BaseDrawerItem.kt
│ │ │ ├── BaseViewHolder.kt
│ │ │ ├── ContainerDrawerItem.kt
│ │ │ ├── DividerDrawerItem.kt
│ │ │ ├── ExpandableBadgeDrawerItem.kt
│ │ │ ├── ExpandableDrawerItem.kt
│ │ │ ├── MiniDrawerItem.kt
│ │ │ ├── MiniProfileDrawerItem.kt
│ │ │ ├── PrimaryDrawerItem.kt
│ │ │ ├── ProfileDrawerItem.kt
│ │ │ ├── ProfileSettingDrawerItem.kt
│ │ │ ├── SecondaryDrawerItem.kt
│ │ │ ├── SecondarySwitchDrawerItem.kt
│ │ │ ├── SecondaryToggleDrawerItem.kt
│ │ │ ├── SectionDrawerItem.kt
│ │ │ ├── SwitchDrawerItem.kt
│ │ │ ├── ToggleDrawerItem.kt
│ │ │ ├── interfaces/
│ │ │ │ ├── Badgeable.kt
│ │ │ │ ├── Checkable.kt
│ │ │ │ ├── ColorfulBadgeable.kt
│ │ │ │ ├── Describable.kt
│ │ │ │ ├── DescribableColor.kt
│ │ │ │ ├── IDrawerItem.kt
│ │ │ │ ├── IProfile.kt
│ │ │ │ ├── Iconable.kt
│ │ │ │ ├── Nameable.kt
│ │ │ │ ├── NameableColor.kt
│ │ │ │ ├── SelectIconable.kt
│ │ │ │ ├── Selectable.kt
│ │ │ │ ├── SelectableColor.kt
│ │ │ │ ├── Tagable.kt
│ │ │ │ └── Typefaceable.kt
│ │ │ └── utils/
│ │ │ ├── BadgeDrawableBuilder.kt
│ │ │ └── DrawerItemExtensions.kt
│ │ ├── util/
│ │ │ ├── AbstractDrawerImageLoader.kt
│ │ │ ├── DrawerImageLoader.kt
│ │ │ ├── DrawerItemViewHelper.kt
│ │ │ ├── DrawerUtils.kt
│ │ │ ├── Extensions.kt
│ │ │ ├── FixStateListDrawable.kt
│ │ │ ├── MaterialDrawerSliderViewExtensions.kt
│ │ │ ├── MenuDrawerUtils.kt
│ │ │ └── Utils.kt
│ │ ├── view/
│ │ │ └── BezelImageView.kt
│ │ └── widget/
│ │ ├── AccountHeaderView.kt
│ │ ├── MaterialDrawerSliderView.kt
│ │ └── MiniDrawerSliderView.kt
│ └── res/
│ ├── color/
│ │ └── color_drawer_item_text.xml
│ ├── drawable/
│ │ ├── material_drawer_badge.xml
│ │ ├── material_drawer_circle_mask.xml
│ │ ├── material_drawer_ico_account_layer.xml
│ │ ├── material_drawer_ico_chevron_down.xml
│ │ ├── material_drawer_ico_menu_down.xml
│ │ ├── material_drawer_rectangle_mask.xml
│ │ ├── material_drawer_shadow_bottom.xml
│ │ └── material_drawer_shadow_top.xml
│ ├── drawable-v21/
│ │ └── material_drawer_ico_account.xml
│ ├── layout/
│ │ ├── material_drawer.xml
│ │ ├── material_drawer_compact_header.xml
│ │ ├── material_drawer_fits_not.xml
│ │ ├── material_drawer_header.xml
│ │ ├── material_drawer_inner_shadow.xml
│ │ ├── material_drawer_item_container.xml
│ │ ├── material_drawer_item_divider.xml
│ │ ├── material_drawer_item_expandable.xml
│ │ ├── material_drawer_item_expandable_badge.xml
│ │ ├── material_drawer_item_mini.xml
│ │ ├── material_drawer_item_mini_profile.xml
│ │ ├── material_drawer_item_primary.xml
│ │ ├── material_drawer_item_profile.xml
│ │ ├── material_drawer_item_profile_setting.xml
│ │ ├── material_drawer_item_secondary.xml
│ │ ├── material_drawer_item_secondary_switch.xml
│ │ ├── material_drawer_item_secondary_toggle.xml
│ │ ├── material_drawer_item_section.xml
│ │ ├── material_drawer_item_switch.xml
│ │ ├── material_drawer_item_toggle.xml
│ │ └── material_drawer_recycler_view.xml
│ ├── values/
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── ids.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── values-fr/
│ │ └── strings.xml
│ ├── values-pt/
│ │ └── strings.xml
│ └── values-sw600dp/
│ └── dimens.xml
├── materialdrawer-iconics/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── gradle.properties
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── com/
│ └── mikepenz/
│ └── materialdrawer/
│ └── iconics/
│ ├── IconicsExtension.kt
│ └── IconicsImageHolder.kt
├── materialdrawer-nav/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── gradle.properties
│ ├── proguard-rules.pro
│ ├── proguard-rules.txt
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── com/
│ └── mikepenz/
│ └── materialdrawer/
│ ├── model/
│ │ └── NavigationDrawerItem.kt
│ └── util/
│ └── DrawerNavigationUI.kt
└── settings.gradle.kts
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [mikepenz]
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
## About this issue
- Briefly describe the issue
- How can the issue be reproduced / sample code
## Details
- [ ] Used library version
- [ ] Used support library version
- [ ] Used gradle build tools version
- [ ] Used tooling / Android Studio version
- [ ] Other used libraries, potential conflicting libraries
## Checklist
- [ ] Searched for [similar issues](https://github.com/mikepenz/MaterialDrawer/issues)
- [ ] Checked out the [sample application](https://github.com/mikepenz/MaterialDrawer/tree/develop/app)
- [ ] Read the [README](https://github.com/mikepenz/MaterialDrawer/blob/develop/README.md)
- [ ] Checked out the [CHANGELOG](https://github.com/mikepenz/MaterialDrawer/releases)
- [ ] Read the [FAQ](https://github.com/mikepenz/MaterialDrawer/blob/develop/FAQ.md)
- [ ] Checked out the [MIGRATION GUIDE](https://github.com/mikepenz/MaterialDrawer/blob/develop/MIGRATION.md)
================================================
FILE: .github/ci-gradle.properties
================================================
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.workers.max=2
org.gradle.jvmargs=-Xmx6G
org.gradle.caching=true
org.gradle.configureondemand=true
# parallel kapt
kapt.use.worker.api=true
================================================
FILE: .github/config/configuration.json
================================================
{
"categories": [
{
"title": "## 🚀 Features",
"labels": [
"feature"
]
},
{
"title": "## 🐛 Fixes",
"labels": [
"fix"
]
},
{
"title": "## 🧪 Tests",
"labels": [
"test"
]
},
{
"title": "## 💬 Other",
"labels": [
"other",
"dependencies"
]
}
]
}
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
================================================
FILE: .github/workflows/ci.yml
================================================
# Thanks to https://github.com/coil-kt/coil/blob/master/.github/workflows/ci.yml
name: CI
on:
push:
tags:
- '*'
pull_request:
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 100
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: |
11
15
17
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Validate gradle wrapper
uses: gradle/actions/wrapper-validation@v4
- name: Copy CI gradle.properties
run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties
- name: Build Debug
run: ./gradlew clean app:assembleDebug
- name: Run Lint
if: github.event_name == 'pull_request'
run: ./gradlew lintDebug
- name: Setup Ruby
if: github.event_name == 'pull_request'
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
bundler-cache: true
- name: Run Danger
if: github.event_name == 'pull_request'
run: |
gem install danger
bundle exec danger --dangerfile=Dangerfile --danger_id=danger-pr
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare Keystore and Local.
if: startsWith(github.ref, 'refs/tags/')
run: |
echo "${{ secrets.KEYSTORE }}" > opensource.jks.asc
gpg -d --passphrase "${{ secrets.KEYSTORE_PASSPHRASE }}" --batch "opensource.jks.asc" > "app/opensource.jks"
- name: Build Release App
if: startsWith(github.ref, 'refs/tags/')
run: ./gradlew app:assembleRelease app:bundleRelease -P"com.mikepenz.android.signing.enabled"="true" -P"com.mikepenz.android.signing.storeFile"="opensource.jks" -P"com.mikepenz.android.signing.storePassword"="${{ secrets.STORE_PASSWORD }}" -P"com.mikepenz.android.signing.keyAlias"="${{ secrets.KEY_ALIAS }}" -P"com.mikepenz.android.signing.keyPassword"="${{ secrets.KEY_PASSWORD }}"
- name: Relase Sonatype
if: startsWith(github.ref, 'refs/tags/')
run: |
./gradlew build -x test -x lint
./gradlew materialdrawer:publishAllPublicationsToMavenCentralRepository -x test -x lint -Plibrary_only --no-configure-on-demand --no-parallel
./gradlew materialdrawer-nav:publishAllPublicationsToMavenCentralRepository -x test -x lint -Plibrary_nav_only --no-configure-on-demand --no-parallel
./gradlew materialdrawer-iconics:publishAllPublicationsToMavenCentralRepository -x test -x lint -Plibrary_iconics_only --no-configure-on-demand --no-parallel
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.NEXUS_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.NEXUS_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.SIGNING_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_PRIVATE_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}
- name: Collect artifacts
run: |
COLLECT_PWD=${PWD}
mkdir -p "artifacts"
find . -name "*.apk" -type f -exec cp {} "artifacts" \;
find . -name "*.aab" -type f -exec cp {} "artifacts" \;
- name: Archive Artifacts
uses: actions/upload-artifact@v7
with:
name: "App-Artifacts"
path: artifacts/*
- name: Build Changelog
id: github_release
uses: mikepenz/release-changelog-builder-action@v6
if: startsWith(github.ref, 'refs/tags/')
with:
configuration: ".github/config/configuration.json"
ignorePreReleases: ${{ !contains(github.ref, '-') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Release
uses: mikepenz/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
body: ${{steps.github_release.outputs.changelog}}
prerelease: ${{ contains(github.ref, '-rc') || contains(github.ref, '-b') || contains(github.ref, '-a') }}
files: artifacts/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/gradle-dependency-submission.yml
================================================
name: Gradle Dependency Submission
on:
push:
branches:
- develop
jobs:
gradle-dependency-detection:
runs-on: ubuntu-latest
# The Dependency Submission API requires write permission
permissions:
contents: write
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v6
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: |
11
15
17
- name: Copy CI gradle.properties
run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties
- name: Checkout Gradle Build Cache
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
!~/.gradle/wrapper/dists/**/gradle*.zip
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
restore-keys: |
gradle-${{ runner.os }}-
- name: Submit Dependency Graph
uses: mikepenz/gradle-dependency-submission@main
with:
gradle-build-module: :app
gradle-build-configuration: debugCompileClasspath
sub-module-mode: INDIVIDUAL_DEEP
================================================
FILE: .gitignore
================================================
# Built application files
*.apk
*.ap_
# Files for the Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
# Gradle files
.gradle/
build/
/*/build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
#IntelliJ files
*.iml
.idea/
functiongraphic.psd
/.kotlin
================================================
FILE: Dangerfile
================================================
github.dismiss_out_of_range_messages
# Make it more obvious that a PR is a work in progress and shouldn't be merged yet.
has_wip_label = github.pr_labels.any? { |label| label.include? "Engineers at work" }
has_wip_title = github.pr_title.include? "[WIP]"
if has_wip_label || has_wip_title
warn("PR is marked as Work in Progress")
end
# Ensure the PR is not marked as DO NOT MERGE
fail("PR specifies label DO NOT MERGE") if github.pr_labels.any? { |label| label.include? "DO NOT MERGE" }
# Warn when there is a big PR
warn("Big PR") if git.lines_of_code > 5000
File.open("settings.gradle.kts", "r") do |file_handle|
file_handle.each_line do |setting|
if setting.include? "include"
gradleModule = setting[10, setting.length-13]
# AndroidLint
androidLintFile = String.new(gradleModule + "/build/reports/lint-results.xml")
androidLintDebugFile = String.new(gradleModule + "/build/reports/lint-results-debug.xml")
if File.file?(androidLintFile) || File.file?(androidLintDebugFile)
android_lint.skip_gradle_task = true
android_lint.severity = "Warning"
if File.file?(androidLintFile)
android_lint.report_file = androidLintFile
else
android_lint.report_file = androidLintDebugFile
end
android_lint.filtering = true
android_lint.lint(inline_mode: true)
end
# Detekt
detektFile = String.new(gradleModule + "/build/reports/detekt.xml")
if File.file?(detektFile)
kotlin_detekt.report_file = detektFile
kotlin_detekt.skip_gradle_task = true
kotlin_detekt.severity = "warning"
kotlin_detekt.filtering = true
kotlin_detekt.detekt(inline_mode: true)
end
end
end
end
================================================
FILE: FAQ/accountheader_single_profile_without_dropdown.md
================================================
# How can i use the AccountHeader with just one profile and disable the dropdown?
This can be simply achieved by adding `headerView.selectionListEnabledForSingleProfile = false` to the
`Builder` of the `AccountHeader`. This will then disable the dropdown and hide the arrow from the
header.
## Sample code
```kotlin
headerView.selectionListEnabledForSingleProfile = false
```
## Links
* https://github.com/mikepenz/MaterialDrawer/issues/615
* https://github.com/mikepenz/MaterialDrawer/issues/454
* https://github.com/mikepenz/MaterialDrawer/issues/443
* https://github.com/mikepenz/MaterialDrawer/issues/350
================================================
FILE: FAQ/howto_modify_add_custom_draweritems.md
================================================
# How do I change existing or add my own CustomDrawerItems to the MaterialDrawer?
Please head over here on how to implement a proper CustomDrawerItem:
- http://stackoverflow.com/a/32543209/325479
- http://stackoverflow.com/a/32542999/325479
Or you check out custom drawer item implementations here:
- https://github.com/mikepenz/MaterialDrawer/tree/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems
You might also find some answers here:
- https://github.com/mikepenz/MaterialDrawer/issues?utf8=%E2%9C%93&q=CustomDrawerItem
================================================
FILE: FAQ/howto_use_different_sub_library_version.md
================================================
# How do I use different versions of the sub dependencies like the support libraries?
This can be really easy achieved by providing a resolutionStrategy to your build.
## Sample code
Simple add the following to your modules build.gradle. It allows you to define whatever version you need for the sub dependencies.
```xml
configurations.all {
resolutionStrategy.force "com.android.support:support-v4:${versions.androidX}"
resolutionStrategy.force "com.android.support:appcompat-v7:${versions.androidX}"
resolutionStrategy.force "com.android.support:cardview-v7:${versions.androidX}"
resolutionStrategy.force "com.android.support:recyclerview-v7:${versions.androidX}"
resolutionStrategy.force "com.android.support:design:${versions.androidX}"
resolutionStrategy.force "com.android.support:support-annotations:${versions.androidX}"
}
```
https://github.com/mikepenz/MaterialDrawer/blob/develop/app/build.gradle
================================================
FILE: FAQ/opening-drawer-from-espresso.md
================================================
### Q: How to open the drawer from an instrumental test written with Espresso?
### A:
First, you need a add `espresso-contrib` to your project. It has the needed `DrawerActions` class.
`androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2.2'`
Then, you need to open the drawer with his `openDrawer()` method and the drawer layout ID. The generated one is `R.id.material_drawer_layout`
`onView(withId(R.id.material_drawer_layout)).perform(DrawerActions.open());`
================================================
FILE: FAQ/when_to_use_this_library.md
================================================
### Developer:
Since there is MaterialDrawer in Support Library, could you give a couple of points why to use your library instead of from Support Library one?
### Author:
Well, that really depends on the use case of the drawer in this project, and which functionalities + which customizations are needed.
The MaterialDrawer was created long before Google came up with their drawer in the design support libraries.
Basically, the one of Google is definitely great and I recommend it to being used in all cases where it is enough. I am a fan of keeping
things as simple as possible, as it will have the great effect on the user experience. So if this project only needs a very minimal drawer
implementation, with just `Main` items, no `profile` functionality, and no great flexibility (no custom items, no custom styles, ... no advanced API for doing stuff programmatically) then you should definitely consider using the one from the design library.
If you have a more advanced use case, like using the profile switcher, the profile page, custom items like different sizes, or checkboxes, etc then you might want to stay with my library.
================================================
FILE: FAQ.md
================================================
# FAQ / WIKI
This is the official **MaterialDrawer** FAQ/Wiki. People can contribute to it through pull requests.
Each question and it's answer is hosted in a separate file within the FAQ folder.
## MaterialDrawer
* [Should I use this library or Material Drawer from the Support library?](FAQ/when_to_use_this_library.md)
* [How do I make the drawer appear underneath the Toolbar?](FAQ/howto_show_drawer_under_toolbar.md)
* [How do I create a Multi-pane layout with MaterialDrawer for tablets?](FAQ/howto_show_drawer_in_tablet_multipane.md)
* [Why is there whitespace where the status bar should be while my theme is set as fullscreen ?](FAQ/status_bar_whitespace.md)
* [How do I change existing or add my own CustomDrawerItems to the MaterialDrawer?](FAQ/howto_modify_add_custom_draweritems.md)
* [How to use different dependency library versions](FAQ/howto_use_different_sub_library_version.md)
## AccountHeader
* [How can I use the AccountHeader with just one profile and disable the dropdown?](FAQ/accountheader_single_profile_without_dropdown.md)
## Testing
* [How can I open the drawer from an instrumental test written with Espresso?](FAQ/opening-drawer-from-espresso.md)
## CustomDrawerItem's
* [A custom SecondaryDrawerItem that takes different name when its on disabled state](https://gist.github.com/AngleV/400377184386193c985d905bd97f2d40)
## Asked super frequently
### How can i create a drawer without a default selection
```kotlin
//just set the selection to -1
slider.setSelectionAtPosition(-1)
```
### Can I lock the Drawer
As the MaterialDrawer will just create a normal DrawerLayout (with some magic around it) everything a normal
DrawerLayout can do is also available in the MaterialDrawer.
```kotlin
drawerLayout.setDrawerLockMode(int lockMode); //or (int lockMode, int edgeGravity)
```
================================================
FILE: Gemfile
================================================
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gem 'danger'
gem 'danger-android_lint'
gem 'danger-kotlin_detekt'
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2021 Mike Penz
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: MIGRATION.md
================================================
### Upgrade Notes
#### v8.0.0
- Ground up refactor of the way the `MaterialDrawer` is used and integrated into the app.
- Please consider carefully before upgrading to this version.
- Prior to v8 the `Drawer` would automatically inject the `DrawerLayout` into the layout hierarchy, apply window flags, and take over control of the `ActionBarDrawerToggle`.
- This may seem convenient for easy usecases, but created big problems for more advanced implementations where taking over window insets is expected
- v8 will no longer do any of this, and gives back all the control to the developer, no more unexpected layout flags, changes to the layout hierarchy or anything similar.
- The core principle behind v8 is to offer just the UI and give back all control to developers.
- Additionally v8 eliminates dependencies on `Materialize`, `Android-Iconics`
- v8 also now comes with better theming support and better dark mode support
- As v8 is ground up different in the way it is set up it is recommended to re-read the README and check out the sample again
- Basic upgrade procedure:
- Add `DrawerLayout` into your layout
- Add `MaterialDrawerSliderView` as child to the `DrawerLayout`
- Find the reference to the `MaterialDrawerSliderView` in your `Activity` / `Fragment`
- Use the `MaterialDrawerSliderView` to fill the list / do updates
- Basic upgrade prodedure for the `AccountHeader`:
- Create an instance of the `AccountHeaderView`
- Attach to the slider via `attachToSliderView(slider)`
- Additionally v8 is more optimized for Kotlin meaning all legacy `with(*)` methods were replaced (kept as extension functions as legacy support) with properties
##### Note
- Please report if additional upgrade notes are required
#### v7.0.0
Now library is kotlin-first.
* this release contains a big amount of changes, including many breaking API changes to make its implementation easier, and make the APIs better compatible with kotlin.
* please note that the interface for items changed in the `FastAdapter` as such migrating to the new version will require more effort.
* Update `FastAdapter` to v4 and make all relevant adjustments to the provided `DrawerItem`s
* Check the `FastAdapter` changelog and [migration guide](https://github.com/mikepenz/FastAdapter/blob/develop/MIGRATION.md)
* Update `Android-Iconics` to v4
* Ensure to update `Android-Iconics` for your project, and use the updated kotlin icon dependencies
* See the migration notes for `Android-Iconics` if you run into problems
* The general interfaces and everything stayed the same, and mainly everything was migrated to kotlin
If you have any issues during the migration, or any questions come up please open a github issue so we can improve the migration guide or the documentation.
#### v6.1.1
* Further adjustments for the theme to properly meet the new material 2 design guidelines.
```xml
- @color/material_drawer_header_selection_subtext
```
#### v6.1.0-rc01.2
* With the introduction of the material 2 design behaviour, new theme attributes were added.
```xml
- @color/material_drawer_selected
- true
```
* Reworked the header views to be a lot more simple by using a `ConstraintLayout`
* Any previously custom headers require to be adjusted to the new structure. (The statusbar `Guideline` is required, for example)
* The viewHolder.item has no longer the item itself as tag directly. It is now defined with an id `R.id.material_drawer_item`. `ViewHolder.itemView.getTag(R.id.material_drawer_item)` will now return the `IDrawerItem`.
#### v6.1.0-rc01
* Final upgrade to the new shiny androidX dependencies :)
#### v6.0.3
**IMPORTANT IF YOU USE THE FASTADAPTER OR ABOUTLIBRARIES**
* You have to update your FastAdapter dependency to v3.2.1 with this release
* See the MIGRATION information of the FastAdapter https://github.com/mikepenz/FastAdapter/blob/develop/MIGRATION.md
#### v6.0.0
**IMPORTANT IF YOU USE THE FASTADAPTER OR ABOUTLIBRARIES**
* You have to update your FastAdapter dependency to v3.0.0 with this release
* See the MIGRATION information of the FastAdapter https://github.com/mikepenz/FastAdapter/blob/develop/MIGRATION.md
#### v5.9.0 & v5.9.2
**IMPORTANT IF YOU USE THE FASTADAPTER OR ABOUTLIBRARIES**
* You have to update your FastAdapter dependency to v2.5.0 with this release
* See the MIGRATION information of the FastAdapter https://github.com/mikepenz/FastAdapter/blob/develop/MIGRATION.md
#### v5.8.0
**IMPORTANT IF YOU USE THE FASTADAPTER OR ABOUTLIBRARIES**
* You have to update your FastAdapter dependency to v2.1.0 with this release
* See the MIGRATION information of the FastAdapter https://github.com/mikepenz/FastAdapter/blob/develop/MIGRATION.md
#### v5.7.0
**IMPORTANT IF YOU IMPLEMENT CUSTOM-DRAWER-ITEMS OR USE THE FASTADAPTER**
* You have to update your `FastAdapter` dependency to v2.0.0 with this release
* If you have `CustomDrawerItem`'s not based on the `AbstractDrawerITems` make sure you implement the `unbindView` method, and the new required methods
* See the MIGRATION information of the **FastAdapter** https://github.com/mikepenz/FastAdapter/blob/develop/MIGRATION.md
#### v5.6.0
**IMPORTANT IF YOU IMPLEMENT CUSTOM-DRAWER-ITEMS OR USE THE FASTADAPTER**
* This release brings a breaking interface change. Your items now have to implement `bindView(ViewHolder holder, List payloads)` instead of `bindView(VH holder)`.
* The additional payload can be used to implement a more performant view updating when only parts of the item have changed. Please also refer to the `DiffUtils` which may provide the payload.
#### v5.5.1
* add `void set(ImageView imageView, Uri uri, Drawable placeholder, String tag);` to `IDrawerImageLoader` interface, similar to the `tag` provided in the placeholder method
#### v5.5.0
* **Dropping support for API < 14. New MinSdkVersion is 14**
#### v5.3.3 -> 5.3.4
* If you use the `FastAdapter` please read the upgrade notes for v1.6.0 (https://github.com/mikepenz/FastAdapter/releases/tag/v1.6.0)
#### v5.3.1 -> v5.3.2
* the `withOnMiniDrawerItemClickListener` was renamed to `withOnMiniDrawerItemOnClickListener`
* added new separate `OnMiniDrawerItemClickListener` which allows to hook into the default behavior, and prevent it if necessary
* NOTE: this one now uses the `withOnMiniDrawerItemClickListener` method.
#### v5.2.0 -> v5.2.1
* the `SecondaryDrawerItem` is now a subclass of the `PrimaryDrawerItem` (extends `PrimaryDrawerItem`). If you have an `if` which checks for the type with `instanceOf` make sure you check for the `SecondaryDrawerItem` first. (`secondaryDrawerItem instanceOf PrimaryDrawerItem == true`)
#### v5.1.6 -> 5.1.8
* if you use the `FastAdapter` please check out the release notes of v1.4.0 (https://github.com/mikepenz/FastAdapter/releases/tag/v1.4.0)
#### v5.0.0 -> 5.0.5
* the `expanding` functionality is now handled by the `FastAdapter` so the toggling code is no longer needed. See the following diff for the change (just the `DrawerActivity`) https://github.com/mikepenz/MaterialDrawer/commit/88e9bdf8cccaac5aaf567ac6ffe682aeccba4f29
#### v4.6.0 -> v5.0.0
* the identifier was changed from `int` to `long` as the internal adapter (FastAdapter) uses `long` to identify items (as the `Adapter` does)
* v5.0.0 no longer sets the `FULL_SCREEN` flag to get the drawer below the `StatusBar` it now uses the `fitsSystemWindows` everywhere. This should improve compatiblity with a lot of things like the `CoordinatorLayout` and should also improve compatiblity with future Android updates
* removed the following methods:
* DrawerUIUtils.getScreenWidth -> moved to UIUtils from the `Materialize` library
* DrawerBuilder.withTranslucentStatusBarProgrammatically -> no longer necessary as we now depend on the `fitsSystemWindows` flag
* `StatusBarColor` can now be set via the `Drawer.getDrawerLayout().setStatusBarBackgroundColor(color)`
* DrawerBuilder.keyboardSupportEnabled -> `KeyboardUtil` should no longer be necessary
* `StatusBar` on **API < 21** is no longer colored, because of the changed way how we display the `Drawer` under the `StatusBar`
* `DrawerItems` changed. Please take a look at the `CustomDrawerItems` from the sample or the default ones, to add the changes to your `CustomDrawerItems`
* ...
#### v4.5.9 -> v4.6.0
* it is now possible to let the `Drawer` manage the `MiniDrawer`. Enable this via `withGenerateMiniDrawer(true)`. Afterwards remove the `MiniDrawer` calls inside the listeners, those are now done within the `Drawer`. You can get the `MiniDrawer` result object via `Drawer.getMiniDrawer();`
#### v4.3.7 -> v4.4.3
* added new method `withHeaderPadding` to the drawer and `withPaddingBelowHeader` to the header to control the padding separately from the `divider`which can be controlled via `withHeaderDivider`
#### v4.3.7
* depends on the latest `v23.1.0` **support libraries**. Those also require you to have `compileSDKVersion 23`
#### v4.2.0 -> v4.3.0
* new `placeholder(Context ctx, String tag)` to the `IDrawerImageLoader` interface
* new `AbstractDrawerImageLoader` to simplify the `DrawerImageLoader` usage. See the new implementation in the `CustomApplication`
* to keep the old behavior just change from `new DrawerImageLoader.IDrawerImageLoader() {` to `new AbstractDrawerImageLoader() {` for the `DrawerImageLoader.init`
* add new `tag` to the placeholder, to be able to define different placeholders for different targets
#### v4.2.0
* no more need to define an identifier for the items, they get one automatically. if you do not have logics which require you to do so, you are safe to forget about the identifier now.
#### v4.0.2 -> v4.0.7
* renamed `setDivider()` to `withDivider`
* remove `setTypeface()` use `withTypeface()` instead
#### v4.0.0 -> v4.0.2
* `getCurrentSelection()` will now return the `identifier` of the current selection or `null`
* `getCurrentSelectedPosition()` was added
* renamed all `*Footer*` methods to `*StickyFooter*` to prevent confusion
#### < v4.0.0
##### Common changes
* depends on the latest `v23` **support libraries**. Those also require you to have `compileSDKVersion 23`
* change the `onItemClick` listener to `onItemClick(View view, int i, IDrawerItem iDrawerItem)`
* modify the import of the `AccountHeader` and `AccountHeaderBuilder` to
```gradle
import com.mikepenz.materialdrawer.AccountHeader
import com.mikepenz.materialdrawer.AccountHeaderBuilder
```
* the `identifier` should now be set for the `DrawerItems` as it is used now as default for all update/modify/.. actions
* rename `withCheckable()` to `withSelectable()`
* rename `set*` methods of the `DrawerItems` to `with*` methods as those were renamed
* rename all methods like `setSelection`, `setFooterSelection`, `removeItem`, ... to `*ByPosition` (added the **ByPosition**)
* rename all methods like `setSelectionByIdentifier`, `setFooterSelectionByIdentifier`, ... to `setSelection`, `setFooterSelection` (removed the **ByIdentifier**)
* change `updateName`, `updateIcon`, `updateBadge` those methods take now an `identifier` and the specific `Holder` object
* all `get*` methods of the `DrawerItems` will now return a `Holder` object for the specific type, making it easier to work with types like `String`, `StringRes`, `Color`, `ColorRes`, `ColorInt`, ..
##### Android-Iconics (icon font)
* the MaterialDrawer now only includes the `core` of the Android-Iconics project
* add the fonts you use https://github.com/mikepenz/Android-Iconics#2-choose-your-desired-fonts
* pre MaterialDrawer v4.0.0 following fonts were included
```gradle
compile 'com.mikepenz:google-material-typeface:1.2.0.1@aar' //Google Material Design Icons
compile 'com.mikepenz:fontawesome-typeface:4.4.0.1@aar' //FontAwesome **NOTE:** the packagename changed for this font
```
##### Advanced usage changes
* changed the `ListView` to a `RecyclerView`
* rename methods with `*ListView*` to `*RecyclerView*`
* the `IDrawerItem` interface was extended to better reflect a `RecyclerView` and to improve performance
* added an `AbstractDrawerItem` to implement some common methods
* see the [SectionDrawerItem](https://github.com/mikepenz/MaterialDrawer/blob/feature/refactoring/library/src/main/java/com/mikepenz/materialdrawer/model/SectionDrawerItem.java) for an easy example
================================================
FILE: README.md
================================================
# MaterialDrawer
... the flexible, easy to use, all in one drawer library for your Android project.
-------
What's included 🚀 •
Setup 🛠️ •
Migration Guide 🧬 •
WIKI / FAQ 📖 •
Used by •
Sample App
-------
### What's included 🚀
- **the easiest possible integration**
- **uses the androidX support libraries**
- compatible down to **API Level 16**
- includes an **AccountSwitcher**
- quick and simple api
- follows the **NEW Google Material Design Guidelines**
- use **vector** (.svg) icons and **icon fonts** via the [Android-Iconics](https://github.com/mikepenz/Android-Iconics) integration
- **Google Material Design Icons**, Google **Material Community** Design Icons, FontAwesome and more
- comes with various **themes** which help to get your own themes clean
- modify the colors on the go
- comes with multiple default drawer items
- based on a **RecyclerView**
- **RTL** support
- Gmail like **MiniDrawer**
- expandable items
- **badge** support
- define custom drawer items
- tested and **stable**
- sticky footer or headers
- **absolutely NO limits**
- NavController support by @petretiandrea
> If you upgrade from < 8.0.0 follow the [MIGRATION GUIDE](https://github.com/mikepenz/MaterialDrawer/blob/develop/MIGRATION.md)
# Preview
## Screenshots 🎉

# Setup
## Latest releases 🛠
- Kotlin && M3 && JVM
17 | [v10.0.0-b01](https://github.com/mikepenz/MaterialDrawer/tree/v10.0.0-b01)
- Kotlin && Material 3 | [v9.0.2](https://github.com/mikepenz/MaterialDrawer/tree/v9.0.2)
- Kotlin | [v8.4.5](https://github.com/mikepenz/MaterialDrawer/tree/v8.4.5) (Provided as-is only)
- Java && AndroidX | [v6.1.2](https://github.com/mikepenz/MaterialDrawer/tree/v6.1.2) (Provided as-is only)
- Java && AppCompat | [v6.0.9](https://github.com/mikepenz/MaterialDrawer/tree/v6.0.9) (Provided as-is only)
### 1. Provide the gradle dependency
The latest release is available
on [Maven Central](https://search.maven.org/artifact/com.mikepenz/materialdrawer/9.0.1/aar).
```gradle
implementation("com.mikepenz:materialdrawer:${latestRelease}")
```
```gradle
//required support lib modules
implementation "androidx.appcompat:appcompat:${versions.appcompat}"
implementation "androidx.recyclerview:recyclerview:${versions.recyclerView}"
implementation "androidx.annotation:annotation:${versions.annotation}"
implementation "com.google.android.material:material:1.5.0-alpha05" // requires at least 1.5.0-x
implementation "androidx.constraintlayout:constraintlayout:${versions.constraintLayout}"
```
[NavController Support @ Maven Central](https://search.maven.org/artifact/com.mikepenz/materialdrawer-nav/9.0.1/aar).
```gradle
// Add for NavController support
implementation "com.mikepenz:materialdrawer-nav:${lastestMaterialDrawerRelease}"
```
[Android-Iconics Support @ Maven Central](https://search.maven.org/artifact/com.mikepenz/materialdrawer-iconics/9.0.1/aar)
.
```gradle
// Add for Android-Iconics support
implementation "com.mikepenz:materialdrawer-iconics:${lastestMaterialDrawerRelease}"
```
### 2. Add the `Drawer` into the XML
The `MaterialDrawerSliderView` has to be provided as child of the `DrawerLayout` and will as such act as the slider
```kotlin
... your content ...
```
### 3. Add the `DrawerStyle` to your theme
```xml
```
Great. Your drawer is now ready to use.
### Note
> Using v9.x with Material 3 theming requires a `Material3` theme as base for the activity.
# Additional Setup
### Add items and adding some functionality
```kotlin
//if you want to update the items at a later time it is recommended to keep it in a variable
val item1 = PrimaryDrawerItem().apply { nameRes = R.string.drawer_item_home; identifier = 1 }
val item2 = SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_settings; identifier = 2 }
// get the reference to the slider and add the items
slider.itemAdapter.add(
item1,
DividerDrawerItem(),
item2,
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_settings }
)
// specify a click listener
slider.onDrawerItemClickListener = { v, drawerItem, position ->
// do something with the clicked item :D
false
}
```
### Selecting an item
```kotlin
//set the selection to the item with the identifier 1
slider.setSelection(1)
//set the selection to the item with the identifier 2
slider.setSelection(item2)
//set the selection and also fire the `onItemClick`-listener
slider.setSelection(1, true)
```
By default, when a drawer item is clicked, it becomes the new selected item. If this isn't the expected behavior,
you can disable it for this item using `isSelectable = false`:
```kotlin
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_dialog; isSelectable = false }
```
### Modify items or the drawer
```kotlin
//modify an item of the drawer
item1.apply {
nameText = "A new name for this drawerItem"; badge = StringHolder("19")
badgeStyle = BadgeStyle().apply { textColor = ColorHolder.fromColor(Color.WHITE); color = ColorHolder.fromColorRes(R.color.md_red_700) }
}
//notify the drawer about the updated element. it will take care about everything else
slider.updateItem(item1)
//to update only the name, badge, icon you can also use one of the quick methods
slider.updateName(1, "A new name")
//the result object also allows you to add new items, remove items, add footer, sticky footer, ..
slider.addItem(DividerDrawerItem())
slider.addStickyFooterItem(PrimaryDrawerItem().apply { nameTest = "StickyFooter" })
//remove items with an identifier
slider.removeItem(2)
//open / close the drawer
slider.drawerLayout?.openDrawer(slider)
slider.drawerLayout?.closeDrawer(slider)
//get the reference to the `DrawerLayout` itself
slider.drawerLayout
```
### Add profiles and an AccountHeader
```kotlin
// Create the AccountHeader
headerView = AccountHeaderView(this).apply {
attachToSliderView(slider) // attach to the slider
addProfiles(
ProfileDrawerItem().apply { nameText = "Mike Penz"; descriptionText = "mikepenz@gmail.com"; iconRes = R.drawable.profile; identifier = 102 }
)
onAccountHeaderListener = { view, profile, current ->
// react to profile changes
false
}
withSavedInstance(savedInstanceState)
}
```
### Android-Iconics support
The MaterialDrawer provides an extension for the [Android-Iconics](https://github.com/mikepenz/Android-Iconics) library. This allows you to create your `DrawerItems` with an icon from any font.
Choose the fonts you need. [Available Fonts](https://github.com/mikepenz/Android-Iconics#2-choose-your-desired-fonts)
```gradle
// Add for Android-Iconics support
implementation "com.mikepenz:materialdrawer-iconics:${lastestMaterialDrawerRelease}"
// fonts
implementation 'com.mikepenz:google-material-typeface:x.y.z@aar' //Google Material Icons
implementation 'com.mikepenz:fontawesome-typeface:x.y.z@aar' //FontAwesome
```
```kotlin
//now you can simply use any icon of the Google Material Icons font
PrimaryDrawerItem().apply { iconicsIcon = GoogleMaterial.Icon.gmd_wb_sunny }
//Or an icon from FontAwesome
SecondaryDrawerItem().apply { iconicsIcon = FontAwesomeBrand.Icon.fab_github }
```
# Advanced Setup
For advanced usecases. Please have a look at the provided sample activities.
## Load images via url
The MaterialDrawer supports fetching images from URLs and setting them for the Profile icons. As the MaterialDrawer does not contain an ImageLoading library
the dev can choose his own implementation (Picasso, Glide, ...). This has to be done, before the first image should be loaded via URL. (Should be done in the Application, but any other spot before loading the first image is working too)
* SAMPLE using [PICASSO](https://github.com/square/picasso)
* [SAMPLE](https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/CustomApplication.kt) using [GLIDE](https://github.com/bumptech/glide)
```kotlin
//initialize and create the image loader logic
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable) {
Picasso.get().load(uri).placeholder(placeholder).into(imageView)
}
override fun cancel(imageView: ImageView) {
Picasso.get().cancelRequest(imageView)
}
/*
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) {
super.set(imageView, uri, placeholder, tag)
}
override fun placeholder(ctx: Context): Drawable {
return super.placeholder(ctx)
}
override fun placeholder(ctx: Context, tag: String?): Drawable {
return super.placeholder(ctx, tag)
}
*/
})
```
An implementation with [GLIDE v4](https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/CustomApplication.kt) (See tag v6.1.1 for glide v3 sample) can be found in the sample application
## JVM Target 1.8
```
// Since 8.1.0 the drawer includes core ktx 1.3.0 which requires jvm 1.8
kotlinOptions {
jvmTarget = "1.8"
}
```
## Style the drawer 🖌️
### Custom style - styles.xml
Create your custom style. If you don't need a custom theme see the next section, how you can set the colors just by overwriting the original colors.
```xml
// define a custom drawer style
// define a custom header style
// define the custom styles for the theme
```
### Adjust BezelImageView style
Overwrite the Style of the BezelImageView for the whole MaterialDrawer
```xml
```
# Used by
(feel free to send me new projects)
* [Screener](https://play.google.com/store/apps/details?id=de.toastcode.screener)
* [Meldmail](https://play.google.com/store/apps/details?id=com.meldmail)
* [Academic Schedule](https://play.google.com/store/apps/details?id=com.auebcsschedule.ppt)
* [Sprit Club](https://play.google.com/store/apps/details?id=at.idev.spritpreise)
* [StickyNotes](https://play.google.com/store/apps/details?id=com.jsvmsoft.stickynotes)
* [MLManager](https://github.com/javiersantos/MLManager)
* [Fimpl](https://play.google.com/store/apps/details?id=com.danielZET.fimpl)
* [Teacher Gradebook](https://play.google.com/store/apps/details?id=com.apolosoft.cuadernoprofesor)
* [AS Sales Management](https://play.google.com/store/apps/details?id=com.armsoft.mtrade)
* [Sporza Voetbal](http://play.google.com/store/apps/details?id=be.vrt.mobile.android.sporza.voetbal)
* [Atmosphere](https://play.google.com/store/apps/details?id=com.peakpocketstudios.atmosphere)
* [Fitness Challenge](https://play.google.com/store/apps/details?id=com.isidroid.fitchallenge)
* [I'm Reading Quran - Kur'an Okuyorum](https://play.google.com/store/apps/details?id=com.homemade.kuranokuma)
* [Makota Money Manager](https://play.google.com/store/apps/details?id=be.jatra.makota)
* [Companion for Band](https://github.com/adithya321/Companion-for-Band)
* [Recipedia](https://play.google.com/store/apps/details?id=com.md.recipedia)
* [Right Сourse - ruble course](https://play.google.com/store/apps/details?id=com.currency.work.currencychecker)
* [Gameru](https://play.google.com/store/apps/details?id=net.gameru)
* [Boost for reddit](https://play.google.com/store/apps/details?id=com.rubenmayayo.reddit)
* [Calendula](https://github.com/citiususc/calendula)
* [MyTimes](https://github.com/debo1994/MyTimes)
* [VoIP By Antisip](https://play.google.com/store/apps/details?id=com.antisip.vbyantisip)
* [MBox - One Place for Entertainment](https://play.google.com/store/apps/details?id=com.paperwrrk.android.mbox)
* [D Notes - Smart and Material Note Taking](https://play.google.com/store/apps/details?id=com.dvdb.bergnotes)
* [Moviebase](https://play.google.com/store/apps/details?id=com.moviebase)
* [MyFuelLog2](https://play.google.com/store/apps/details?id=com.acty.myfuellog2)
* [MECSol](https://play.google.com/store/apps/details?id=tk.rlta.mecsol)
* [3D Geeks: Thingiverse Browser for 3D Printing](https://play.google.com/store/apps/details?id=work.twob.threed)
* [Tusky: Mastodon Client for Android](https://github.com/tuskyapp/Tusky)
* [Tibia Live](https://tibia.space/)
* [Walkaholic](https://play.google.com/store/apps/details?id=com.walkaholic.hikeapp)
* [Pachli: Mastodon Client](https://pachli.app)
# Articles about the MaterialDrawer
* [java-help.ru - MaterialDrawer tutorial](http://java-help.ru/material-navigationdrawer/)
* [MaterialDrawer in multiple activities](https://android.jlelse.eu/android-using-navigation-drawer-across-multiple-activities-the-easiest-way-b011f152aebd)
# Credits
- Mirosław Stanek - [GitHub](https://github.com/frogermcs)
- For his InstaMaterial concept and the idea of inflating the drawerLayout [InstaMaterial Concept](http://frogermcs.github.io/InstaMaterial-concept-part-7-navigation-drawer/)
- Lunae Luman - [Behance](https://www.behance.net/gallery/18526001/Material-Wallpaper) for the Header Image
# Developed By
- Mike Penz
- [mikepenz.dev](https://mikepenz.dev) - [blog.mikepenz.dev](https://blog.mikepenz.dev) -
- [paypal.me/mikepenz](http://paypal.me/mikepenz)
- [Automatic changelog generation action](https://github.com/marketplace/actions/release-changelog-builder)
# License
Copyright 2021 Mike Penz
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle.kts
================================================
plugins {
id("com.mikepenz.convention.android-application")
id("com.mikepenz.convention.kotlin")
id("com.mikepenz.aboutlibraries.plugin")
id("androidx.navigation.safeargs.kotlin")
}
android {
namespace = "com.mikepenz.materialdrawer.app"
defaultConfig {
multiDexEnabled = true
setProperty("archivesBaseName", "MaterialDrawer-v$versionName-c$versionCode")
}
productFlavors {
}
buildFeatures {
viewBinding = true
}
packaging {
resources {
excludes.add("META-INF/library-core_release.kotlin_module")
excludes.add("META-INF/library_release.kotlin_module")
}
}
}
dependencies {
implementation(project(":materialdrawer"))
implementation(project(":materialdrawer-iconics"))
implementation(project(":materialdrawer-nav"))
implementation(libs.google.material)
implementation(libs.androidx.cardView)
implementation(libs.androidx.recyclerView)
implementation(libs.androidx.navigation.fragment)
implementation(libs.androidx.navigation.ui)
// used to showcase how to load images
implementation("io.coil-kt:coil:2.7.0")
//used to provide different itemAnimators for the RecyclerView
//https://github.com/mikepenz/ItemAnimators
implementation(libs.itemAnimators.core)
// used to provide out of the box icon font support. simplifies development,
// and provides scalable icons. the core is very very light
// https://github.com/mikepenz/Android-Iconics
implementation(libs.iconics.core)
// used to generate the Open Source section
// https://github.com/mikepenz/AboutLibraries
implementation(baseLibs.aboutlibraries.view)
// used to provide the MiniDrawer to normal Drawer crossfade effect via a SlidingPane layout
// --> https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/MiniDrawerActivity.java
// https://github.com/mikepenz/Crossfader
implementation("com.mikepenz:crossfader:1.6.0@aar")
// used to provide the two step crossfade DrawerLayout. Which allows to have a mini layout which transforms to a normal layout within the drawer
// --> https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/CrossfadeDrawerLayoutActvitiy.java
// https://github.com/mikepenz/CrossfadeDrawerLayout
implementation("com.mikepenz:crossfadedrawerlayout:1.1.0@aar")
// icon fonts used inside the sample
// https://github.com/mikepenz/Android-Iconics
implementation("com.mikepenz:google-material-typeface:4.0.0.2-kotlin@aar")
implementation("com.mikepenz:fontawesome-typeface:5.13.3.0-kotlin@aar")
implementation("com.mikepenz:octicons-typeface:11.1.0.0-kotlin@aar")
implementation("androidx.multidex:multidex:2.0.1")
implementation("androidx.slidingpanelayout:slidingpanelayout:1.1.0") {
version {
strictly("1.1.0")
}
}
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
}
configurations.configureEach {
resolutionStrategy.force(libs.fastAdapter.core)
resolutionStrategy.force(libs.iconics.core)
}
================================================
FILE: app/gradle.properties
================================================
com.mikepenz.compose.enabled=false
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Entwicklung/android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/ActionBarActivity.kt
================================================
package com.mikepenz.materialdrawer.app
import android.os.Bundle
import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.materialdrawer.app.databinding.ActivitySampleActionbarBinding
import com.mikepenz.materialdrawer.iconics.iconicsIcon
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.Nameable
import com.mikepenz.materialdrawer.model.interfaces.nameRes
class ActionBarActivity : AppCompatActivity() {
private lateinit var binding: ActivitySampleActionbarBinding
override fun onCreate(savedInstanceState: Bundle?) {
//supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
super.onCreate(savedInstanceState)
binding = ActivitySampleActionbarBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
setTitle(R.string.drawer_item_action_bar_drawer)
binding.slider.apply {
itemAdapter.add(
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_home
iconicsIcon = FontAwesome.Icon.faw_home
},
SecondaryDrawerItem().apply {
nameRes = R.string.drawer_item_settings
iconicsIcon = FontAwesome.Icon.faw_cog
}
)
onDrawerItemClickListener = { _, drawerItem, _ ->
if (drawerItem is Nameable) {
Toast.makeText(this@ActionBarActivity, (drawerItem as Nameable).name!!.getText(this@ActionBarActivity), Toast.LENGTH_SHORT).show()
}
false
}
setSavedInstance(savedInstanceState)
}
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeButtonEnabled(false)
}
override fun onSaveInstanceState(_outState: Bundle) {
//add the values which need to be saved from the drawer to the bundle
super.onSaveInstanceState(binding.slider.saveInstanceState(_outState))
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
onBackPressed()
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onBackPressed() {
//handle the back press :D close the drawer first and if the drawer is closed close the activity
if (binding.root.isDrawerOpen(binding.slider)) {
binding.root.closeDrawer(binding.slider)
} else {
super.onBackPressed()
}
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/AdvancedActivity.kt
================================================
package com.mikepenz.materialdrawer.app
import android.content.res.Configuration
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesomeBrand
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.actionBar
import com.mikepenz.iconics.utils.backgroundColorRes
import com.mikepenz.iconics.utils.paddingDp
import com.mikepenz.iconics.utils.sizeDp
import com.mikepenz.materialdrawer.app.databinding.ActivitySampleBinding
import com.mikepenz.materialdrawer.app.drawerItems.CustomPrimaryDrawerItem
import com.mikepenz.materialdrawer.app.drawerItems.CustomUrlPrimaryDrawerItem
import com.mikepenz.materialdrawer.app.drawerItems.OverflowMenuDrawerItem
import com.mikepenz.materialdrawer.holder.ColorHolder
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.iconics.iconicsIcon
import com.mikepenz.materialdrawer.model.*
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.util.addStickyDrawerItems
import com.mikepenz.materialdrawer.widget.AccountHeaderView
class AdvancedActivity : AppCompatActivity() {
private lateinit var binding: ActivitySampleBinding
private lateinit var headerView: AccountHeaderView
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
private lateinit var profile: IProfile
private lateinit var profile2: IProfile
private lateinit var profile3: IProfile
private lateinit var profile4: IProfile
private lateinit var profile5: IProfile
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySampleBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
// Handle Toolbar
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeButtonEnabled(true)
supportActionBar?.setTitle(R.string.drawer_item_advanced_drawer)
actionBarDrawerToggle = ActionBarDrawerToggle(this, binding.root, binding.toolbar, com.mikepenz.materialdrawer.R.string.material_drawer_open, com.mikepenz.materialdrawer.R.string.material_drawer_close)
// Create a few sample profile
profile = ProfileDrawerItem().apply { nameText = "Mike Penz"; descriptionText = "mikepenz@gmail.com"; iconRes = R.drawable.profile }
profile2 = ProfileDrawerItem().apply { nameText = "Max Muster"; descriptionText = "max.mustermann@gmail.com"; iconRes = R.drawable.profile2; identifier = 2 }
profile3 = ProfileDrawerItem().apply { nameText = "Felix House"; descriptionText = "felix.house@gmail.com"; iconRes = R.drawable.profile3 }
profile4 = ProfileDrawerItem().apply { nameText = "Mr. X"; descriptionText = "mister.x.super@gmail.com"; iconRes = R.drawable.profile4; identifier = 4 }
profile5 = ProfileDrawerItem().apply { nameText = "Batman"; descriptionText = "batman@gmail.com"; iconRes = R.drawable.profile5 }
// Create the AccountHeader
buildHeader(false, savedInstanceState)
binding.slider.apply {
itemAdapter.add(
PrimaryDrawerItem().apply { nameRes = R.string.drawer_item_home; iconicsIcon = FontAwesome.Icon.faw_home },
//here we use a customPrimaryDrawerItem we defined in our sample app
//this custom DrawerItem extends the PrimaryDrawerItem so it just overwrites some methods
OverflowMenuDrawerItem().apply {
nameRes = R.string.drawer_item_menu_drawer_item; descriptionRes = R.string.drawer_item_menu_drawer_item_desc; menu = R.menu.fragment_menu
onMenuItemClickListener = PopupMenu.OnMenuItemClickListener { item ->
Toast.makeText(this@AdvancedActivity, item.title, Toast.LENGTH_SHORT).show()
false
}
iconicsIcon = GoogleMaterial.Icon.gmd_filter_center_focus
},
CustomPrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_free_play; iconicsIcon = FontAwesome.Icon.faw_gamepad; background =
ColorHolder.fromColorRes(R.color.colorAccent)
},
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_custom; descriptionText = "This is a description"; iconicsIcon = FontAwesome.Icon.faw_eye
},
CustomUrlPrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_fragment_drawer; description = StringHolder(R.string.drawer_item_fragment_drawer_desc); iconUrl =
"https://avatars3.githubusercontent.com/u/1476232?v=3&s=460"
},
SectionDrawerItem().apply { nameRes = R.string.drawer_item_section_header },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_settings; iconicsIcon = FontAwesome.Icon.faw_cart_plus },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_help; iconicsIcon = FontAwesome.Icon.faw_database; isEnabled = false },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_open_source; iconicsIcon = FontAwesomeBrand.Icon.fab_github },
SecondaryDrawerItem().apply {
nameRes = R.string.drawer_item_contact; tag = "Bullhorn"; iconDrawable =
IconicsDrawable(this@AdvancedActivity, GoogleMaterial.Icon.gmd_add).apply { actionBar(); paddingDp = 5 }; isIconTinted = true
},
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_help; iconicsIcon = FontAwesome.Icon.faw_question; isEnabled = false }
)
addStickyDrawerItems(
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_settings; iconicsIcon = FontAwesome.Icon.faw_cog; identifier = 10 },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_open_source; iconicsIcon = FontAwesomeBrand.Icon.fab_github }
)
onDrawerItemClickListener = { _, drawerItem, _ ->
if (drawerItem is Nameable) {
Toast.makeText(this@AdvancedActivity, drawerItem.name?.getText(this@AdvancedActivity), Toast.LENGTH_SHORT).show()
}
false
}
setSavedInstance(savedInstanceState)
}
}
/**
* small helper method to reuse the logic to build the AccountHeader
* this will be used to replace the header of the drawer with a compact/normal header
*
* @param compact
* @param savedInstanceState
*/
private fun buildHeader(compact: Boolean, savedInstanceState: Bundle?) {
// Create the AccountHeader
headerView = AccountHeaderView(this, compact = compact).apply {
attachToSliderView(binding.slider)
headerBackground = ImageHolder(R.drawable.header)
addProfiles(
profile,
profile2,
profile3,
profile4,
profile5,
//don't ask but google uses 14dp for the add account icon in gmail but 20dp for the normal icons (like manage account)
ProfileSettingDrawerItem().apply { nameText = "Add Account"; descriptionText = "Add new GitHub Account"; iconDrawable = IconicsDrawable(this@AdvancedActivity, GoogleMaterial.Icon.gmd_add).apply { actionBar(); paddingDp = 5 }; identifier = PROFILE_SETTING.toLong() },
ProfileSettingDrawerItem().apply { nameText = "Manage Account"; iconicsIcon = GoogleMaterial.Icon.gmd_settings }
)
onAccountHeaderListener = { _, profile, _ ->
//sample usage of the onProfileChanged listener
//if the clicked item has the identifier 1 add a new profile ;)
if (profile is IDrawerItem<*> && (profile as IDrawerItem<*>).identifier == PROFILE_SETTING.toLong()) {
val newProfile = ProfileDrawerItem().apply { nameText = "Batman"; descriptionText = "batman@gmail.com"; iconRes = R.drawable.profile5; isNameShown = true }
val profiles = headerView.profiles
if (profiles != null) {
//we know that there are 2 setting elements. set the new profile above them ;)
headerView.addProfile(newProfile, profiles.size - 2)
} else {
headerView.addProfiles(newProfile)
}
}
//false if you have not consumed the event and it should close the drawer
false
}
withSavedInstance(savedInstanceState)
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
actionBarDrawerToggle.onConfigurationChanged(newConfig)
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
actionBarDrawerToggle.syncState()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle item selection
when (item.itemId) {
R.id.menu_1 -> {
//update the profile2 and set a new image.
profile2.iconDrawable = IconicsDrawable(this, GoogleMaterial.Icon.gmd_android).apply { backgroundColorRes = R.color.colorAccent; paddingDp = 4; sizeDp = 48 }
headerView.updateProfile(profile2)
return true
}
R.id.menu_4 -> {
//we want to replace our current header with a compact header
//build the new compact header
buildHeader(true, null)
return true
}
R.id.menu_5 -> {
//we want to replace our current header with a normal header
//build the new compact header
buildHeader(false, null)
binding.slider.invalidate()
return true
}
else -> {
return actionBarDrawerToggle.onOptionsItemSelected(item)
}
}
}
override fun onSaveInstanceState(_outState: Bundle) {
var outState = _outState
//add the values which need to be saved from the drawer to the bundle
outState = binding.slider.saveInstanceState(outState)
//add the values which need to be saved from the accountHeader to the bundle
outState = headerView.saveInstanceState(outState)
super.onSaveInstanceState(outState)
}
override fun onBackPressed() {
//handle the back press :D close the drawer first and if the drawer is closed close the activity
if (binding.root.isDrawerOpen(binding.slider)) {
binding.root.closeDrawer(binding.slider)
} else {
super.onBackPressed()
}
}
companion object {
private const val PROFILE_SETTING = 1
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/CollapsingToolbarActivity.kt
================================================
package com.mikepenz.materialdrawer.app
import android.graphics.Color
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import coil.load
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesomeBrand
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.actionBar
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.materialdrawer.app.databinding.ActivitySampleCollapsingToolbarBinding
import com.mikepenz.materialdrawer.app.utils.convertDpToPixel
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.iconics.iconicsIcon
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import com.mikepenz.materialdrawer.model.SectionDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.nameRes
import com.mikepenz.materialdrawer.util.removeAllItems
import com.mikepenz.materialdrawer.widget.AccountHeaderView
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
class CollapsingToolbarActivity : AppCompatActivity() {
private lateinit var binding: ActivitySampleCollapsingToolbarBinding
private lateinit var headerView: AccountHeaderView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySampleCollapsingToolbarBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
setSupportActionBar(binding.toolbar)
binding.collapsingToolbar.title = getString(R.string.drawer_item_collapsing_toolbar_drawer)
binding.materialDrawerSwipeRefresh.setProgressViewOffset(true, 0, convertDpToPixel(48f, this).toInt())
binding.materialDrawerSwipeRefresh.setOnRefreshListener {
binding.materialDrawerSwipeRefresh.postDelayed({
binding.slider.setDrawerItems()
binding.slider.setSelection(5, false)
binding.materialDrawerSwipeRefresh.isRefreshing = false
}, 3000)
}
// Create the AccountHeader
headerView = AccountHeaderView(this).apply {
attachToSliderView(binding.slider)
headerBackground = ImageHolder(R.drawable.header)
withSavedInstance(savedInstanceState)
}
binding.slider.apply {
setDrawerItems()
setSelection(1, false)
setSavedInstance(savedInstanceState)
}
fillFab()
loadBackdrop()
}
private fun MaterialDrawerSliderView.setDrawerItems() {
removeAllItems()
itemAdapter.add(
PrimaryDrawerItem().apply { nameRes = R.string.drawer_item_home; iconicsIcon = FontAwesome.Icon.faw_home; identifier = 1 },
PrimaryDrawerItem().apply { nameRes = R.string.drawer_item_free_play; iconicsIcon = FontAwesome.Icon.faw_gamepad },
PrimaryDrawerItem().apply { nameRes = R.string.drawer_item_custom; iconicsIcon = FontAwesome.Icon.faw_eye; identifier = 5 },
SectionDrawerItem().apply { nameRes = R.string.drawer_item_section_header },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_settings; iconicsIcon = FontAwesome.Icon.faw_cog },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_help; iconicsIcon = FontAwesome.Icon.faw_question; isEnabled = false },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_open_source; iconicsIcon = FontAwesomeBrand.Icon.fab_github },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_contact; iconicsIcon = FontAwesome.Icon.faw_bullhorn }
)
}
private fun loadBackdrop() {
binding.backdrop.load("https://unsplash.it/600/300/?random")
}
private fun fillFab() {
binding.floatingActionButton.setImageDrawable(IconicsDrawable(this, GoogleMaterial.Icon.gmd_favorite).apply { actionBar(); colorInt = Color.WHITE })
}
override fun onSaveInstanceState(_outState: Bundle) {
var outState = _outState
//add the values which need to be saved from the drawer to the bundle
outState = binding.slider.saveInstanceState(outState)
//add the values which need to be saved from the accountHeader to the bundle
outState = headerView.saveInstanceState(outState)
super.onSaveInstanceState(outState)
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/CompactHeaderDrawerActivity.kt
================================================
package com.mikepenz.materialdrawer.app
import android.content.Context
import android.content.res.Configuration
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.util.TypedValue
import android.view.Menu
import android.view.MenuItem
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesomeBrand
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.actionBar
import com.mikepenz.iconics.utils.paddingDp
import com.mikepenz.materialdrawer.app.databinding.ActivitySampleBinding
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.ColorHolder
import com.mikepenz.materialdrawer.iconics.iconicsIcon
import com.mikepenz.materialdrawer.iconics.withIcon
import com.mikepenz.materialdrawer.model.*
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.widget.AccountHeaderView
class CompactHeaderDrawerActivity : AppCompatActivity() {
private lateinit var binding: ActivitySampleBinding
//save our header or result
private lateinit var headerView: AccountHeaderView
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySampleBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
// Handle Toolbar
setSupportActionBar(binding.toolbar)
supportActionBar?.setTitle(R.string.drawer_item_compact_header)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeButtonEnabled(true)
actionBarDrawerToggle = ActionBarDrawerToggle(this, binding.root, binding.toolbar, com.mikepenz.materialdrawer.R.string.material_drawer_open, com.mikepenz.materialdrawer.R.string.material_drawer_close)
// Create a few sample profile
val profile = ProfileDrawerItem().apply {
nameText = "Mike Penz"; descriptionText = "mikepenz@gmail.com"; iconRes = R.drawable.profile; badgeText = "123"
badgeStyle = BadgeStyle().apply {
textColor = ColorHolder.fromColor(Color.WHITE)
color = ColorHolder.fromColorRes(R.color.colorAccent)
}
}
val profile2 = ProfileDrawerItem().apply { nameText = "Max Muster"; descriptionText = "max.mustermann@gmail.com"; iconRes = R.drawable.profile2 }
val profile3 = ProfileDrawerItem().apply { nameText = "Felix House"; descriptionText = "felix.house@gmail.com"; iconRes = R.drawable.profile3 }
val profile4 = ProfileDrawerItem().apply { nameText = "Mr. X"; descriptionText = "mister.x.super@gmail.com"; iconRes = R.drawable.profile4 }
val profile5 = ProfileDrawerItem().apply { nameText = "Batman"; descriptionText = "batman@gmail.com"; iconRes = R.drawable.profile5 }
// Create the AccountHeader
headerView = AccountHeaderView(this).apply {
attachToSliderView(binding.slider)
addProfiles(
profile,
profile2,
profile3,
profile4,
profile5,
//don't ask but google uses 14dp for the add account icon in gmail but 20dp for the normal icons (like manage account)
ProfileSettingDrawerItem().withName("Add Account").withDescription("Add new GitHub Account").withIcon(IconicsDrawable(context, GoogleMaterial.Icon.gmd_add).apply { actionBar(); paddingDp = 5 }).withIconTinted(true).withIdentifier(PROFILE_SETTING.toLong()),
ProfileSettingDrawerItem().withName("Manage Account").withIcon(GoogleMaterial.Icon.gmd_settings).withIdentifier(100001)
)
withSavedInstance(savedInstanceState)
}
binding.slider.apply {
itemAdapter.add(
PrimaryDrawerItem().apply { nameRes = R.string.drawer_item_home; iconicsIcon = FontAwesome.Icon.faw_home; identifier = 1 },
PrimaryDrawerItem().apply { nameRes = R.string.drawer_item_free_play; iconicsIcon = FontAwesome.Icon.faw_gamepad },
PrimaryDrawerItem().apply { nameRes = R.string.drawer_item_custom; iconicsIcon = FontAwesome.Icon.faw_eye; identifier = 5 },
SectionDrawerItem().apply { nameRes = R.string.drawer_item_section_header },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_settings; iconicsIcon = FontAwesome.Icon.faw_cog },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_help; iconicsIcon = FontAwesome.Icon.faw_question; isEnabled = false },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_open_source; iconicsIcon = FontAwesomeBrand.Icon.fab_github },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_contact; iconicsIcon = FontAwesome.Icon.faw_bullhorn }
)
onDrawerItemClickListener = { _, drawerItem, _ ->
if (drawerItem.identifier == 1L) {
startSupportActionMode(ActionBarCallBack())
}
if (drawerItem is Nameable) {
binding.toolbar.title = drawerItem.name?.getText(this@CompactHeaderDrawerActivity)
}
false
}
setSavedInstance(savedInstanceState)
}
// set the selection to the item with the identifier 5
if (savedInstanceState == null) {
binding.slider.setSelection(5, false)
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
actionBarDrawerToggle.onConfigurationChanged(newConfig)
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
actionBarDrawerToggle.syncState()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (actionBarDrawerToggle.onOptionsItemSelected(item)) {
return true
}
return super.onOptionsItemSelected(item)
}
override fun onSaveInstanceState(_outState: Bundle) {
var outState = _outState
//add the values which need to be saved from the drawer to the bundle
outState = binding.slider.saveInstanceState(outState)
//add the values which need to be saved from the accountHeader to the bundle
outState = headerView.saveInstanceState(outState)
super.onSaveInstanceState(outState)
}
override fun onBackPressed() {
//handle the back press :D close the drawer first and if the drawer is closed close the activity
if (binding.root.isDrawerOpen(binding.slider)) {
binding.root.closeDrawer(binding.slider)
} else {
super.onBackPressed()
}
}
internal inner class ActionBarCallBack : ActionMode.Callback {
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return false
}
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.statusBarColor =
getThemeColor(android.R.attr.colorPrimaryDark, ContextCompat.getColor(this@CompactHeaderDrawerActivity, R.color.colorPrimaryDark))
}
mode.menuInflater.inflate(R.menu.cab, menu)
return true
}
override fun onDestroyActionMode(mode: ActionMode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.statusBarColor = Color.TRANSPARENT
}
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
return false
}
private fun Context.getThemeColor(@AttrRes attr: Int, @ColorInt def: Int = 0): Int {
val tv = TypedValue()
return if (theme.resolveAttribute(attr, tv, true)) {
if (tv.resourceId != 0) ResourcesCompat.getColor(resources, tv.resourceId, theme) else tv.data
} else def
}
}
companion object {
private const val PROFILE_SETTING = 1
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/CrossfadeDrawerLayoutActvitiy.kt
================================================
package com.mikepenz.materialdrawer.app
import android.content.res.Configuration
import android.os.Bundle
import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.GravityCompat
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesomeBrand
import com.mikepenz.materialdrawer.app.databinding.ActivitySampleCrossfaderBinding
import com.mikepenz.materialdrawer.iconics.iconicsIcon
import com.mikepenz.materialdrawer.interfaces.ICrossfader
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import com.mikepenz.materialdrawer.model.SectionDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.util.getOptimalDrawerWidth
import com.mikepenz.materialdrawer.widget.AccountHeaderView
class CrossfadeDrawerLayoutActvitiy : AppCompatActivity() {
private lateinit var binding: ActivitySampleCrossfaderBinding
private lateinit var headerView: AccountHeaderView
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySampleCrossfaderBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
// Handle Toolbar
setSupportActionBar(binding.toolbar)
//set the back arrow in the toolbars
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeButtonEnabled(true)
supportActionBar?.setTitle(R.string.drawer_item_crossfade_drawer_layout_drawer)
actionBarDrawerToggle = ActionBarDrawerToggle(this, binding.root, binding.toolbar, com.mikepenz.materialdrawer.R.string.material_drawer_open, com.mikepenz.materialdrawer.R.string.material_drawer_close)
// Create a few sample profile
val profile = ProfileDrawerItem().apply { nameText = "Mike Penz"; descriptionText = "mikepenz@gmail.com"; iconRes = R.drawable.profile }
val profile2 = ProfileDrawerItem().apply { nameText = "Max Muster"; descriptionText = "max.mustermann@gmail.com"; iconRes = R.drawable.profile2 }
val profile3 = ProfileDrawerItem().apply { nameText = "Felix House"; descriptionText = "felix.house@gmail.com"; iconRes = R.drawable.profile3 }
val profile4 = ProfileDrawerItem().apply { nameText = "Mr. X"; descriptionText = "mister.x.super@gmail.com"; iconRes = R.drawable.profile4 }
val profile5 = ProfileDrawerItem().apply { nameText = "Batman"; descriptionText = "batman@gmail.com"; iconRes = R.drawable.profile5 }
// Create the AccountHeader
headerView = AccountHeaderView(this).apply {
attachToSliderView(binding.crossFadeLargeView)
addProfiles(
profile,
profile2,
profile3,
profile4,
profile5
)
withSavedInstance(savedInstanceState)
}
binding.crossFadeLargeView.apply {
itemAdapter.add(
PrimaryDrawerItem().apply { nameRes = R.string.drawer_item_home; iconicsIcon = FontAwesome.Icon.faw_home; identifier = 1 },
PrimaryDrawerItem().apply { nameRes = R.string.drawer_item_free_play; iconicsIcon = FontAwesome.Icon.faw_gamepad },
PrimaryDrawerItem().apply { nameRes = R.string.drawer_item_custom; iconicsIcon = FontAwesome.Icon.faw_eye; identifier = 5 },
SectionDrawerItem().apply { nameRes = R.string.drawer_item_section_header },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_settings; iconicsIcon = FontAwesome.Icon.faw_cog },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_help; iconicsIcon = FontAwesome.Icon.faw_question; isEnabled = false },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_open_source; iconicsIcon = FontAwesomeBrand.Icon.fab_github },
SecondaryDrawerItem().apply { nameRes = R.string.drawer_item_contact; iconicsIcon = FontAwesome.Icon.faw_bullhorn }
)
onDrawerItemClickListener = { v, drawerItem, position ->
if (drawerItem is Nameable) {
Toast.makeText(this@CrossfadeDrawerLayoutActvitiy, drawerItem.name?.getText(this@CrossfadeDrawerLayoutActvitiy), Toast.LENGTH_SHORT).show()
}
false
}
setSavedInstance(savedInstanceState)
}
binding.crossFadeSmallView.drawer = binding.crossFadeLargeView
//define maxDrawerWidth
binding.root.maxWidthPx = getOptimalDrawerWidth(this)
binding.crossFadeSmallView.background = binding.crossFadeLargeView.background
//define the crossfader to be used with the miniDrawer. This is required to be able to automatically toggle open / close
binding.crossFadeSmallView.crossFader = object : ICrossfader {
override val isCrossfaded: Boolean
get() = binding.root.isCrossfaded
override fun crossfade() {
val isFaded = isCrossfaded
binding.root.crossfade(400)
//only close the drawer if we were already faded and want to close it now
if (isFaded) {
binding.root.closeDrawer(GravityCompat.START)
}
}
}
// set the selection to the item with the identifier 5
if (savedInstanceState == null) {
binding.crossFadeLargeView.setSelection(5, false)
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
actionBarDrawerToggle.onConfigurationChanged(newConfig)
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
actionBarDrawerToggle.syncState()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (actionBarDrawerToggle.onOptionsItemSelected(item)) {
return true
}
return super.onOptionsItemSelected(item)
}
override fun onSaveInstanceState(_outState: Bundle) {
var outState = _outState
//add the values which need to be saved from the drawer to the bundle
outState = binding.crossFadeLargeView.saveInstanceState(outState)
//add the values which need to be saved from the accountHeader to the bundle
outState = headerView.saveInstanceState(outState)
super.onSaveInstanceState(outState)
}
override fun onBackPressed() {
//handle the back press :D close the drawer first and if the drawer is closed close the activity
if (binding.root.isDrawerOpen(binding.crossFadeSlider)) {
binding.root.closeDrawer(binding.crossFadeSlider)
} else {
super.onBackPressed()
}
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/CustomApplication.kt
================================================
package com.mikepenz.materialdrawer.app
import android.content.Context
import android.graphics.drawable.Drawable
import android.net.Uri
import android.widget.ImageView
import androidx.multidex.MultiDexApplication
import coil.dispose
import coil.load
import com.google.android.material.color.DynamicColors
import com.mikepenz.iconics.Iconics
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.utils.backgroundColorRes
import com.mikepenz.iconics.utils.sizeDp
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerImageLoader
import com.mikepenz.materialdrawer.util.getPlaceHolder
/**
* Created by mikepenz on 27.03.15.
*/
class CustomApplication : MultiDexApplication() {
override fun onCreate() {
super.onCreate()
Iconics.init(this)
DynamicColors.applyToActivitiesIfAvailable(this)
//initialize and create the image loader logic
/*
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable) {
Picasso.get().load(uri).placeholder(placeholder).into(imageView)
}
override fun cancel(imageView: ImageView) {
Picasso.get().cancelRequest(imageView)
}
})
*/
//initialize and create the image loader logic
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) {
imageView.load(uri) {
allowHardware(false)
placeholder(placeholder)
}
}
override fun cancel(imageView: ImageView) {
imageView.dispose()
}
override fun placeholder(ctx: Context, tag: String?): Drawable {
//define different placeholders for different imageView targets
//default tags are accessible via the DrawerImageLoader.Tags
//custom ones can be checked via string. see the CustomUrlBasePrimaryDrawerItem LINE 111
return when (tag) {
DrawerImageLoader.Tags.PROFILE.name -> getPlaceHolder(ctx)
DrawerImageLoader.Tags.ACCOUNT_HEADER.name -> IconicsDrawable(ctx, " ").apply { backgroundColorRes = R.color.colorPrimary; sizeDp = 56 }
"customUrlItem" -> IconicsDrawable(ctx, " ").apply { backgroundColorRes = R.color.colorAccent; sizeDp = 56 }
//we use the default one for
//DrawerImageLoader.Tags.PROFILE_DRAWER_ITEM.name()
else -> super.placeholder(ctx, tag)
}
}
})
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/DrawerActivity.kt
================================================
package com.mikepenz.materialdrawer.app
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Color
import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import com.mikepenz.aboutlibraries.LibsBuilder
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesomeBrand
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.actionBar
import com.mikepenz.iconics.utils.paddingDp
import com.mikepenz.materialdrawer.app.databinding.ActivitySampleBinding
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.ColorHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.iconics.iconicsIcon
import com.mikepenz.materialdrawer.model.*
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.util.addItems
import com.mikepenz.materialdrawer.util.updateBadge
import com.mikepenz.materialdrawer.widget.AccountHeaderView
class DrawerActivity : AppCompatActivity() {
private lateinit var binding: ActivitySampleBinding
private lateinit var headerView: AccountHeaderView
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySampleBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
// Handle Toolbar
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(false)
supportActionBar?.setHomeButtonEnabled(true)
actionBarDrawerToggle = ActionBarDrawerToggle(
this,
binding.root,
binding.toolbar,
com.mikepenz.materialdrawer.R.string.material_drawer_open,
com.mikepenz.materialdrawer.R.string.material_drawer_close
)
binding.root.addDrawerListener(actionBarDrawerToggle)
// Create a few sample profile
// NOTE you have to define the loader logic too. See the CustomApplication for more details
val profile = ProfileDrawerItem().apply {
nameText = "Mike Penz"; descriptionText = "mikepenz@gmail.com"; iconUrl = "https://avatars3.githubusercontent.com/u/1476232?v=3&s=460"; identifier =
100
}
val profile2 = ProfileDrawerItem().apply {
nameText = "Demo User"; descriptionText = "demo@github.com"; iconUrl = "https://avatars2.githubusercontent.com/u/3597376?v=3&s=460"; identifier =
101
}
val profile3 =
ProfileDrawerItem().apply { nameText = "Max Muster"; descriptionText = "max.mustermann@gmail.com"; iconRes = R.drawable.profile2; identifier = 102 }
val profile4 =
ProfileDrawerItem().apply { nameText = "Felix House"; descriptionText = "felix.house@gmail.com"; iconRes = R.drawable.profile3; identifier = 103 }
val profile5 =
ProfileDrawerItem().apply { nameText = "Mr. X"; descriptionText = "mister.x.super@gmail.com"; iconRes = R.drawable.profile4; identifier = 104 }
val profile6 = ProfileDrawerItem().apply {
nameText = "Batman"; descriptionText = "batman@gmail.com"; iconRes = R.drawable.profile5; identifier = 105; badgeText = "123"
badgeStyle = BadgeStyle().apply {
textColor = ColorHolder.fromColor(Color.BLACK)
color = ColorHolder.fromColor(Color.WHITE)
}
}
// Create the AccountHeader
headerView = AccountHeaderView(this).apply {
attachToSliderView(binding.slider)
addProfiles(
profile,
profile2,
profile3,
profile4,
profile5,
profile6,
//don't ask but google uses 14dp for the add account icon in gmail but 20dp for the normal icons (like manage account)
ProfileSettingDrawerItem().apply {
nameText = "Add Account"; descriptionText = "Add new GitHub Account"; iconDrawable =
IconicsDrawable(context, GoogleMaterial.Icon.gmd_add).apply { actionBar(); paddingDp = 5 }.mutate(); isIconTinted = true; identifier =
PROFILE_SETTING.toLong()
},
ProfileSettingDrawerItem().apply { nameText = "Manage Account"; iconicsIcon = GoogleMaterial.Icon.gmd_settings; identifier = 100001 }
)
onAccountHeaderListener = { view, profile, current ->
//sample usage of the onProfileChanged listener
//if the clicked item has the identifier 1 add a new profile ;)
if (profile is IDrawerItem<*> && profile.identifier == PROFILE_SETTING.toLong()) {
val count = 100 + (profiles?.size ?: 0) + 1
val newProfile =
ProfileDrawerItem().withNameShown(true).withName("Batman$count").withEmail("batman$count@gmail.com").withIcon(R.drawable.profile5)
.withIdentifier(count.toLong())
profiles?.let {
//we know that there are 2 setting elements. set the new profile above them ;)
addProfile(newProfile, it.size - 2)
} ?: addProfiles(newProfile)
}
//false if you have not consumed the event and it should close the drawer
false
}
withSavedInstance(savedInstanceState)
}
binding.slider.apply {
addItems(
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_compact_header; descriptionRes = R.string.drawer_item_compact_header_desc; iconicsIcon =
GoogleMaterial.Icon.gmd_brightness_5; isSelectable = false; identifier = 1
},
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_action_bar_drawer; descriptionRes = R.string.drawer_item_action_bar_drawer_desc; iconicsIcon =
FontAwesome.Icon.faw_home; isSelectable = false; identifier = 2
},
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_multi_drawer; descriptionRes = R.string.drawer_item_multi_drawer_desc; iconicsIcon =
FontAwesome.Icon.faw_gamepad; isSelectable = false; identifier = 3
},
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_advanced_drawer; descriptionRes = R.string.drawer_item_advanced_drawer_desc; iconicsIcon =
GoogleMaterial.Icon.gmd_adb; isSelectable = false; identifier = 5
},
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_embedded_drawer; descriptionRes = R.string.drawer_item_embedded_drawer_desc; iconicsIcon =
GoogleMaterial.Icon.gmd_battery_full; isSelectable = false; identifier = 7
},
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_fullscreen_drawer; descriptionRes = R.string.drawer_item_fullscreen_drawer_desc; iconicsIcon =
GoogleMaterial.Icon.gmd_label; isSelectable = false; identifier = 8
},
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_menu_drawer; descriptionRes = R.string.drawer_item_menu_drawer_desc; iconicsIcon =
GoogleMaterial.Icon.gmd_filter_list; isSelectable = false; identifier = 10
},
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_mini_drawer; descriptionRes = R.string.drawer_item_mini_drawer_desc; iconicsIcon =
GoogleMaterial.Icon.gmd_battery_charging_full; isSelectable = false; identifier = 11
},
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_fragment_drawer; descriptionRes = R.string.drawer_item_fragment_drawer_desc; iconicsIcon =
GoogleMaterial.Icon.gmd_disc_full; isSelectable = false; identifier = 12
},
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_collapsing_toolbar_drawer; descriptionRes =
R.string.drawer_item_collapsing_toolbar_drawer_desc; iconicsIcon = GoogleMaterial.Icon.gmd_camera_rear; isSelectable = false; identifier =
13
},
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_persistent_compact_header; descriptionRes =
R.string.drawer_item_persistent_compact_header_desc; iconicsIcon = GoogleMaterial.Icon.gmd_brightness_5; isSelectable = false; identifier =
14
},
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_crossfade_drawer_layout_drawer; descriptionRes =
R.string.drawer_item_crossfade_drawer_layout_drawer_desc; iconicsIcon = GoogleMaterial.Icon.gmd_format_bold; isSelectable =
false; identifier = 15
},
PrimaryDrawerItem().apply {
nameRes = R.string.drawer_item_navigation_drawer; descriptionRes = R.string.drawer_item_navigation_drawer_desc; iconicsIcon =
GoogleMaterial.Icon.gmd_navigation; isSelectable = false; identifier = 1305
},
ExpandableBadgeDrawerItem().apply {
nameText = "Collapsable Badge"; iconicsIcon = GoogleMaterial.Icon.gmd_format_bold; identifier = 18; isSelectable = false; badge =
StringHolder("100")
badgeStyle = BadgeStyle().apply { textColor = ColorHolder.fromColor(Color.WHITE); color = ColorHolder.fromColorRes(R.color.colorAccent) }
subItems = mutableListOf(
SecondaryDrawerItem().apply {
nameText = "CollapsableItem"; level = 2; iconicsIcon = GoogleMaterial.Icon.gmd_format_bold; identifier = 2000
},
SecondaryDrawerItem().apply {
nameText = "CollapsableItem 2"; level = 2; iconicsIcon = GoogleMaterial.Icon.gmd_format_bold; identifier = 2001
}
)
},
ExpandableDrawerItem().apply {
nameText = "Collapsable"; iconicsIcon = GoogleMaterial.Icon.gmd_filter_list; identifier = 19; isSelectable = false
subItems = mutableListOf(
SecondaryDrawerItem().apply {
nameText = "CollapsableItem"; level = 2; iconicsIcon = GoogleMaterial.Icon.gmd_filter_list; identifier = 2002
},
SecondaryDrawerItem().apply {
nameText = "CollapsableItem 2"; level = 2; iconicsIcon = GoogleMaterial.Icon.gmd_filter_list; identifier = 2003
}
)
},
SectionDrawerItem().apply { nameRes = R.string.drawer_item_section_header },
SecondaryDrawerItem().apply {
nameRes = R.string.drawer_item_open_source; iconicsIcon = FontAwesomeBrand.Icon.fab_github; identifier = 20; isSelectable = false
},
SecondaryDrawerItem().apply {
nameRes = R.string.drawer_item_contact; iconicsIcon = GoogleMaterial.Icon.gmd_format_color_fill; identifier = 21; isSelectable = false
}
/*,
DividerDrawerItem ()
SwitchDrawerItem ().withName("Switch").withIcon(Octicons.Icon.oct_tools).withChecked(true).withOnCheckedChangeListener(onCheckedChangeListener)
SwitchDrawerItem ().withName("Switch2").withIcon(Octicons.Icon.oct_tools).withChecked(true).withOnCheckedChangeListener(onCheckedChangeListener).withSelectable(false)
ToggleDrawerItem ().withName("Toggle").withIcon(Octicons.Icon.oct_tools).withChecked(true).withOnCheckedChangeListener(onCheckedChangeListener)
DividerDrawerItem ()
SecondarySwitchDrawerItem ().withName("Secondary switch").withIcon(Octicons.Icon.oct_tools).withChecked(true).withOnCheckedChangeListener(onCheckedChangeListener)
SecondarySwitchDrawerItem ().withName("Secondary Switch2").withIcon(Octicons.Icon.oct_tools).withChecked(true).withOnCheckedChangeListener(onCheckedChangeListener).withSelectable(false)
SecondaryToggleDrawerItem ().withName("Secondary toggle").withIcon(Octicons.Icon.oct_tools).withChecked(true).withOnCheckedChangeListener(onCheckedChangeListener)
*/
)
onDrawerItemClickListener = { _, drawerItem, _ ->
//check if the drawerItem is set.
//there are different reasons for the drawerItem to be null
//--> click on the header
//--> click on the footer
//those items don't contain a drawerItem
var intent: Intent? = null
when {
drawerItem.identifier == 1L -> intent = Intent(this@DrawerActivity, CompactHeaderDrawerActivity::class.java)
drawerItem.identifier == 2L -> intent = Intent(this@DrawerActivity, ActionBarActivity::class.java)
drawerItem.identifier == 3L -> intent = Intent(this@DrawerActivity, MultiDrawerActivity::class.java)
drawerItem.identifier == 5L -> intent = Intent(this@DrawerActivity, AdvancedActivity::class.java)
drawerItem.identifier == 7L -> intent = Intent(this@DrawerActivity, EmbeddedDrawerActivity::class.java)
drawerItem.identifier == 8L -> intent = Intent(this@DrawerActivity, FullscreenDrawerActivity::class.java)
drawerItem.identifier == 10L -> intent = Intent(this@DrawerActivity, MenuDrawerActivity::class.java)
drawerItem.identifier == 11L -> intent = Intent(this@DrawerActivity, MiniDrawerActivity::class.java)
drawerItem.identifier == 12L -> intent = Intent(this@DrawerActivity, FragmentActivity::class.java)
drawerItem.identifier == 13L -> intent = Intent(this@DrawerActivity, CollapsingToolbarActivity::class.java)
drawerItem.identifier == 14L -> intent = Intent(this@DrawerActivity, PersistentDrawerActivity::class.java)
drawerItem.identifier == 15L -> intent = Intent(this@DrawerActivity, CrossfadeDrawerLayoutActvitiy::class.java)
drawerItem.identifier == 1305L -> intent = Intent(this@DrawerActivity, NavControllerActivity::class.java)
drawerItem.identifier == 20L -> intent = LibsBuilder().intent(this@DrawerActivity)
}
if (intent != null) {
this@DrawerActivity.startActivity(intent)
}
false
}
setSavedInstance(savedInstanceState)
}
//slider.withStickyHeader(R.layout.header)
//slider.addStickyDrawerItems(
// SecondaryDrawerItem().withName(R.string.drawer_item_settings).withIcon(FontAwesome.Icon.faw_cog).withIdentifier(10),
// SecondaryDrawerItem().withName(R.string.drawer_item_open_source).withIcon(FontAwesomeBrand.Icon.fab_github)
//)
//only set the active selection or active profile if we do not recreate the activity
if (savedInstanceState == null) {
// set the selection to the item with the identifier 11
binding.slider.setSelection(21, false)
//set the active profile
headerView.activeProfile = profile3
}
binding.slider.updateBadge(4, StringHolder(10.toString() + ""))
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
actionBarDrawerToggle.onConfigurationChanged(newConfig)
}
override fun onResume() {
super.onResume()
actionBarDrawerToggle.syncState()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (actionBarDrawerToggle.onOptionsItemSelected(item)) {
return true
}
return super.onOptionsItemSelected(item)
}
override fun onSaveInstanceState(_outState: Bundle) {
var outState = _outState
//add the values which need to be saved from the drawer to the bundle
outState = binding.slider.saveInstanceState(outState)
//add the values which need to be saved from the accountHeader to the bundle
outState = headerView.saveInstanceState(outState)
super.onSaveInstanceState(outState)
}
override fun onBackPressed() {
//handle the back press :D close the drawer first and if the drawer is closed close the activity
if (binding.root.isDrawerOpen(binding.slider)) {
binding.root.closeDrawer(binding.slider)
} else {
super.onBackPressed()
}
}
companion object {
private const val PROFILE_SETTING = 100000
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/EmbeddedDrawerActivity.kt
================================================
package com.mikepenz.materialdrawer.app
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.MenuItem
import android.view.ViewGroup
import android.widget.CompoundButton
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesomeBrand
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.actionBar
import com.mikepenz.iconics.utils.paddingDp
import com.mikepenz.materialdrawer.app.databinding.ActivityEmbeddedBinding
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.iconics.withIcon
import com.mikepenz.materialdrawer.interfaces.OnCheckedChangeListener
import com.mikepenz.materialdrawer.model.*
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.widget.AccountHeaderView
class EmbeddedDrawerActivity : AppCompatActivity() {
private lateinit var binding: ActivityEmbeddedBinding
private lateinit var headerView: AccountHeaderView
private val onCheckedChangeListener = object : OnCheckedChangeListener {
override fun onCheckedChanged(drawerItem: IDrawerItem<*>, buttonView: CompoundButton, isChecked: Boolean) {
if (drawerItem is Nameable) {
Log.i("material-drawer", "DrawerItem: " + (drawerItem as Nameable).name + " - toggleChecked: " + isChecked)
} else {
Log.i("material-drawer", "toggleChecked: $isChecked")
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityEmbeddedBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
// Handle Toolbar
setSupportActionBar(binding.toolbar)
//set the back arrow in the toolbar
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setTitle(R.string.drawer_item_embedded_drawer)
// Create a few sample profile
// NOTE you have to define the loader logic too. See the CustomApplication for more details
val profile = ProfileDrawerItem().withName("Mike Penz").withEmail("mikepenz@gmail.com").withIcon("https://avatars3.githubusercontent.com/u/1476232?v=3&s=460")
val profile2 = ProfileDrawerItem().withName("Bernat Borras").withEmail("alorma@github.com").withIcon(Uri.parse("https://avatars3.githubusercontent.com/u/887462?v=3&s=460"))
val profile3 = ProfileDrawerItem().withName("Max Muster").withEmail("max.mustermann@gmail.com").withIcon(resources.getDrawable(R.drawable.profile2))
val profile4 = ProfileDrawerItem().withName("Felix House").withEmail("felix.house@gmail.com").withIcon(resources.getDrawable(R.drawable.profile3))
val profile5 = ProfileDrawerItem().withName("Mr. X").withEmail("mister.x.super@gmail.com").withIcon(resources.getDrawable(R.drawable.profile4)).withIdentifier(4)
val profile6 = ProfileDrawerItem().withName("Batman").withEmail("batman@gmail.com").withIcon(resources.getDrawable(R.drawable.profile5))
// Create the AccountHeader
headerView = AccountHeaderView(this).apply {
attachToSliderView(binding.slider)
addProfiles(
profile,
profile2,
profile3,
profile4,
profile5,
profile6,
//don't ask but google uses 14dp for the add account icon in gmail but 20dp for the normal icons (like manage account)
ProfileSettingDrawerItem().withName("Add Account").withDescription("Add new GitHub Account").withIcon(IconicsDrawable(context, GoogleMaterial.Icon.gmd_add).apply { actionBar(); paddingDp = 5 }).withIconTinted(true).withIdentifier(PROFILE_SETTING.toLong()),
ProfileSettingDrawerItem().withName("Manage Account").withIcon(GoogleMaterial.Icon.gmd_settings).withIdentifier(100001)
)
onAccountHeaderListener = { view, profile, current ->
//sample usage of the onProfileChanged listener
//if the clicked item has the identifier 1 add a new profile ;)
if (profile is IDrawerItem<*> && (profile as IDrawerItem<*>).identifier == PROFILE_SETTING.toLong()) {
val newProfile = ProfileDrawerItem().withNameShown(true).withName("Batman").withEmail("batman@gmail.com").withIcon(resources.getDrawable(R.drawable.profile5))
headerView.profiles?.let {
//we know that there are 2 setting elements. set the new profile above them ;)
headerView.addProfile(newProfile, it.size - 2)
} ?: headerView.addProfiles(newProfile)
}
//false if you have not consumed the event and it should close the drawer
false
}
withSavedInstance(savedInstanceState)
}
binding.slider.apply {
customWidth = ViewGroup.LayoutParams.MATCH_PARENT
itemAdapter.add(
PrimaryDrawerItem().withName(R.string.drawer_item_compact_header).withIcon(GoogleMaterial.Icon.gmd_brightness_5).withIdentifier(1),
PrimaryDrawerItem().withName(R.string.drawer_item_action_bar_drawer).withIcon(FontAwesome.Icon.faw_home).withBadge("22")
.withBadgeStyle(BadgeStyle(Color.RED, Color.RED)).withIdentifier(2),
PrimaryDrawerItem().withName(R.string.drawer_item_multi_drawer).withIcon(FontAwesome.Icon.faw_gamepad).withIdentifier(3),
PrimaryDrawerItem().withName(R.string.drawer_item_non_translucent_status_drawer).withIcon(FontAwesome.Icon.faw_eye).withIdentifier(4),
PrimaryDrawerItem().withDescription("A more complex sample").withName(R.string.drawer_item_advanced_drawer)
.withIcon(GoogleMaterial.Icon.gmd_adb).withIdentifier(5),
SectionDrawerItem().withName(R.string.drawer_item_section_header),
SecondaryDrawerItem().withName(R.string.drawer_item_open_source).withIcon(FontAwesomeBrand.Icon.fab_github),
SecondaryDrawerItem().withName(R.string.drawer_item_contact).withIcon(GoogleMaterial.Icon.gmd_format_color_fill).withTag("Bullhorn"),
DividerDrawerItem(),
SwitchDrawerItem().withName("Switch").withIcon(GoogleMaterial.Icon.gmd_pan_tool).withChecked(true)
.withOnCheckedChangeListener(onCheckedChangeListener),
ToggleDrawerItem().withName("Toggle").withIcon(GoogleMaterial.Icon.gmd_pan_tool).withChecked(true)
.withOnCheckedChangeListener(onCheckedChangeListener)
)
onDrawerItemClickListener = { v, drawerItem, position ->
if (drawerItem is Nameable) {
Toast.makeText(this@EmbeddedDrawerActivity, drawerItem.name?.getText(this@EmbeddedDrawerActivity), Toast.LENGTH_SHORT).show()
}
false
}
setSavedInstance(savedInstanceState)
}
}
override fun onSaveInstanceState(_outState: Bundle) {
var outState = _outState
//add the values which need to be saved from the drawer to the bundle
outState = binding.slider.saveInstanceState(outState)
//add the values which need to be saved from the accountHeader to the bundle
outState = headerView.saveInstanceState(outState)
super.onSaveInstanceState(outState)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
//handle the click on the back arrow click
return when (item.itemId) {
android.R.id.home -> {
onBackPressed()
true
}
else -> super.onOptionsItemSelected(item)
}
}
companion object {
private const val PROFILE_SETTING = 1
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/FragmentActivity.kt
================================================
package com.mikepenz.materialdrawer.app
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import com.mikepenz.materialdrawer.app.databinding.ActivitySampleFragmentBinding
import com.mikepenz.materialdrawer.app.fragment.DrawerFragment
import com.mikepenz.materialdrawer.app.fragment.SecondDrawerFragment
class FragmentActivity : AppCompatActivity() {
private lateinit var binding: ActivitySampleFragmentBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySampleFragmentBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
// Handle Toolbar
setSupportActionBar(binding.toolbar)
supportActionBar?.setTitle(R.string.drawer_item_fragment_drawer)
//ignore the DemoFragment and it's layout it's just to showcase the handle with an keyboard
if (savedInstanceState == null) {
val f = DrawerFragment.newInstance("Demo")
supportFragmentManager.beginTransaction().replace(R.id.fragment_container, f).commit()
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.fragment_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
//handle the click on the back arrow click
when (item.itemId) {
R.id.menu_1 -> {
val f = DrawerFragment.newInstance("Demo")
supportFragmentManager.beginTransaction().replace(R.id.fragment_container, f).commit()
return true
}
R.id.menu_2 -> {
val f2 = SecondDrawerFragment.newInstance("Demo 2")
supportFragmentManager.beginTransaction().replace(R.id.fragment_container, f2).commit()
return true
}
android.R.id.home -> {
onBackPressed()
return true
}
else -> return super.onOptionsItemSelected(item)
}
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/FullscreenDrawerActivity.kt
================================================
package com.mikepenz.materialdrawer.app
import android.content.res.Configuration
import android.graphics.Color
import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.updatePadding
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesomeBrand
import com.mikepenz.materialdrawer.app.databinding.ActivitySampleFullscreenBinding
import com.mikepenz.materialdrawer.iconics.withIcon
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import com.mikepenz.materialdrawer.model.SectionDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.withEnabled
import com.mikepenz.materialdrawer.model.interfaces.withIdentifier
import com.mikepenz.materialdrawer.model.interfaces.withName
class FullscreenDrawerActivity : AppCompatActivity() {
private lateinit var binding: ActivitySampleFullscreenBinding
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySampleFullscreenBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
// Handle Toolbar
setSupportActionBar(binding.toolbar)
binding.toolbar.setBackgroundColor(Color.BLACK)
binding.toolbar.background.alpha = 90
supportActionBar?.setTitle(R.string.drawer_item_fullscreen_drawer)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeButtonEnabled(true)
actionBarDrawerToggle = ActionBarDrawerToggle(this, binding.root, binding.toolbar, com.mikepenz.materialdrawer.R.string.material_drawer_open, com.mikepenz.materialdrawer.R.string.material_drawer_close)
binding.slider.apply {
itemAdapter.add(
PrimaryDrawerItem().withName(R.string.drawer_item_home).withIcon(FontAwesome.Icon.faw_home).withIdentifier(1),
PrimaryDrawerItem().withName(R.string.drawer_item_free_play).withIcon(FontAwesome.Icon.faw_gamepad),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye),
//add some more items to get a scrolling list
SectionDrawerItem().withName(R.string.drawer_item_section_header),
SecondaryDrawerItem().withName(R.string.drawer_item_settings).withIcon(FontAwesome.Icon.faw_cog),
SecondaryDrawerItem().withName(R.string.drawer_item_help).withIcon(FontAwesome.Icon.faw_question).withEnabled(false),
SecondaryDrawerItem().withName(R.string.drawer_item_open_source).withIcon(FontAwesomeBrand.Icon.fab_github),
SecondaryDrawerItem().withName(R.string.drawer_item_contact).withIcon(FontAwesome.Icon.faw_bullhorn),
SectionDrawerItem().withName(R.string.drawer_item_section_header),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye)
)
setSavedInstance(savedInstanceState)
}
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
binding.toolbar.updatePadding(top = insets.systemWindowInsetTop)
insets
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
actionBarDrawerToggle.onConfigurationChanged(newConfig)
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
actionBarDrawerToggle.syncState()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (actionBarDrawerToggle.onOptionsItemSelected(item)) {
return true
}
return super.onOptionsItemSelected(item)
}
override fun onSaveInstanceState(_outState: Bundle) {
var outState = _outState
//add the values which need to be saved from the drawer to the bundle
outState = binding.slider.saveInstanceState(outState)
super.onSaveInstanceState(outState)
}
override fun onBackPressed() {
//handle the back press :D close the drawer first and if the drawer is closed close the activity
if (binding.root.isDrawerOpen(binding.slider)) {
binding.root.closeDrawer(binding.slider)
} else {
super.onBackPressed()
}
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/MenuDrawerActivity.kt
================================================
package com.mikepenz.materialdrawer.app
import android.content.res.Configuration
import android.os.Bundle
import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import com.mikepenz.materialdrawer.app.databinding.ActivitySampleBinding
import com.mikepenz.materialdrawer.model.interfaces.Nameable
import com.mikepenz.materialdrawer.util.inflateMenu
class MenuDrawerActivity : AppCompatActivity() {
private lateinit var binding: ActivitySampleBinding
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySampleBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
// Handle Toolbar
setSupportActionBar(binding.toolbar)
supportActionBar?.setTitle(R.string.drawer_item_menu_drawer)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeButtonEnabled(true)
actionBarDrawerToggle = ActionBarDrawerToggle(this, binding.root, binding.toolbar, com.mikepenz.materialdrawer.R.string.material_drawer_open, com.mikepenz.materialdrawer.R.string.material_drawer_close)
binding.slider.apply {
inflateMenu(R.menu.example_menu)
onDrawerItemClickListener = { v, drawerItem, position ->
if (drawerItem is Nameable) {
Toast.makeText(this@MenuDrawerActivity, (drawerItem as Nameable).name!!.getText(this@MenuDrawerActivity), Toast.LENGTH_SHORT).show()
}
false
}
setSavedInstance(savedInstanceState)
}
// set the selection to the item with the identifier 5
if (savedInstanceState == null) {
binding.slider.setSelection(5, false)
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
actionBarDrawerToggle.onConfigurationChanged(newConfig)
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
actionBarDrawerToggle.syncState()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (actionBarDrawerToggle.onOptionsItemSelected(item)) {
return true
}
return super.onOptionsItemSelected(item)
}
override fun onSaveInstanceState(_outState: Bundle) {
var outState = _outState
//add the values which need to be saved from the drawer to the bundle
outState = binding.slider.saveInstanceState(outState)
super.onSaveInstanceState(outState)
}
override fun onBackPressed() {
//handle the back press :D close the drawer first and if the drawer is closed close the activity
if (binding.root.isDrawerOpen(binding.slider)) {
binding.root.closeDrawer(binding.slider)
} else {
super.onBackPressed()
}
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/MiniDrawerActivity.kt
================================================
package com.mikepenz.materialdrawer.app
import android.content.res.Configuration
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.CompoundButton
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.mikepenz.crossfader.Crossfader
import com.mikepenz.crossfader.util.UIUtils
import com.mikepenz.crossfader.view.CrossFadeSlidingPaneLayout
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesomeBrand
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.actionBar
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.materialdrawer.app.databinding.ActivityMiniDrawerBinding
import com.mikepenz.materialdrawer.app.utils.CrossfadeWrapper
import com.mikepenz.materialdrawer.app.utils.SystemUtils
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.iconics.withIcon
import com.mikepenz.materialdrawer.interfaces.OnCheckedChangeListener
import com.mikepenz.materialdrawer.model.*
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.model.utils.withIsHiddenInMiniDrawer
import com.mikepenz.materialdrawer.widget.AccountHeaderView
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
import com.mikepenz.materialdrawer.widget.MiniDrawerSliderView
class MiniDrawerActivity : AppCompatActivity() {
private lateinit var binding: ActivityMiniDrawerBinding
//save our header or result
private lateinit var headerView: AccountHeaderView
private lateinit var miniSliderView: MiniDrawerSliderView
private lateinit var sliderView: MaterialDrawerSliderView
private lateinit var crossFader: Crossfader<*>
private val onCheckedChangeListener = object : OnCheckedChangeListener {
override fun onCheckedChanged(drawerItem: IDrawerItem<*>, buttonView: CompoundButton, isChecked: Boolean) {
if (drawerItem is Nameable) {
Log.i("material-drawer", "DrawerItem: " + (drawerItem as Nameable).name + " - toggleChecked: " + isChecked)
} else {
Log.i("material-drawer", "toggleChecked: $isChecked")
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMiniDrawerBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
// Handle Toolbar
setSupportActionBar(binding.toolbar)
//set the back arrow in the toolbar
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setTitle(R.string.drawer_item_mini_drawer)
// Create a few sample profile
// NOTE you have to define the loader logic too. See the CustomApplication for more details
val profile = ProfileDrawerItem().withName("Mike Penz").withEmail("mikepenz@gmail.com").withIcon("https://avatars3.githubusercontent.com/u/1476232?v=3&s=460")
val profile2 = ProfileDrawerItem().withName("Bernat Borras").withEmail("alorma@github.com").withIcon(Uri.parse("https://avatars3.githubusercontent.com/u/887462?v=3&s=460"))
val profile3 = ProfileDrawerItem().withName("Max Muster").withEmail("max.mustermann@gmail.com").withIcon(resources.getDrawable(R.drawable.profile2))
val profile4 = ProfileDrawerItem().withName("Felix House").withEmail("felix.house@gmail.com").withIcon(resources.getDrawable(R.drawable.profile3))
val profile5 = ProfileDrawerItem().withName("Mr. X").withEmail("mister.x.super@gmail.com").withIcon(resources.getDrawable(R.drawable.profile4)).withIdentifier(4)
val profile6 = ProfileDrawerItem().withName("Batman").withEmail("batman@gmail.com").withIcon(resources.getDrawable(R.drawable.profile5))
// Create the AccountHeader
headerView = AccountHeaderView(this).apply {
addProfiles(
profile,
profile2,
profile3,
profile4,
profile5,
profile6,
//don't ask but google uses 14dp for the add account icon in gmail but 20dp for the normal icons (like manage account)
ProfileSettingDrawerItem().withName("Add Account").withDescription("Add new GitHub Account").withIcon(GoogleMaterial.Icon.gmd_add).withIdentifier(PROFILE_SETTING.toLong()),
ProfileSettingDrawerItem().withName("Manage Account").withIcon(GoogleMaterial.Icon.gmd_settings)
)
onAccountHeaderListener = { view, profile, current ->
//sample usage of the onProfileChanged listener
//if the clicked item has the identifier 1 add a new profile ;)
if (profile is IDrawerItem<*> && (profile as IDrawerItem<*>).identifier == PROFILE_SETTING.toLong()) {
val newProfile = ProfileDrawerItem().withNameShown(true).withName("Batman").withEmail("batman@gmail.com").withIcon(resources.getDrawable(R.drawable.profile5))
headerView.profiles?.let {
//we know that there are 2 setting elements. set the new profile above them ;)
headerView.addProfile(newProfile, it.size - 2)
} ?: headerView.addProfiles(newProfile)
}
//false if you have not consumed the event and it should close the drawer
false
}
withSavedInstance(savedInstanceState)
}
sliderView = MaterialDrawerSliderView(this).apply {
accountHeader = this@MiniDrawerActivity.headerView
customWidth = MATCH_PARENT
itemAdapter.add(
PrimaryDrawerItem().withName(R.string.drawer_item_compact_header).withIcon(GoogleMaterial.Icon.gmd_brightness_5).withIdentifier(1),
PrimaryDrawerItem().withName("Item NOT in mini drawer").withIcon(GoogleMaterial.Icon.gmd_bluetooth).withIdentifier(100)
.withIsHiddenInMiniDrawer(true),
PrimaryDrawerItem().withName(R.string.drawer_item_action_bar_drawer).withIcon(FontAwesome.Icon.faw_home).withBadge("22")
.withBadgeStyle(BadgeStyle(Color.RED, Color.RED)).withIdentifier(2).withSelectable(false),
PrimaryDrawerItem().withName(R.string.drawer_item_multi_drawer).withIcon(FontAwesome.Icon.faw_gamepad).withIdentifier(3),
PrimaryDrawerItem().withName(R.string.drawer_item_non_translucent_status_drawer).withIcon(FontAwesome.Icon.faw_eye).withIdentifier(4),
PrimaryDrawerItem().withDescription("A more complex sample").withName(R.string.drawer_item_advanced_drawer)
.withIcon(GoogleMaterial.Icon.gmd_adb).withIdentifier(5),
SectionDrawerItem().withName(R.string.drawer_item_section_header),
SecondaryDrawerItem().withName(R.string.drawer_item_open_source).withIcon(FontAwesomeBrand.Icon.fab_github),
SecondaryDrawerItem().withName(R.string.drawer_item_contact).withIcon(GoogleMaterial.Icon.gmd_format_color_fill).withTag("Bullhorn"),
DividerDrawerItem(),
SwitchDrawerItem().withName("Switch").withIcon(GoogleMaterial.Icon.gmd_pan_tool).withChecked(true)
.withOnCheckedChangeListener(onCheckedChangeListener),
ToggleDrawerItem().withName("Toggle").withIcon(GoogleMaterial.Icon.gmd_pan_tool).withChecked(true)
.withOnCheckedChangeListener(onCheckedChangeListener)
)
onDrawerItemClickListener = { v, item, position ->
if (item is Nameable) {
Toast.makeText(this@MiniDrawerActivity, item.name?.getText(this@MiniDrawerActivity), Toast.LENGTH_SHORT).show()
}
false
}
}
miniSliderView = MiniDrawerSliderView(this).apply {
drawer = sliderView
}
//get the widths in px for the first and second panel
val firstWidth = UIUtils.convertDpToPixel(300f, this).toInt()
val secondWidth = UIUtils.convertDpToPixel(72f, this).toInt()
//create and build our crossfader (see the MiniDrawer is also builded in here, as the build method returns the view to be used in the crossfader)
//the crossfader library can be found here: https://github.com/mikepenz/Crossfader
crossFader = Crossfader()
.withContent(findViewById(R.id.crossfade_content))
.withFirst(sliderView, firstWidth)
.withSecond(miniSliderView, secondWidth)
.withSavedInstance(savedInstanceState)
.build()
//define the crossfader to be used with the miniDrawer. This is required to be able to automatically toggle open / close
miniSliderView.crossFader = CrossfadeWrapper(crossFader)
//define a shadow (this is only for normal LTR layouts if you have a RTL app you need to define the other one
crossFader.getCrossFadeSlidingPaneLayout().setShadowResourceLeft(com.mikepenz.materialdrawer.R.drawable.material_drawer_shadow_left)
}
override fun onSaveInstanceState(_outState: Bundle) {
var outState = _outState
//add the values, which need to be saved from the drawer to the bundle
if (::sliderView.isInitialized) {
outState = sliderView.saveInstanceState(outState)
}
//add the values, which need to be saved from the accountHeader to the bundle
if (::headerView.isInitialized) {
outState = headerView.saveInstanceState(outState)
}
//add the values, which need to be saved from the crossFader to the bundle
if (::crossFader.isInitialized) {
outState = crossFader.saveInstanceState(outState)
}
super.onSaveInstanceState(outState)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater = menuInflater
if (SystemUtils.screenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
inflater.inflate(R.menu.embedded, menu)
menu.findItem(R.id.menu_1).icon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_sort).apply { actionBar(); colorInt = Color.WHITE }
}
return true
}
override fun onBackPressed() {
//handle the back press :D close the drawer first and if the drawer is closed close the activity
if (crossFader.isCrossFaded) {
crossFader.crossFade()
} else {
super.onBackPressed()
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
//handle the click on the back arrow click
return when (item.itemId) {
R.id.menu_1 -> {
crossFader.crossFade()
true
}
android.R.id.home -> {
onBackPressed()
true
}
else -> super.onOptionsItemSelected(item)
}
}
companion object {
private const val PROFILE_SETTING = 1
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/MultiDrawerActivity.kt
================================================
package com.mikepenz.materialdrawer.app
import android.os.Bundle
import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesomeBrand
import com.mikepenz.materialdrawer.app.databinding.ActivityMultiSampleBinding
import com.mikepenz.materialdrawer.app.drawerItems.GmailDrawerItem
import com.mikepenz.materialdrawer.iconics.withIcon
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import com.mikepenz.materialdrawer.model.SectionDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.util.updateItem
class MultiDrawerActivity : AppCompatActivity() {
private lateinit var binding: ActivityMultiSampleBinding
override fun onCreate(savedInstanceState: Bundle?) {
//supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
super.onCreate(savedInstanceState)
binding = ActivityMultiSampleBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
// Handle Toolbar
setSupportActionBar(binding.toolbar)
supportActionBar?.setTitle(R.string.drawer_item_multi_drawer)
binding.slider.apply {
itemAdapter.add(
GmailDrawerItem().withName(R.string.drawer_item_home).withIcon(FontAwesome.Icon.faw_home).withIdentifier(1),
GmailDrawerItem().withName(R.string.drawer_item_free_play).withIcon(FontAwesome.Icon.faw_gamepad),
GmailDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye).withIdentifier(5),
SectionDrawerItem().withName(R.string.drawer_item_section_header),
SecondaryDrawerItem().withName(R.string.drawer_item_settings).withIcon(FontAwesome.Icon.faw_cog),
SecondaryDrawerItem().withName(R.string.drawer_item_help).withIcon(FontAwesome.Icon.faw_question).withEnabled(false),
SecondaryDrawerItem().withName(R.string.drawer_item_open_source).withIcon(FontAwesomeBrand.Icon.fab_github),
SecondaryDrawerItem().withName(R.string.drawer_item_contact).withIcon(FontAwesome.Icon.faw_bullhorn)
)
adapter.onClickListener = { v, adapter, drawerItem, position ->
if (drawerItem is Badgeable) {
if (drawerItem.badge != null) {
//note don't do this if your badge contains a "+"
//only use toString() if you set the test as String
val badge = Integer.valueOf(drawerItem.badge?.toString() ?: "0")
if (badge > 0) {
drawerItem.withBadge((badge - 1).toString())
binding.slider.updateItem(drawerItem)
}
}
}
if (drawerItem is Nameable) {
Toast.makeText(this@MultiDrawerActivity, drawerItem.name?.getText(this@MultiDrawerActivity), Toast.LENGTH_SHORT).show()
}
false
}
setSavedInstance(savedInstanceState)
setSelectionAtPosition(0)
}
binding.sliderEnd.apply {
stickyHeaderView = layoutInflater.inflate(R.layout.header, null)
itemAdapter.add(
PrimaryDrawerItem().withName(R.string.drawer_item_home).withIcon(FontAwesome.Icon.faw_home).withIdentifier(1),
PrimaryDrawerItem().withName(R.string.drawer_item_free_play).withIcon(FontAwesome.Icon.faw_gamepad),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye).withIdentifier(5),
SectionDrawerItem().withName(R.string.drawer_item_section_header),
SecondaryDrawerItem().withName(R.string.drawer_item_settings).withIcon(FontAwesome.Icon.faw_cog),
SecondaryDrawerItem().withName(R.string.drawer_item_help).withIcon(FontAwesome.Icon.faw_question).withEnabled(false),
SecondaryDrawerItem().withName(R.string.drawer_item_open_source).withIcon(FontAwesomeBrand.Icon.fab_github),
SecondaryDrawerItem().withName(R.string.drawer_item_contact).withIcon(FontAwesome.Icon.faw_bullhorn)
)
adapter.onClickListener = { v, adapter, drawerItem, position ->
if (drawerItem is Nameable) {
Toast.makeText(this@MultiDrawerActivity, drawerItem.name?.getText(this@MultiDrawerActivity), Toast.LENGTH_SHORT).show()
}
false
}
savedInstanceKey = "end"
setSavedInstance(savedInstanceState)
}
//set the back arrow in the toolbar
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeButtonEnabled(false)
}
override fun onSaveInstanceState(_outState: Bundle) {
var outState = _outState
//add the values which need to be saved from the drawer to the bundle
outState = binding.slider.saveInstanceState(outState)
//add the values which need to be saved from the drawer to the bundle
outState = binding.sliderEnd.saveInstanceState(outState)
super.onSaveInstanceState(outState)
}
override fun onBackPressed() {
//handle the back press :D close the drawer first and if the drawer is closed close the activity
when {
binding.root.isDrawerOpen(binding.slider) -> binding.root.closeDrawer(binding.slider)
binding.root.isDrawerOpen(binding.sliderEnd) -> binding.root.closeDrawer(binding.sliderEnd)
else -> super.onBackPressed()
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
//handle the click on the back arrow click
return when (item.itemId) {
android.R.id.home -> {
onBackPressed()
true
}
else -> super.onOptionsItemSelected(item)
}
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/NavControllerActivity.kt
================================================
package com.mikepenz.materialdrawer.app
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.os.bundleOf
import androidx.navigation.findNavController
import com.mikepenz.aboutlibraries.LibsBuilder
import com.mikepenz.materialdrawer.app.databinding.ActivitySampleNavBinding
import com.mikepenz.materialdrawer.model.DividerDrawerItem
import com.mikepenz.materialdrawer.model.NavigationDrawerItem
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.withName
import com.mikepenz.materialdrawer.util.addItems
import com.mikepenz.materialdrawer.util.addStickyDrawerItems
import com.mikepenz.materialdrawer.util.setupWithNavController
import java.io.Serializable
class NavControllerActivity : AppCompatActivity() {
private lateinit var binding: ActivitySampleNavBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySampleNavBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
val toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
val navController = findNavController(R.id.nav_host_fragment)
binding.slider.apply {
addItems(
NavigationDrawerItem(R.id.action_global_fragmentHome, PrimaryDrawerItem().withName("Home"), null, null),
DividerDrawerItem(),
NavigationDrawerItem(R.id.messageFragment1, PrimaryDrawerItem().withName("Fragment1")),
NavigationDrawerItem(R.id.messageFragment2, PrimaryDrawerItem().withName("Fragment2"))
)
addStickyDrawerItems(
NavigationDrawerItem(R.id.messageFragment3, PrimaryDrawerItem().withName("Fragment3")),
NavigationDrawerItem(R.id.about_libraries, PrimaryDrawerItem().withName("AboutLibs"), bundleOf("data" to (LibsBuilder() as Serializable)))
)
}
// setup the drawer with navigation controller
binding.slider.setupWithNavController(navController)
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/PersistentDrawerActivity.kt
================================================
package com.mikepenz.materialdrawer.app
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.updatePadding
import com.mikepenz.crossfader.Crossfader
import com.mikepenz.crossfader.util.UIUtils
import com.mikepenz.crossfader.view.CrossFadeSlidingPaneLayout
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesomeBrand
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import com.mikepenz.materialdrawer.app.databinding.ActivityPersistentDrawerBinding
import com.mikepenz.materialdrawer.app.utils.CrossfadeWrapper
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.iconics.withIcon
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import com.mikepenz.materialdrawer.model.SectionDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.widget.AccountHeaderView
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
import com.mikepenz.materialdrawer.widget.MiniDrawerSliderView
class PersistentDrawerActivity : AppCompatActivity() {
private lateinit var binding: ActivityPersistentDrawerBinding
//save our header or result
private lateinit var headerView: AccountHeaderView
private lateinit var miniSliderView: MiniDrawerSliderView
private lateinit var sliderView: MaterialDrawerSliderView
private lateinit var crossFader: Crossfader<*>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPersistentDrawerBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
//Remove line to test RTL support
// getWindow().getDecorView().setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
//example how to implement a persistentDrawer as shown in the google material design guidelines
//https://material-design.storage.googleapis.com/publish/material_v_4/material_ext_publish/0Bx4BSt6jniD7YVdKQlF3TEo2S3M/patterns_navdrawer_behavior_persistent2.png
//https://www.google.com/design/spec/patterns/navigation-drawer.html#navigation-drawer-behavior
// Handle Toolbar
setSupportActionBar(binding.toolbar)
supportActionBar?.setTitle(R.string.drawer_item_persistent_compact_header)
// Create a few sample profile
val profile = ProfileDrawerItem().withName("Mike Penz").withEmail("mikepenz@gmail.com").withIcon(R.drawable.profile)
val profile2 = ProfileDrawerItem().withName("Max Muster").withEmail("max.mustermann@gmail.com").withIcon(R.drawable.profile2)
val profile3 = ProfileDrawerItem().withName("Felix House").withEmail("felix.house@gmail.com").withIcon(R.drawable.profile3)
val profile4 = ProfileDrawerItem().withName("Mr. X").withEmail("mister.x.super@gmail.com").withIcon(R.drawable.profile4)
val profile5 = ProfileDrawerItem().withName("Batman").withEmail("batman@gmail.com").withIcon(R.drawable.profile5)
// Create the AccountHeader
headerView = AccountHeaderView(this).apply {
addProfiles(
profile,
profile2,
profile3,
profile4,
profile5
)
headerBackground = ImageHolder(ColorDrawable(Color.parseColor("#FDFDFD")))
}
// UIUtils.getActionBarHeight(this))
//.withAccountHeader(R.layout.material_drawer_compact_persistent_header)
//Create the drawer
sliderView = MaterialDrawerSliderView(this).apply {
accountHeader = this@PersistentDrawerActivity.headerView
customWidth = ViewGroup.LayoutParams.MATCH_PARENT
itemAdapter.add(
PrimaryDrawerItem().withName(R.string.drawer_item_home).withIcon(FontAwesome.Icon.faw_home).withIdentifier(1),
PrimaryDrawerItem().withName(R.string.drawer_item_free_play).withIcon(FontAwesome.Icon.faw_gamepad),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye),
SectionDrawerItem().withName(R.string.drawer_item_section_header),
SecondaryDrawerItem().withName(R.string.drawer_item_settings).withIcon(FontAwesome.Icon.faw_cog),
SecondaryDrawerItem().withName(R.string.drawer_item_help).withIcon(FontAwesome.Icon.faw_question).withEnabled(false),
SecondaryDrawerItem().withName(R.string.drawer_item_open_source).withIcon(FontAwesomeBrand.Icon.fab_github),
SecondaryDrawerItem().withName(R.string.drawer_item_contact).withIcon(FontAwesome.Icon.faw_bullhorn)
)
}
// create the MiniDrawer and define the drawer and header to be used (it will automatically use the items from them)
miniSliderView = MiniDrawerSliderView(this).apply {
includeSecondaryDrawerItems = true
drawer = sliderView
}
//set the back arrow in the toolbar
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeButtonEnabled(false)
//get the widths in px for the first and second panel
val firstWidth = UIUtils.convertDpToPixel(300f, this).toInt()
val secondWidth = UIUtils.convertDpToPixel(72f, this).toInt()
//create and build our crossfader (see the MiniDrawer is also builded in here, as the build method returns the view to be used in the crossfader)
crossFader = Crossfader()
.withContent(findViewById(R.id.crossfade_content))
.withFirst(sliderView as View, firstWidth)
.withSecond(miniSliderView, secondWidth)
.withSavedInstance(savedInstanceState)
.build()
//define the crossfader to be used with the miniDrawer. This is required to be able to automatically toggle open / close
miniSliderView.crossFader = CrossfadeWrapper(crossFader)
//define and create the arrow ;)
val toggle = headerView.findViewById(R.id.material_drawer_account_header_toggle)
//for RTL you would have to define the other arrow
toggle.setImageDrawable(IconicsDrawable(this, GoogleMaterial.Icon.gmd_chevron_left).apply { sizeDp = 16; colorInt = Color.BLACK })
toggle.setOnClickListener { crossFader.crossFade() }
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
binding.toolbar.updatePadding(top = insets.systemWindowInsetTop)
insets
}
}
override fun onSaveInstanceState(_outState: Bundle) {
var outState = _outState
//add the values, which need to be saved from the drawer to the bundle
if (::sliderView.isInitialized) {
outState = sliderView.saveInstanceState(outState)
}
//add the values, which need to be saved from the accountHeader to the bundle
if (::headerView.isInitialized) {
outState = headerView.saveInstanceState(outState)
}
//add the values, which need to be saved from the crossFader to the bundle
if (::crossFader.isInitialized) {
outState = crossFader.saveInstanceState(outState)
}
super.onSaveInstanceState(outState)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
//handle the click on the back arrow click
return when (item.itemId) {
android.R.id.home -> {
onBackPressed()
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onBackPressed() {
//handle the back press :D close the drawer first and if the drawer is closed close the activity
if (crossFader.isCrossFaded) {
crossFader.crossFade()
} else {
super.onBackPressed()
}
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/AccountDividerDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.app.drawerItems
import android.content.res.ColorStateList
import android.view.View
import androidx.annotation.LayoutRes
import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.app.R
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.AbstractDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.IProfile
import com.mikepenz.materialdrawer.util.getDividerColor
class AccountDividerDrawerItem : AbstractDrawerItem(), IProfile {
override val type: Int
get() = R.id.material_drawer_profile_item_divider
override val layoutRes: Int
@LayoutRes
get() = com.mikepenz.materialdrawer.R.layout.material_drawer_item_divider
override var name: StringHolder? = null
override var description: StringHolder? = null
override var icon: ImageHolder? = null
override var iconColor: ColorStateList? = null
override fun bindView(holder: ViewHolder, payloads: List) {
super.bindView(holder, payloads)
val ctx = holder.itemView.context
//set the identifier from the drawerItem here. It can be used to run tests
holder.itemView.id = hashCode()
//define how the divider should look like
holder.view.isClickable = false
holder.view.isEnabled = false
holder.view.minimumHeight = 1
ViewCompat.setImportantForAccessibility(holder.view,
ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO)
//set the color for the divider
holder.divider.setBackgroundColor(ctx.getDividerColor())
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
class ViewHolder internal constructor(internal val view: View) : RecyclerView.ViewHolder(view) {
internal val divider: View = view.findViewById(com.mikepenz.materialdrawer.R.id.material_drawer_divider)
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomBaseViewHolder.kt
================================================
package com.mikepenz.materialdrawer.app.drawerItems
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.app.R
open class CustomBaseViewHolder(var view: View) : RecyclerView.ViewHolder(view) {
var icon: ImageView = view.findViewById(R.id.material_drawer_icon)
var name: TextView = view.findViewById(R.id.material_drawer_name)
var description: TextView = view.findViewById(R.id.material_drawer_description)
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomCenteredPrimaryDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.app.drawerItems
import com.mikepenz.materialdrawer.app.R
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
class CustomCenteredPrimaryDrawerItem : PrimaryDrawerItem() {
override val layoutRes: Int
get() = R.layout.material_drawer_item_primary_centered
override val type: Int
get() = R.id.material_drawer_item_centered_primary
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomPrimaryDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.app.drawerItems
import com.mikepenz.materialdrawer.holder.ColorHolder
import com.mikepenz.materialdrawer.model.AbstractBadgeableDrawerItem
class CustomPrimaryDrawerItem : AbstractBadgeableDrawerItem() {
var background: ColorHolder? = null
fun withBackgroundColor(backgroundColor: Int): CustomPrimaryDrawerItem {
this.background = ColorHolder.fromColor(backgroundColor)
return this
}
fun withBackgroundRes(backgroundRes: Int): CustomPrimaryDrawerItem {
this.background = ColorHolder.fromColorRes(backgroundRes)
return this
}
override fun bindView(holder: AbstractBadgeableDrawerItem.ViewHolder, payloads: List) {
super.bindView(holder, payloads)
background?.applyToBackground(holder.itemView)
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomUrlBasePrimaryDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.app.drawerItems
import android.net.Uri
import androidx.annotation.StringRes
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.holder.ColorHolder
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.BaseDrawerItem
import com.mikepenz.materialdrawer.util.DrawerImageLoader
import com.mikepenz.materialdrawer.util.setDrawerVerticalPadding
import com.mikepenz.materialdrawer.util.themeDrawerItem
/**
* Created by mikepenz on 03.02.15.
*/
abstract class CustomUrlBasePrimaryDrawerItem : BaseDrawerItem() {
var description: StringHolder? = null
var descriptionTextColor: ColorHolder? = null
fun withIcon(url: String): T {
this.icon = ImageHolder(url)
return this as T
}
fun withIcon(uri: Uri): T {
this.icon = ImageHolder(uri)
return this as T
}
fun withDescription(description: String): T {
this.description = StringHolder(description)
return this as T
}
fun withDescription(@StringRes descriptionRes: Int): T {
this.description = StringHolder(descriptionRes)
return this as T
}
/**
* a helper method to have the logic for all secondaryDrawerItems only once
*
* @param viewHolder
*/
protected fun bindViewHelper(viewHolder: CustomBaseViewHolder) {
val ctx = viewHolder.itemView.context
//set the identifier from the drawerItem here. It can be used to run tests
viewHolder.itemView.id = hashCode()
//get the correct color for the background
val selectedColor = getSelectedColor(ctx)
//get the correct color for the text
val color = getColor(ctx)
val shapeAppearanceModel = getShapeAppearanceModel(ctx)
//set the background for the item
themeDrawerItem(ctx, viewHolder.view, selectedColor, isSelectedBackgroundAnimated, shapeAppearanceModel, isSelected = isSelected)
//set the text for the name
StringHolder.applyTo(this.name, viewHolder.name)
//set the text for the description or hide
StringHolder.applyToOrHide(this.description, viewHolder.description)
//set the colors for textViews
viewHolder.name.setTextColor(color)
//set the description text color
descriptionTextColor?.applyToOr(viewHolder.description, color)
//define the typeface for our textViews
if (typeface != null) {
viewHolder.name.typeface = typeface
viewHolder.description.typeface = typeface
}
//we make sure we reset the image first before setting the new one in case there is an empty one
DrawerImageLoader.instance.cancelImage(viewHolder.icon)
viewHolder.icon.setImageBitmap(null)
//get the drawables for our icon and set it
icon?.applyTo(viewHolder.icon, "customUrlItem")
//for android API 17 --> Padding not applied via xml
setDrawerVerticalPadding(viewHolder.view)
//set the item selected if it is
viewHolder.itemView.isSelected = isSelected
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomUrlPrimaryDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.app.drawerItems
import android.view.View
import android.widget.TextView
import androidx.annotation.LayoutRes
import com.mikepenz.materialdrawer.app.R
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.ColorfulBadgeable
/**
* Created by mikepenz on 03.02.15.
*/
class CustomUrlPrimaryDrawerItem : CustomUrlBasePrimaryDrawerItem(), ColorfulBadgeable {
override var badge: StringHolder? = null
override var badgeStyle: BadgeStyle? = BadgeStyle()
override val type: Int
get() = R.id.material_drawer_item_custom_url_item
override val layoutRes: Int
@LayoutRes
get() = com.mikepenz.materialdrawer.R.layout.material_drawer_item_primary
override fun bindView(holder: ViewHolder, payloads: List) {
super.bindView(holder, payloads)
val ctx = holder.itemView.context
//bind the basic view parts
bindViewHelper(holder)
//set the text for the badge or hide
val badgeVisible = StringHolder.applyToOrHide(badge, holder.badge)
//style the badge if it is visible
if (badgeVisible) {
badgeStyle!!.style(holder.badge, getColor(ctx))
holder.badge.visibility = View.VISIBLE
} else {
holder.badge.visibility = View.GONE
}
//define the typeface for our textViews
if (typeface != null) {
holder.badge.typeface = typeface
}
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
class ViewHolder(view: View) : CustomBaseViewHolder(view) {
internal val badge: TextView = view.findViewById(R.id.material_drawer_badge)
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/GmailDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.app.drawerItems
import android.content.Context
import android.view.View
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.ShapeAppearanceModel
import com.mikepenz.materialdrawer.app.R
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.util.themeDrawerItem
/**
* Describes the main [IDrawerItem] bing used as primary item, following the guidelines
*/
open class GmailDrawerItem : PrimaryDrawerItem() {
override val type: Int
get() = R.id.material_drawer_item_gmail_item
override fun applyDrawerItemTheme(ctx: Context, view: View, selected_color: Int, animate: Boolean, shapeAppearanceModel: ShapeAppearanceModel) {
themeDrawerItem(ctx, view, selected_color, animate, shapeAppearanceModel, R.dimen.gmail_material_drawer_item_background_padding_top_bottom, R.dimen.gmail_material_drawer_item_background_padding_start, R.dimen.gmail_material_drawer_item_background_padding_end, isSelected = isSelected)
}
override fun getShapeAppearanceModel(ctx: Context): ShapeAppearanceModel {
val cornerRadius = ctx.resources.getDimensionPixelSize(R.dimen.gmail_material_drawer_item_corner_radius)
return ShapeAppearanceModel.builder()
.setTopRightCorner(CornerFamily.ROUNDED, cornerRadius.toFloat())
.setBottomRightCorner(CornerFamily.ROUNDED, cornerRadius.toFloat())
.build()
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/IconDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.app.drawerItems
import android.content.Context
import android.content.res.ColorStateList
import android.view.View
import android.widget.ImageView
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.app.R
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.model.AbstractDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.Iconable
import com.mikepenz.materialdrawer.model.interfaces.SelectIconable
import com.mikepenz.materialdrawer.util.createDrawerItemColorStateList
/**
* Created by mikepenz on 03.02.15.
*/
class IconDrawerItem : AbstractDrawerItem(), Iconable, SelectIconable {
override var icon: ImageHolder? = null
override var iconColor: ColorStateList? = null
override var selectedIcon: ImageHolder? = null
override var isIconTinted = false
override val type: Int
get() = R.id.material_drawer_item_icon_only
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_icon_only
override fun bindView(holder: ViewHolder, payloads: List) {
super.bindView(holder, payloads)
val ctx = holder.itemView.context
//set the identifier from the drawerItem here. It can be used to run tests
holder.itemView.id = hashCode()
//get the correct color for the icon
val iconColor = this.iconColor ?: ctx.getPrimaryDrawerIconColor()
//get the drawables for our icon and set it)
val icon = ImageHolder.decideIcon(icon, ctx, iconColor, isIconTinted, 1)
val selectedIcon = ImageHolder.decideIcon(selectedIcon, ctx, iconColor, isIconTinted, 1)
ImageHolder.applyMultiIconTo(icon, selectedIcon, iconColor, isIconTinted, holder.icon)
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
class ViewHolder internal constructor(private val view: View) : RecyclerView.ViewHolder(view) {
var icon: ImageView = view.findViewById(R.id.material_drawer_icon)
}
}
internal fun Context.getPrimaryDrawerIconColor(): ColorStateList {
return createDrawerItemColorStateList(this, com.mikepenz.materialdrawer.R.styleable.MaterialDrawerSliderView_materialDrawerPrimaryIcon)!!
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/OverflowMenuDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.app.drawerItems
import android.view.View
import android.widget.ImageButton
import androidx.annotation.LayoutRes
import androidx.appcompat.widget.PopupMenu
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.sizeDp
import com.mikepenz.materialdrawer.app.R
import com.mikepenz.materialdrawer.model.BaseDescribeableDrawerItem
import com.mikepenz.materialdrawer.model.BaseViewHolder
/**
* Created by mikepenz on 03.02.15.
*/
class OverflowMenuDrawerItem : BaseDescribeableDrawerItem() {
var menu: Int = 0
var onMenuItemClickListener: PopupMenu.OnMenuItemClickListener? = null
var onDismissListener: PopupMenu.OnDismissListener? = null
override val type: Int
get() = R.id.material_drawer_item_overflow_menu
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_overflow_menu_primary
@Deprecated("Use property setter instead")
fun withMenu(menu: Int): OverflowMenuDrawerItem {
this.menu = menu
return this
}
@Deprecated("Use property setter instead")
fun withOnMenuItemClickListener(onMenuItemClickListener: PopupMenu.OnMenuItemClickListener): OverflowMenuDrawerItem {
this.onMenuItemClickListener = onMenuItemClickListener
return this
}
@Deprecated("Use property setter instead")
fun withOnDismissListener(onDismissListener: PopupMenu.OnDismissListener): OverflowMenuDrawerItem {
this.onDismissListener = onDismissListener
return this
}
override fun bindView(holder: ViewHolder, payloads: List) {
super.bindView(holder, payloads)
val ctx = holder.itemView.context
//bind the basic view parts
bindViewHelper(holder)
//handle menu click
holder.menu.setOnClickListener { view ->
val popup = PopupMenu(view.context, view)
val inflater = popup.menuInflater
inflater.inflate(menu, popup.menu)
popup.setOnMenuItemClickListener(onMenuItemClickListener)
popup.setOnDismissListener(onDismissListener)
popup.show()
}
//handle image
holder.menu.setImageDrawable(IconicsDrawable(ctx, GoogleMaterial.Icon.gmd_more_vert).apply { sizeDp = 12; colorList = getIconColor(ctx) })
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
class ViewHolder(view: View) : BaseViewHolder(view) {
internal val menu: ImageButton = view.findViewById(R.id.material_drawer_menu_overflow)
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/fragment/DemoFragment.kt
================================================
package com.mikepenz.materialdrawer.app.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.mikepenz.materialdrawer.app.databinding.FragmentSampleBinding
/**
* A simple [Fragment] subclass.
* This is just a demo fragment with a long scrollable view of editTexts. Don't see this as a reference for anything
*/
class DemoFragment : Fragment() {
private var _binding: FragmentSampleBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
// don't look at this layout it's just a listView to show how to handle the keyboard
_binding = FragmentSampleBinding.inflate(inflater, container, false)
binding.title.text = arguments?.getString(KEY_TITLE)
return binding.root
}
companion object {
private val KEY_TITLE = "title"
fun newInstance(title: String): DemoFragment {
return DemoFragment().apply {
arguments = Bundle().apply { putString(KEY_TITLE, title) }
}
}
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/fragment/DemoMessageFragment.kt
================================================
package com.mikepenz.materialdrawer.app.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.mikepenz.materialdrawer.app.R
import com.mikepenz.materialdrawer.app.databinding.FragmentMessageSampleBinding
class DemoMessageFragment : Fragment() {
private var _binding: FragmentMessageSampleBinding? = null
private val binding get() = _binding!!
private var message: String? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
message = arguments?.let { DemoMessageFragmentArgs.fromBundle(it).message }
_binding = FragmentMessageSampleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.messageTextView.text = "$message"
binding.btnFragmentHome.setOnClickListener { navigateTo(R.id.fragmentHome) }
binding.btnFragment1.setOnClickListener { navigateTo(R.id.messageFragment1) }
binding.btnFragment2.setOnClickListener { navigateTo(R.id.messageFragment2) }
binding.btnFragment3.setOnClickListener { navigateTo(R.id.messageFragment3) }
binding.btnPopup.setOnClickListener { navigateTo(R.id.action_global_fragmentHome) }
}
private fun navigateTo(destination: Int) {
findNavController().navigate(destination)
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/fragment/DrawerFragment.kt
================================================
package com.mikepenz.materialdrawer.app.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesomeBrand
import com.mikepenz.materialdrawer.app.R
import com.mikepenz.materialdrawer.app.databinding.FragmentSimpleSampleBinding
import com.mikepenz.materialdrawer.iconics.withIcon
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import com.mikepenz.materialdrawer.model.SectionDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.withEnabled
import com.mikepenz.materialdrawer.model.interfaces.withIdentifier
import com.mikepenz.materialdrawer.model.interfaces.withName
/**
* A simple [Fragment] subclass.
* This is just a demo fragment with a long scrollable view of editTexts. Don't see this as a reference for anything
*/
class DrawerFragment : Fragment() {
private var _binding: FragmentSimpleSampleBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
// don't look at this layout it's just a listView to show how to handle the keyboard
_binding = FragmentSimpleSampleBinding.inflate(inflater, container, false)
binding.title.text = arguments?.getString(KEY_TITLE)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.slider.apply {
itemAdapter.add(
PrimaryDrawerItem().withName(R.string.drawer_item_home).withIcon(FontAwesome.Icon.faw_home).withIdentifier(1),
PrimaryDrawerItem().withName(R.string.drawer_item_free_play).withIcon(FontAwesome.Icon.faw_gamepad),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye),
SectionDrawerItem().withName(R.string.drawer_item_section_header),
SecondaryDrawerItem().withName(R.string.drawer_item_settings).withIcon(FontAwesome.Icon.faw_cog),
SecondaryDrawerItem().withName(R.string.drawer_item_help).withIcon(FontAwesome.Icon.faw_question).withEnabled(false),
SecondaryDrawerItem().withName(R.string.drawer_item_open_source).withIcon(FontAwesomeBrand.Icon.fab_github),
SecondaryDrawerItem().withName(R.string.drawer_item_contact).withIcon(FontAwesome.Icon.faw_bullhorn)
)
setSelection(1)
setSavedInstance(savedInstanceState)
}
}
override fun onSaveInstanceState(_outState: Bundle) {
var outState = _outState
//add the values which need to be saved from the drawer to the bundle
outState = binding.slider.saveInstanceState(outState)
super.onSaveInstanceState(outState)
}
companion object {
private const val KEY_TITLE = "title"
fun newInstance(title: String): DrawerFragment {
return DrawerFragment().apply {
arguments = Bundle().apply { putString(KEY_TITLE, title) }
}
}
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/fragment/SecondDrawerFragment.kt
================================================
package com.mikepenz.materialdrawer.app.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.materialdrawer.app.R
import com.mikepenz.materialdrawer.app.databinding.FragmentSimpleSampleBinding
import com.mikepenz.materialdrawer.iconics.withIcon
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.withIdentifier
import com.mikepenz.materialdrawer.model.interfaces.withName
/**
* A simple [Fragment] subclass.
* This is just a demo fragment with a long scrollable view of editTexts. Don't see this as a reference for anything
*/
class SecondDrawerFragment : Fragment() {
private var _binding: FragmentSimpleSampleBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
// don't look at this layout it's just a listView to show how to handle the keyboard
_binding = FragmentSimpleSampleBinding.inflate(inflater, container, false)
binding.title.text = arguments?.getString(KEY_TITLE)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.slider.apply {
itemAdapter.add(
PrimaryDrawerItem().withName(R.string.drawer_item_home).withIcon(FontAwesome.Icon.faw_home).withIdentifier(1),
PrimaryDrawerItem().withName(R.string.drawer_item_free_play).withIcon(FontAwesome.Icon.faw_gamepad),
PrimaryDrawerItem().withName(R.string.drawer_item_custom).withIcon(FontAwesome.Icon.faw_eye)
)
setSavedInstance(savedInstanceState)
setSelection(1)
}
}
override fun onSaveInstanceState(_outState: Bundle) {
var outState = _outState
//add the values which need to be saved from the drawer to the bundle
outState = binding.slider.saveInstanceState(outState)
super.onSaveInstanceState(outState)
}
companion object {
private val KEY_TITLE = "title"
fun newInstance(title: String): SecondDrawerFragment {
return SecondDrawerFragment().apply {
arguments = Bundle().apply { putString(KEY_TITLE, title) }
}
}
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/utils/CrossfadeWrapper.kt
================================================
package com.mikepenz.materialdrawer.app.utils
import com.mikepenz.crossfader.Crossfader
import com.mikepenz.materialdrawer.interfaces.ICrossfader
/**
* Created by mikepenz on 18.07.15.
*/
class CrossfadeWrapper(private val crossfader: Crossfader<*>) : ICrossfader {
override val isCrossfaded: Boolean
get() = crossfader.isCrossFaded()
override fun crossfade() {
crossfader.crossFade()
}
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/utils/SystemUtils.kt
================================================
package com.mikepenz.materialdrawer.app.utils
import android.content.res.Resources
object SystemUtils {
val screenOrientation: Int
get() = Resources.getSystem().configuration.orientation
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/utils/UIUtils.kt
================================================
package com.mikepenz.materialdrawer.app.utils
import android.content.Context
import android.content.res.Resources
import android.util.DisplayMetrics
/**
* This method converts dp unit to equivalent pixels, depending on device density.
*
* @param dp A value in dp (density independent pixels) unit. Which we need to convert into pixels
* @param context Context to get resources and device specific display metrics
* @return A float value to represent px equivalent to dp depending on device density
*/
fun convertDpToPixel(dp: Float, context: Context): Float {
val resources: Resources = context.resources
val metrics: DisplayMetrics = resources.displayMetrics
return dp * (metrics.densityDpi / 160f)
}
================================================
FILE: app/src/main/java/com/mikepenz/materialdrawer/app/widget/CrossfadeDrawerLayout.kt
================================================
package com.mikepenz.materialdrawer.app.widget
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.ViewCompat
import androidx.drawerlayout.widget.DrawerLayout
import com.mikepenz.crossfadedrawerlayout.ApplyTransformationListener
import com.mikepenz.crossfadedrawerlayout.animation.ResizeWidthAnimation
import com.mikepenz.materialdrawer.app.R
import com.mikepenz.materialdrawer.app.utils.convertDpToPixel
/**
* Created by mikepenz on 20.10.15.
*/
open class CrossfadeDrawerLayout : DrawerLayout {
var drawerOpened = false
private set
private var touchDown = -1f
private var prevTouch = -1f
//remember the previous width to optimize performance
private var prevWidth = -1
var crossfadeListener: ((containerView: View?, currentSlidePercentage: Float, slideOffset: Int) -> Unit)? = null
var minWidthPx = 0
var maxWidthPx = 0
lateinit var container: ConstraintLayout
private set
lateinit var smallView: ViewGroup
private set
lateinit var largeView: ViewGroup
private set
var isCrossfaded = false
private set
var sliderOnRight = false
constructor(context: Context) : super(context) {
init(context)
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) {
init(context)
}
private fun init(ctx: Context) {
super.addDrawerListener(innerDrawerListener)
//define default valuse for min and max
minWidthPx = convertDpToPixel(72f, ctx).toInt()
maxWidthPx = convertDpToPixel(200f, ctx).toInt()
}
override fun addView(child: View, index: Int) {
super.addView(wrapSliderContent(child, index), index)
}
override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams?) {
super.addView(wrapSliderContent(child, index), index, params)
}
/**
* this will wrap the view which is added to the slider into another layout so we can then overlap the small and large view
*
* @param child
* @param index
* @return
*/
private fun wrapSliderContent(child: View, index: Int): View? {
if (child.id == R.id.crossFadeSlider) {
container = child as ConstraintLayout
largeView = child.findViewById(R.id.crossFadeLargeView)
largeView.alpha = 0f
largeView.visibility = View.GONE
smallView = child.findViewById(R.id.crossFadeSmallView)
return container
}
return child
}
private var innerDrawerListener = object : DrawerListener {
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
drawerOpened = slideOffset == 1f
}
override fun onDrawerOpened(drawerView: View) {
drawerOpened = true
}
override fun onDrawerClosed(drawerView: View) {
val lp = drawerView.layoutParams as MarginLayoutParams
lp.width = minWidthPx
drawerView.layoutParams = lp
//revert alpha :D
smallView.alpha = 1f
smallView.bringToFront()
largeView.alpha = 0f
drawerOpened = false
}
override fun onDrawerStateChanged(newState: Int) {}
}
override fun dispatchTouchEvent(motionEvent: MotionEvent): Boolean {
if (drawerOpened) {
if (motionEvent.action == MotionEvent.ACTION_DOWN) {
touchDown = motionEvent.x
prevTouch = motionEvent.x
return super.dispatchTouchEvent(motionEvent)
} else if (motionEvent.action == MotionEvent.ACTION_UP) {
val click = touchDown == prevTouch
touchDown = -1f
prevTouch = -1f
val lp = container.layoutParams as MarginLayoutParams
val percentage = calculatePercentage(lp.width)
if (percentage > 50) {
fadeUp(DEFAULT_ANIMATION)
} else {
fadeDown(DEFAULT_ANIMATION)
}
return if (click) {
super.dispatchTouchEvent(motionEvent)
} else {
true
}
} else if (motionEvent.action == MotionEvent.ACTION_MOVE && touchDown != -1f) {
val lp = container.layoutParams as MarginLayoutParams
//the current drawer width
var diff = motionEvent.x - touchDown
if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL || sliderOnRight) {
diff *= -1
}
if (diff == 0f) {
//no difference nothing to do
//return super.dispatchTouchEvent(motionEvent);
} else if (diff > 0 && lp.width <= maxWidthPx && lp.width + diff < maxWidthPx && lp.width >= minWidthPx) {
lp.width = (lp.width + diff).toInt()
container.layoutParams = lp
touchDown = motionEvent.x
overlapViews(lp.width)
} else if (diff < 0 && lp.width >= minWidthPx && lp.width + diff > minWidthPx) {
lp.width = (lp.width + diff).toInt()
container.layoutParams = lp
touchDown = motionEvent.x
overlapViews(lp.width)
} else if (lp.width < minWidthPx) {
lp.width = minWidthPx
container.layoutParams = lp
drawerOpened = false
touchDown = -1f
overlapViews(minWidthPx)
} else if (lp.width + diff < minWidthPx) { //return super.dispatchTouchEvent(motionEvent);
}
//return true;
}
}
return super.dispatchTouchEvent(motionEvent)
}
override fun onTouchEvent(motionEvent: MotionEvent): Boolean {
try {
return super.onTouchEvent(motionEvent)
} catch (ex: RuntimeException) {
ex.printStackTrace()
}
return false
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
try {
return super.onInterceptTouchEvent(ev)
} catch (ex: IllegalArgumentException) {
ex.printStackTrace()
}
return false
}
override fun openDrawer(gravity: Int) {
drawerOpened = true
super.openDrawer(gravity)
}
override fun openDrawer(drawerView: View) {
drawerOpened = true
super.openDrawer(drawerView)
}
override fun openDrawer(drawerView: View, animate: Boolean) {
drawerOpened = true
super.openDrawer(drawerView, animate)
}
override fun openDrawer(gravity: Int, animate: Boolean) {
drawerOpened = true
super.openDrawer(gravity, animate)
}
override fun closeDrawer(drawerView: View) {
drawerOpened = false
container.clearAnimation()
super.closeDrawer(drawerView)
}
override fun closeDrawer(drawerView: View, animate: Boolean) {
drawerOpened = false
container.clearAnimation()
super.closeDrawer(drawerView, animate)
}
override fun closeDrawer(gravity: Int) {
drawerOpened = false
container.clearAnimation()
super.closeDrawer(gravity)
}
override fun closeDrawer(gravity: Int, animate: Boolean) {
drawerOpened = false
container.clearAnimation()
super.closeDrawer(gravity, animate)
}
/**
* crossfade the small to the large view (with default animation time)
*/
@JvmOverloads
fun crossfade(duration: Int = DEFAULT_ANIMATION) {
if (isCrossfaded) {
fadeDown(duration)
} else {
fadeUp(duration)
}
}
/**
* animate to the large view
*
* @param duration
*/
fun fadeUp(duration: Int) { //animate up
container.clearAnimation()
val anim = ResizeWidthAnimation(container, maxWidthPx, ApplyTransformationListener { width -> overlapViews(width) })
anim.duration = duration.toLong()
container.startAnimation(anim)
}
/**
* animate to the small view
*
* @param duration
*/
fun fadeDown(duration: Int) { //fade down
container.clearAnimation()
val anim = ResizeWidthAnimation(container, minWidthPx, ApplyTransformationListener { width -> overlapViews(width) })
anim.duration = duration.toLong()
container.startAnimation(anim)
}
/**
* calculate the percentage to how many percent the slide is already visible
*
* @param width
* @return
*/
private fun calculatePercentage(width: Int): Float {
val absolute = maxWidthPx - minWidthPx
val current = width - minWidthPx
val percentage = 100.0f * current / absolute
//we can assume that we are crossfaded if the percentage is > 90
isCrossfaded = percentage > 90
return percentage
}
/**
* overlap the views and provide the crossfade effect
*
* @param width
*/
private fun overlapViews(width: Int) {
if (width == prevWidth) {
return
}
//remember this width so it is't processed twice
prevWidth = width
val percentage = calculatePercentage(width)
val alpha = percentage / 100
smallView.alpha = 1f
smallView.isClickable = false
largeView.bringToFront()
largeView.alpha = alpha
largeView.isClickable = true
largeView.visibility = if (alpha > 0.01f) View.VISIBLE else View.GONE
//notify the crossfadeListener
crossfadeListener?.invoke(container, calculatePercentage(width), width)
}
companion object {
private const val DEFAULT_ANIMATION = 200
}
}
================================================
FILE: app/src/main/res/layout/activity_embedded.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_mini_drawer.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_multi_sample.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_persistent_drawer.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_sample.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_sample_actionbar.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_sample_collapsing_toolbar.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_sample_crossfader.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_sample_fragment.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_sample_fullscreen.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_sample_nav.xml
================================================
================================================
FILE: app/src/main/res/layout/footer.xml
================================================
================================================
FILE: app/src/main/res/layout/fragment_message_sample.xml
================================================
================================================
FILE: app/src/main/res/layout/fragment_sample.xml
================================================
================================================
FILE: app/src/main/res/layout/fragment_simple_sample.xml
================================================
================================================
FILE: app/src/main/res/layout/header.xml
================================================
================================================
FILE: app/src/main/res/layout/material_drawer_compact_persistent_header.xml
================================================
================================================
FILE: app/src/main/res/layout/material_drawer_item_icon_only.xml
================================================
================================================
FILE: app/src/main/res/layout/material_drawer_item_overflow_menu_primary.xml
================================================
================================================
FILE: app/src/main/res/layout/material_drawer_item_primary_centered.xml
================================================
================================================
FILE: app/src/main/res/menu/cab.xml
================================================
================================================
FILE: app/src/main/res/menu/embedded.xml
================================================
================================================
FILE: app/src/main/res/menu/example_menu.xml
================================================
-
-
================================================
FILE: app/src/main/res/menu/fragment_menu.xml
================================================
================================================
FILE: app/src/main/res/menu/main.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: app/src/main/res/navigation/navigation.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#039be5
#006db3
#63ccff
#2196F3
#039be5
#039be5
================================================
FILE: app/src/main/res/values/dimens.xml
================================================
4dp
48dp
32dp
20dp
0dp
8dp
4dp
================================================
FILE: app/src/main/res/values/ic_launcher_background.xml
================================================
#3F51B5
================================================
FILE: app/src/main/res/values/ids.xml
================================================
================================================
FILE: app/src/main/res/values/strings.xml
================================================
MaterialDrawer
Settings
PersistentHeader Drawer
Smaller persistent account header
CompactHeader Drawer
Smaller account header
ActionBar Drawer
Shows how to use with an actionbar
Multi Drawer
Left and Right side drawer
NonTranslucentStatus Drawer
Drawer below statusBar
Advanced Drawer
More advanced drawer features
Embedded Drawer
Embed the drawer anywhere
MiniDrawer
Always visible MiniDrawer
FullScreen Drawer
Drawer in FullScreen activity
Drawer in different view hirachy
Menu Drawer
Drawer from menu.xml
Fragment Drawer
Use drawer within fragments
CollapsingToolbar Drawer
Uses a CollapsingToolbarLayout
CrossfadeDrawerLayout
2-Step Drawer behavior
Nav Controller Drawer
Drawer with a Nav Controller
Menu Drawer Item
Sample menu item with overflow button
Home
Free Play
Custom
Section Header
Settings
Help
Open Source
Contact
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/main/res/values/themes.xml
================================================
================================================
FILE: build.gradle.kts
================================================
plugins {
alias(baseLibs.plugins.conventionPlugin)
alias(baseLibs.plugins.kotlinAndroid) apply false
alias(baseLibs.plugins.androidApplication) apply false
alias(baseLibs.plugins.androidLibrary) apply false
alias(baseLibs.plugins.dokka)
alias(baseLibs.plugins.aboutLibraries) apply false
alias(baseLibs.plugins.mavenPublish) apply false
alias(libs.plugins.navSafeArgs) apply false
}
================================================
FILE: gradle/libs.versions.toml
================================================
[versions]
# androidx
appcompat = "1.7.0"
cardview = "1.0.0"
constraintLayout = "2.2.0"
core = "1.15.0"
drawerlayout = "1.2.0"
navigation = "2.8.6"
recyclerView = "1.4.0"
# google
material = "1.12.0"
# other
fastAdapter = "5.7.0"
iconics = "5.4.0"
itemAnimators = "1.1.0"
[plugins]
navSafeArgs = { id = "androidx.navigation.safeargs", version.ref = "navigation" }
[libraries]
# androidx
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
androidx-cardView = { module = "androidx.cardview:cardview", version.ref = "cardview" }
androidx-constraintLayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintLayout" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "core" }
androidx-drawerlayout = { module = "androidx.drawerlayout:drawerlayout", version.ref = "drawerlayout" }
androidx-navigation-fragment = { module = "androidx.navigation:navigation-fragment", version.ref = "navigation" }
androidx-navigation-runtime = { module = "androidx.navigation:navigation-runtime-ktx", version.ref = "navigation" }
androidx-navigation-ui = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navigation" }
androidx-recyclerView = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerView" }
# google
google-material = { module = "com.google.android.material:material", version.ref = "material" }
# other
fastAdapter-core = { module = "com.mikepenz:fastadapter", version.ref = "fastAdapter" }
fastAdapter-expandable = { module = "com.mikepenz:fastadapter-extensions-expandable", version.ref = "fastAdapter" }
iconics-core = { module = "com.mikepenz:iconics-core", version.ref = "iconics" }
itemAnimators-core = { module = "com.mikepenz:itemanimators", version.ref = "itemAnimators" }
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradle.properties
================================================
# Maven stuff
GROUP=com.mikepenz
VERSION_NAME=10.0.0-b01
VERSION_CODE=10000
POM_URL=https://github.com/mikepenz/MaterialDrawer
POM_SCM_URL=https://github.com/mikepenz/MaterialDrawer
POM_SCM_CONNECTION=scm:git@github.com:mikepenz/MaterialDrawer.git
POM_SCM_DEV_CONNECTION=scm:git@github.com:mikepenz/MaterialDrawer.git
POM_SCM_URL_ISSUES=https://github.com/mikepenz/MaterialDrawer/issues
POM_GITHUB_REPO=mikepenz/MaterialDrawer
POM_GITHUB_README=README.md
POM_LICENCE_NAME=Apache-2.0
POM_LICENCE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=mikepenz
POM_DEVELOPER_NAME=Mike Penz
# Project-wide Gradle settings.
org.gradle.jvmargs=-Xmx1536m
android.useAndroidX=true
android.enableJetifier=false
org.gradle.caching=true
org.gradle.configuration-cache=false
android.suppressUnsupportedCompileSdk=34
# Dokka
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true
# convention plugin
com.mikepenz.kotlin.warningsAsErrors.enabled=false
================================================
FILE: gradlew
================================================
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: materialdrawer/.gitignore
================================================
/build
================================================
FILE: materialdrawer/build.gradle.kts
================================================
plugins {
id("com.mikepenz.convention.android-library")
id("com.mikepenz.convention.kotlin")
id("com.mikepenz.convention.publishing")
}
android {
namespace = "com.mikepenz.materialdrawer"
}
dependencies {
implementation(libs.androidx.appcompat)
implementation(libs.androidx.core.ktx)
api(libs.androidx.drawerlayout)
api(libs.androidx.recyclerView)
implementation(libs.androidx.cardView)
implementation(libs.google.material)
// add the constraintLayout used to create the items and headers
implementation(libs.androidx.constraintLayout)
// used to fill the RecyclerView with the items
// and provides single and multi selection, expandable items
// https://github.com/mikepenz/FastAdapter
api(libs.fastAdapter.core)
api(libs.fastAdapter.expandable)
}
================================================
FILE: materialdrawer/gradle.properties
================================================
POM_NAME=MaterialDrawer Library
POM_DESCRIPTION=The flexible, easy to use, all in one drawer library for your Android project.
POM_ARTIFACT_ID=materialdrawer
POM_PACKAGING=aar
================================================
FILE: materialdrawer/proguard-rules.txt
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Entwicklung/android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: materialdrawer/src/main/AndroidManifest.xml
================================================
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/holder/BadgeStyle.kt
================================================
package com.mikepenz.materialdrawer.holder
import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.core.view.ViewCompat
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.model.utils.BadgeDrawableBuilder
/**
* Class to allow defining a BadgeStyle for the `BadgeDrawerItem`
*/
open class BadgeStyle {
/** defines the drawable to use to define the rounded corners */
var gradientDrawable = R.drawable.material_drawer_badge
/** defines the background drawable */
var badgeBackground: Drawable? = null
/** the default color */
var color: ColorHolder? = null
/** the pressed color */
var colorPressed: ColorHolder? = null
/**
* the text size
* NOTE: Will only apply on 21+, also if applying, ensure to apply to all views
*/
var textSizeSp: Float? = null
private var _textColor: ColorHolder? = null
/** defines the default text color */
var textColor: ColorHolder?
get() = _textColor
set(value) {
_textColorStateList = null
_textColor = value
}
private var _textColorStateList: ColorStateList? = null
/** defines the alternative text color state list */
var textColorStateList: ColorStateList?
get() = _textColorStateList
set(value) {
_textColor = null
_textColorStateList = value
}
/** the corner radious */
var corners: DimenHolder? = null
/** dcustom padding to the bottom (default 2dp) */
var paddingTopBottom = DimenHolder.fromDp(2)
/** custom padding to the right (default 3dp) */
var paddingLeftRight = DimenHolder.fromDp(3)
/** the min width to set (default 20dp) */
var minWidth = DimenHolder.fromDp(20)
/**
* elevation to apply on the view
* NOTE: Will only apply on 21+, also if applying, ensure to apply to all views
*/
var elevation: DimenHolder? = null
constructor()
constructor(@ColorInt color: Int, @ColorInt colorPressed: Int) {
this.color = ColorHolder.fromColor(color)
this.colorPressed = ColorHolder.fromColor(colorPressed)
}
constructor(@DrawableRes gradientDrawable: Int, @ColorInt color: Int, @ColorInt colorPressed: Int, @ColorInt textColor: Int) {
this.gradientDrawable = gradientDrawable
this.color = ColorHolder.fromColor(color)
this.colorPressed = ColorHolder.fromColor(colorPressed)
this.textColor = ColorHolder.fromColor(textColor)
}
/** styles theprovided textView with this style, and the provided colorStateList */
@JvmOverloads
open fun style(badgeTextView: TextView, colorStateList: ColorStateList? = null) {
val ctx = badgeTextView.context
//set background for badge
if (badgeBackground == null) {
ViewCompat.setBackground(badgeTextView, BadgeDrawableBuilder(this).build(ctx))
} else {
ViewCompat.setBackground(badgeTextView, badgeBackground)
}
val textSizeSp = textSizeSp
if (textSizeSp != null) {
badgeTextView.textSize = textSizeSp
} else {
// keep the size it is defined in the layout
}
//set the badge text color
when {
textColor != null -> textColor?.applyToOr(badgeTextView, null)
textColorStateList != null -> badgeTextView.setTextColor(textColorStateList)
colorStateList != null -> badgeTextView.setTextColor(colorStateList)
}
//set the padding
val paddingLeftRight = this.paddingLeftRight.asPixel(ctx)
val paddingTopBottom = this.paddingTopBottom.asPixel(ctx)
badgeTextView.setPadding(paddingLeftRight, paddingTopBottom, paddingLeftRight, paddingTopBottom)
//set the min width
badgeTextView.minWidth = minWidth.asPixel(ctx)
// set elevation if expected
elevation?.let { elev ->
ViewCompat.setElevation(badgeTextView, elev.asPixel(ctx).toFloat())
}
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/holder/ColorHolder.kt
================================================
package com.mikepenz.materialdrawer.holder
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.view.View
import android.widget.TextView
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import com.mikepenz.materialdrawer.util.getThemeColorFromAttrOrRes
/**
* Defines a custom holder class to support providing color either as colorRes or colorInt. Does not require a [Context] and will resolve the value when applying.
*/
open class ColorHolder {
/** defines the color as an integer */
var colorInt = 0
internal set
/** defines the color as a ressource */
var colorRes = -1
internal set
/**
* set the textColor of the ColorHolder to an drawable
*
* @param ctx
* @param drawable
*/
open fun applyTo(ctx: Context, drawable: GradientDrawable) {
if (colorInt != 0) {
drawable.setColor(colorInt)
} else if (colorRes != -1) {
drawable.setColor(ContextCompat.getColor(ctx, colorRes))
}
}
/**
* set the textColor of the ColorHolder to a view
*
* @param view
*/
open fun applyToBackground(view: View) {
if (colorInt != 0) {
view.setBackgroundColor(colorInt)
} else if (colorRes != -1) {
view.setBackgroundResource(colorRes)
}
}
/**
* a small helper to set the text color to a textView null save
*/
open fun applyToOr(textView: TextView, colorDefault: ColorStateList?) {
when {
colorInt != 0 -> {
textView.setTextColor(colorInt)
}
colorRes != -1 -> {
textView.setTextColor(ContextCompat.getColor(textView.context, colorRes))
}
colorDefault != null -> {
textView.setTextColor(colorDefault)
}
}
}
/**
* a small helper class to get the color from the colorHolder or from the theme or from the default color value
*/
open fun color(ctx: Context, @AttrRes colorStyle: Int, @ColorRes colorDefaultRes: Int): Int { //get the color from the holder else from the theme
val color = color(ctx)
return if (color == 0) {
ctx.getThemeColorFromAttrOrRes(colorStyle, colorDefaultRes)
} else {
color
}
}
/**
* a small helper to get the color from the colorHolder
*/
open fun color(ctx: Context): Int {
if (colorInt == 0 && colorRes != -1) {
colorInt = ContextCompat.getColor(ctx, colorRes)
}
return colorInt
}
companion object {
/**
* Constructs a [ColorHolder] given a color resource
*/
fun fromColorRes(@ColorRes colorRes: Int): ColorHolder {
val colorHolder = ColorHolder()
colorHolder.colorRes = colorRes
return colorHolder
}
/**
* Constructs a [ColorHolder] given a color integer
*/
fun fromColor(@ColorInt colorInt: Int): ColorHolder {
val colorHolder = ColorHolder()
colorHolder.colorInt = colorInt
return colorHolder
}
/**
* a small static helper class to get the color from the colorHolder or from the theme or from the default color value
*/
fun color(colorHolder: ColorHolder?, ctx: Context, @AttrRes colorStyle: Int, @ColorRes colorDefault: Int): Int {
return colorHolder?.color(ctx, colorStyle, colorDefault)
?: ctx.getThemeColorFromAttrOrRes(colorStyle, colorDefault)
}
/**
* a small static helper class to get the color from the colorHolder
*/
fun color(colorHolder: ColorHolder?, ctx: Context): Int {
return colorHolder?.color(ctx) ?: 0
}
/**
* a small static helper to set the text color to a textView null save
*/
fun applyToOr(colorHolder: ColorHolder?, textView: TextView?, colorDefault: ColorStateList?) {
if (colorHolder != null && textView != null) {
colorHolder.applyToOr(textView, colorDefault)
} else textView?.setTextColor(colorDefault)
}
/**
* a small static helper to set the color to a GradientDrawable null save
*/
fun applyToOrTransparent(colorHolder: ColorHolder?, ctx: Context, gradientDrawable: GradientDrawable?) {
if (colorHolder != null && gradientDrawable != null) {
colorHolder.applyTo(ctx, gradientDrawable)
} else gradientDrawable?.setColor(Color.TRANSPARENT)
}
}
}
/**
* a small static helper class to get the color from the colorHolder or from the theme or from the default color value
*/
fun ColorHolder?.applyColor(ctx: Context, @AttrRes colorStyle: Int, @ColorRes colorDefault: Int): Int {
return ColorHolder.color(this, ctx, colorStyle, colorDefault)
}
/**
* a small static helper class to get the color from the colorHolder
*/
fun ColorHolder?.applyColor(ctx: Context): Int {
return ColorHolder.color(this, ctx)
}
/**
* a small static helper to set the text color to a textView null save
*/
fun ColorHolder?.applyToOrDefault(textView: TextView?, colorDefault: ColorStateList) {
ColorHolder.applyToOr(this, textView, colorDefault)
}
/**
* a small static helper to set the color to a GradientDrawable null save
*/
fun ColorHolder?.applyToOrTransparent(ctx: Context, gradientDrawable: GradientDrawable?) {
ColorHolder.applyToOrTransparent(this, ctx, gradientDrawable)
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/holder/DimenHolder.kt
================================================
package com.mikepenz.materialdrawer.holder
import android.content.Context
import android.content.res.Resources
import android.util.DisplayMetrics
import androidx.annotation.DimenRes
import androidx.annotation.Dimension
import androidx.annotation.Dimension.Companion.DP
import androidx.annotation.Dimension.Companion.PX
/**
* Defines a custom holder class to support providing dimension either as pixel, dp or dimensRes. Does not require a [Context] and will resolve the value when applying.
*/
open class DimenHolder {
/** Defines the pixel dimension */
var pixel = Int.MIN_VALUE
internal set
/** Defines the dp dimension */
var dp = Int.MIN_VALUE
internal set
/** Defines the resource dimension */
var resource = Int.MIN_VALUE
internal set
/** Resturns this [DimenHolder]`s dimension as pixel value */
open fun asPixel(ctx: Context): Int {
return when {
pixel != Int.MIN_VALUE -> pixel
dp != Int.MIN_VALUE -> ctx.convertDpToPixel(dp)
resource != Int.MIN_VALUE -> ctx.resources.getDimensionPixelSize(resource)
else -> 0
}
}
companion object {
/**
* Constructs a [DimenHolder] given a pixel value
*/
fun fromPixel(@Dimension(unit = PX) pixel: Int): DimenHolder {
val dimenHolder = DimenHolder()
dimenHolder.pixel = pixel
return dimenHolder
}
/**
* Constructs a [DimenHolder] given a dp value
*/
fun fromDp(@Dimension(unit = DP) dp: Int): DimenHolder {
val dimenHolder = DimenHolder()
dimenHolder.dp = dp
return dimenHolder
}
/**
* Constructs a [DimenHolder] given a resource id
*/
fun fromResource(@DimenRes resource: Int): DimenHolder {
val dimenHolder = DimenHolder()
dimenHolder.resource = resource
return dimenHolder
}
}
}
/**
* This method converts dp unit to equivalent pixels, depending on device density.
*/
private fun Context.convertDpToPixel(dp: Int): Int {
val resources: Resources = resources
val metrics: DisplayMetrics = resources.displayMetrics
return (dp * (metrics.densityDpi / 160.0)).toInt()
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/holder/ImageHolder.kt
================================================
package com.mikepenz.materialdrawer.holder
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.PorterDuff
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import android.view.View
import android.widget.ImageView
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import com.mikepenz.materialdrawer.util.DrawerImageLoader
import com.mikepenz.materialdrawer.util.FixStateListDrawable
import com.mikepenz.materialdrawer.util.getIconStateList
import java.io.FileNotFoundException
/**
* Defines a custom holder class to support providing images either as uri, drawable, bitmap, or resource. Does not require a [Context] and will resolve the value when applying.
*/
open class ImageHolder {
/** Defines the uri image */
var uri: Uri? = null
internal set
/** Defines the drawable image */
var icon: Drawable? = null
internal set
/** Defines the bitmap image */
var bitmap: Bitmap? = null
internal set
/** Defines the resource image */
var iconRes = -1
internal set
protected constructor()
constructor(url: String) {
this.uri = Uri.parse(url)
}
constructor(uri: Uri) {
this.uri = uri
}
constructor(icon: Drawable?) {
this.icon = icon
}
constructor(bitmap: Bitmap) {
this.bitmap = bitmap
}
constructor(@DrawableRes iconRes: Int) {
this.iconRes = iconRes
}
/**
* sets an existing image to the imageView
*/
@JvmOverloads
open fun applyTo(imageView: ImageView, tag: String? = null): Boolean {
val uri = uri
when {
uri != null -> {
val consumed = DrawerImageLoader.instance.setImage(imageView, uri, tag)
if (!consumed) {
imageView.setImageURI(uri)
}
}
icon != null -> imageView.setImageDrawable(icon)
bitmap != null -> imageView.setImageBitmap(bitmap)
iconRes != -1 -> imageView.setImageResource(iconRes)
else -> {
imageView.setImageBitmap(null)
return false
}
}
return true
}
/**
* this only handles Drawables
*/
open fun decideIcon(ctx: Context, iconColor: ColorStateList, tint: Boolean, paddingDp: Int = 1): Drawable? {
var icon = this.icon
when {
iconRes != -1 -> icon = ContextCompat.getDrawable(ctx, iconRes)
uri != null -> try {
val inputStream = ctx.contentResolver.openInputStream(uri!!)
icon = Drawable.createFromStream(inputStream, uri!!.toString())
} catch (e: FileNotFoundException) {
//no need to handle this
}
bitmap != null -> icon = BitmapDrawable(ctx.resources, bitmap)
}
//if we got an icon AND we have auto tinting enabled AND it is no IIcon, tint it ;)
if (icon != null && tint) {
icon = icon.mutate()
icon.setColorFilter(iconColor.defaultColor, PorterDuff.Mode.SRC_IN)
}
return icon
}
companion object {
/**
* a small static helper to set the image from the imageHolder nullSave to the imageView
*/
@JvmOverloads
fun applyTo(imageHolder: ImageHolder?, imageView: ImageView?, tag: String? = null): Boolean {
return if (imageHolder != null && imageView != null) {
imageHolder.applyTo(imageView, tag)
} else false
}
/**
* a small static helper to set the image from the imageHolder nullSave to the imageView and hide the view if no image was set
*/
@JvmOverloads
fun applyToOrSetInvisible(imageHolder: ImageHolder?, imageView: ImageView?, tag: String? = null) {
val imageSet = applyTo(imageHolder, imageView, tag)
if (imageView != null) {
if (imageSet) {
imageView.visibility = View.VISIBLE
} else {
imageView.visibility = View.INVISIBLE
}
}
}
/**
* a small static helper to set the image from the imageHolder nullSave to the imageView and hide the view if no image was set
*/
@JvmOverloads
fun applyToOrSetGone(imageHolder: ImageHolder, imageView: ImageView?, tag: String? = null) {
val imageSet = applyTo(imageHolder, imageView, tag)
if (imageView != null) {
if (imageSet) {
imageView.visibility = View.VISIBLE
} else {
imageView.visibility = View.GONE
}
}
}
/**
* a small static helper which catches nulls for us
*/
fun decideIcon(imageHolder: ImageHolder?, ctx: Context, iconColor: ColorStateList, tint: Boolean, paddingDp: Int = 1): Drawable? {
return imageHolder?.decideIcon(ctx, iconColor, tint, paddingDp)
}
/**
* decides which icon to apply or hide this view
*/
fun applyDecidedIconOrSetGone(imageHolder: ImageHolder?, imageView: ImageView?, iconColor: ColorStateList, tint: Boolean, paddingDp: Int = 1) {
if (imageHolder != null && imageView != null) {
val drawable = decideIcon(imageHolder, imageView.context, iconColor, tint, paddingDp)
when {
drawable != null -> {
imageView.setImageDrawable(drawable)
imageView.visibility = View.VISIBLE
}
imageHolder.bitmap != null -> {
imageView.setImageBitmap(imageHolder.bitmap)
imageView.visibility = View.VISIBLE
}
else -> imageView.visibility = View.GONE
}
} else if (imageView != null) {
imageView.visibility = View.GONE
}
}
/**
* a small static helper to set a multi state drawable on a view
*/
fun applyMultiIconTo(icon: Drawable?, selectedIcon: Drawable?, iconColor: ColorStateList, tinted: Boolean, imageView: ImageView) {
//if we have an icon then we want to set it
if (icon != null) {
//if we got a different color for the selectedIcon we need a StateList
if (selectedIcon != null) {
if (tinted) {
imageView.setImageDrawable(FixStateListDrawable(icon, selectedIcon, iconColor))
} else {
imageView.setImageDrawable(getIconStateList(icon, selectedIcon))
}
} else if (tinted) {
imageView.setImageDrawable(FixStateListDrawable(icon, iconColor))
} else {
imageView.setImageDrawable(icon)
}
//make sure we display the icon
imageView.visibility = View.VISIBLE
} else {
//hide the icon
imageView.visibility = View.GONE
}
}
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/holder/StringHolder.kt
================================================
package com.mikepenz.materialdrawer.holder
import android.content.Context
import android.view.View
import android.widget.TextView
import androidx.annotation.StringRes
/**
* Defines a custom holder class to support providing strings either as string or resource. Does not require a [Context] and will resolve the value when applying.
*/
open class StringHolder {
/** Defines the string text */
var textString: CharSequence? = null
internal set
/** Defines the string resource */
var textRes = -1
internal set
constructor(text: CharSequence?) {
this.textString = text
}
constructor(@StringRes textRes: Int) {
this.textRes = textRes
}
/**
* Applies the text to a [TextView]
*/
open fun applyTo(textView: TextView?) {
when {
textString != null -> textView?.text = textString
textRes != -1 -> textView?.setText(textRes)
else -> textView?.text = ""
}
}
/**
* Applies the [TextView] if no text given, hide the textView
*/
open fun applyToOrHide(textView: TextView?): Boolean {
textView ?: return false
return when {
textString != null -> {
textView.text = textString
textView.visibility = View.VISIBLE
true
}
textRes != -1 -> {
textView.setText(textRes)
textView.visibility = View.VISIBLE
true
}
else -> {
textView.visibility = View.GONE
false
}
}
}
/**
* Returns the text as [String]
*/
open fun getText(ctx: Context): String? {
if (textString != null) {
return textString.toString()
} else if (textRes != -1) {
return ctx.getString(textRes)
}
return null
}
companion object {
/**
* Helper to apply the text to a [TextView]
*/
fun applyTo(text: StringHolder?, textView: TextView?) {
text?.applyTo(textView)
}
/**
* Helper to apply the text to a [TextView] or hide if null
*/
fun applyToOrHide(text: StringHolder?, textView: TextView?): Boolean {
return if (text != null) {
text.applyToOrHide(textView)
} else {
textView?.visibility = View.GONE
false
}
}
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/interfaces/ICrossfader.kt
================================================
package com.mikepenz.materialdrawer.interfaces
/**
* Helper interface to allow providing crossfade functionality with different implementations to the drawer
*/
interface ICrossfader {
/**
* returns true if currently crossfaded
*/
val isCrossfaded: Boolean
/**
* allows to toggle the crossfade state
*/
fun crossfade()
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/interfaces/OnCheckedChangeListener.kt
================================================
package com.mikepenz.materialdrawer.interfaces
import android.widget.CompoundButton
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
/**
* Interface definition for a callback to be invoked when the checked state
* of a compound button changed.
*/
interface OnCheckedChangeListener {
/**
* Called when the checked state of a compound button has changed.
*/
fun onCheckedChanged(drawerItem: IDrawerItem<*>, buttonView: CompoundButton, isChecked: Boolean)
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/interfaces/OnPostBindViewListener.kt
================================================
package com.mikepenz.materialdrawer.interfaces
import android.view.View
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
/**
* Defines the listener fired after binding a view
*/
interface OnPostBindViewListener {
/**
* allows you to hook in the BindView method and modify the view after binding
*/
fun onBindView(drawerItem: IDrawerItem<*>, view: View)
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractBadgeableDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.view.View
import android.widget.TextView
import androidx.annotation.LayoutRes
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.ColorfulBadgeable
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
/**
* An abstract [IDrawerItem] implementation describing an element which supports badges
*/
abstract class AbstractBadgeableDrawerItem- > : BaseDescribeableDrawerItem
- (), ColorfulBadgeable {
override var badge: StringHolder? = null
override var badgeStyle: BadgeStyle? = BadgeStyle()
override/*"PRIMARY_ITEM"*/ val type: Int
get() = R.id.material_drawer_item_primary
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_primary
override fun bindView(holder: ViewHolder, payloads: List
) {
super.bindView(holder, payloads)
val ctx = holder.itemView.context
//bind the basic view parts
bindViewHelper(holder)
//set the text for the badge or hide
val badgeVisible = StringHolder.applyToOrHide(badge, holder.badge)
//style the badge if it is visible
if (badgeVisible) {
badgeStyle?.style(holder.badge, getColor(ctx))
holder.badge.visibility = View.VISIBLE
} else {
holder.badge.visibility = View.GONE
}
//define the typeface for our textViews
if (typeface != null) {
holder.badge.typeface = typeface
}
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
open class ViewHolder(view: View) : BaseViewHolder(view) {
internal val badge: TextView = view.findViewById(R.id.material_drawer_badge)
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Typeface
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.CallSuper
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.shape.ShapeAppearanceModel
import com.mikepenz.fastadapter.IItemVHFactory
import com.mikepenz.fastadapter.IParentItem
import com.mikepenz.fastadapter.ISubItem
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.holder.ColorHolder
import com.mikepenz.materialdrawer.interfaces.OnPostBindViewListener
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.util.getPrimaryDrawerTextColor
import com.mikepenz.materialdrawer.util.getSelectedColor
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView.Companion.DEFAULT_SELECTED_BACKGROUND_ANIMATED
/**
* The base abstract [IDrawerItem] implementation describing a drawerItem with the general functionality
*/
abstract class AbstractDrawerItem : IDrawerItem, Selectable, SelectableColor, Tagable, Typefaceable {
// the identifier for this item
override var identifier: Long = -1
// the tag for this item
override var tag: Any? = null
// defines if this item is enabled
override var isEnabled = true
/** The factory to use for creating this item, this does not have to be provided if the IItemFactory is implemented by this item too */
override val factory: IItemVHFactory? = null
// defines if the item is selected
override var isSelected = false
// defines if this item is selectable
override var isSelectable = true
// defines if the item's background' change should be animated when it is (de)selected
var isSelectedBackgroundAnimated = DEFAULT_SELECTED_BACKGROUND_ANIMATED
// defines the content descripton of items
var contentDescription: String? = null
override var selectedColor: ColorHolder? = null
override var typeface: Typeface? = null
open var onDrawerItemClickListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null
var onPostBindViewListener: OnPostBindViewListener? = null
protected set
// the parent of this item
override var parent: IParentItem<*>? = null
// the subItems to expand for this item
private var _subItems: MutableList> = mutableListOf()
override var subItems: MutableList>
get() = _subItems
set(subItems) {
this._subItems = subItems
subItems.let {
for (subItem in it) {
subItem.parent = this
}
}
}
//if the this item is currently expanded
override var isExpanded = false
/**
* overwrite this method and return true if the item should auto expand on click, false if you want to disable this
*
* @return true if this item should auto expand in the adapter
*/
override val isAutoExpanding: Boolean
get() = true
@Deprecated("Please consider to replace with the actual property setter")
open fun withContentDescription(contentDescription: String?): T {
this.contentDescription = contentDescription
return this as T
}
/**
* set if this item is selectable
*
* @param selectedBackgroundAnimated true if this item's background should fade when it is (de) selected
* @return
*/
@Deprecated("Please consider to replace with the actual property setter")
fun withSelectedBackgroundAnimated(selectedBackgroundAnimated: Boolean): T {
this.isSelectedBackgroundAnimated = selectedBackgroundAnimated
return this as T
}
/**
* this listener is called when an item is clicked in the drawer.
* WARNING: don't overwrite this in the Switch / Toggle drawerItems if you want the toggle / switch to be selected
* if the item is clicked and the item is not selectable.
*
* @param onDrawerItemClickListener
* @return
*/
@Deprecated("Please consider to replace with the actual property setter")
open fun withOnDrawerItemClickListener(onDrawerItemClickListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null): T {
this.onDrawerItemClickListener = onDrawerItemClickListener
return this as T
}
/**
* add this listener and hook in if you want to modify a drawerItems view without creating a custom drawer item
*
* @param onPostBindViewListener
* @return
*/
@Deprecated("Please consider to replace with the actual property setter")
fun withPostOnBindViewListener(onPostBindViewListener: OnPostBindViewListener): T {
this.onPostBindViewListener = onPostBindViewListener
return this as T
}
/**
* is called after bindView to allow some post creation setps
*
* @param drawerItem the drawerItem which is bound to the view
* @param view the currently view which will be bound
*/
protected fun onPostBindView(drawerItem: IDrawerItem<*>, view: View) {
onPostBindViewListener?.onBindView(drawerItem, view)
}
@Deprecated("Please consider to replace with the actual property setter")
fun withParent(parent: IParentItem<*>): T {
this.parent = parent
return this as T
}
@Deprecated("Please consider to replace with the actual property setter")
fun withSubItems(subItems: MutableList>): T {
this.subItems = subItems
return this as T
}
/**
* an array of subItems
* **WARNING** Make sure the subItems provided already have identifiers
*
* @param subItems
* @return
*/
fun > setSubItems(vararg subItems: SubType) {
val tempSubItems: MutableList> = mutableListOf()
subItems.forEach {
tempSubItems.add(it)
}
this.subItems = tempSubItems
}
@Deprecated("Please consider to replace with the actual property setter")
fun withSubItems(vararg subItems: ISubItem<*>): T {
setSubItems(*subItems)
return this as T
}
@Deprecated("Please consider to replace with the actual property setter")
fun withSetExpanded(expanded: Boolean): T {
isExpanded = expanded
return this as T
}
/**
* generates a view by the defined LayoutRes
*
* @param ctx
* @return
*/
override fun generateView(ctx: Context): View {
val viewHolder = getViewHolder(LayoutInflater.from(ctx).inflate(layoutRes, null, false))
bindView(viewHolder, mutableListOf())
return viewHolder.itemView
}
/**
* generates a view by the defined LayoutRes and pass the LayoutParams from the parent
*
* @param ctx
* @param parent
* @return
*/
override fun generateView(ctx: Context, parent: ViewGroup): View {
val viewHolder = getViewHolder(LayoutInflater.from(ctx).inflate(layoutRes, parent, false))
bindView(viewHolder, mutableListOf())
return viewHolder.itemView
}
@CallSuper
override fun bindView(holder: VH, payloads: List) {
contentDescription?.let {
holder.itemView.contentDescription = it
}
holder.itemView.setTag(com.mikepenz.materialdrawer.R.id.material_drawer_item, this)
}
/**
* called when the view is unbound
*
* @param holder
*/
override fun unbindView(holder: VH) {
holder.itemView.clearAnimation()
}
/**
* View got attached to the window
*
* @param holder
*/
override fun attachToWindow(holder: VH) {
}
/**
* View got detached from the window
*
* @param holder
*/
override fun detachFromWindow(holder: VH) {
}
/**
* is called when the ViewHolder is in a transient state. return true if you want to reuse
* that view anyways
*
* @param holder the viewHolder for the view which failed to recycle
* @return true if we want to recycle anyways (false - it get's destroyed)
*/
override fun failedToRecycle(holder: VH): Boolean {
return false
}
/**
* This method returns the ViewHolder for our item, using the provided View.
*
* @param parent
* @return the ViewHolder for this Item
*/
override fun getViewHolder(parent: ViewGroup): VH {
return getViewHolder(LayoutInflater.from(parent.context).inflate(layoutRes, parent, false))
}
/**
* This method returns the ViewHolder for our item, using the provided View.
*
* @param v
* @return the ViewHolder for this Item
*/
abstract fun getViewHolder(v: View): VH
/**
* If this item equals to the given identifier
*
* @param id
* @return
*/
override fun equals(id: Long): Boolean {
return id == identifier
}
override fun equals(id: Int): Boolean {
return id.toLong() == identifier
}
/**
* If this item equals to the given object
*
* @param other
* @return
*/
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || javaClass != other.javaClass) return false
val that = other as AbstractDrawerItem<*, *>?
return identifier == that?.identifier
}
/**
* the hashCode implementation
*
* @return
*/
override fun hashCode(): Int {
return java.lang.Long.valueOf(identifier).hashCode()
}
/**
* helper method to decide for the correct color
*
* @param ctx
* @return
*/
protected fun getSelectedColor(ctx: Context): Int {
return ctx.getSelectedColor()
}
/**
* helper method to decide for the correct color
*
* @param ctx
* @return
*/
protected open fun getColor(ctx: Context): ColorStateList {
return ctx.getPrimaryDrawerTextColor()
}
/**
* helper method to decide for the ShapeAppearanceModel used for the drawable's corners
*
* @param ctx
* @return
*/
protected open fun getShapeAppearanceModel(ctx: Context): ShapeAppearanceModel {
val cornerRadius = ctx.resources.getDimensionPixelSize(R.dimen.material_drawer_item_corner_radius)
return ShapeAppearanceModel().withCornerSize(cornerRadius.toFloat())
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractSwitchableDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.view.View
import android.widget.CompoundButton
import androidx.annotation.LayoutRes
import androidx.appcompat.widget.SwitchCompat
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.interfaces.OnCheckedChangeListener
import com.mikepenz.materialdrawer.model.interfaces.Checkable
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
/**
* An abstract [IDrawerItem] implementation describing a drawerItem with support for a switch
*/
abstract class AbstractSwitchableDrawerItem- > : Checkable, BaseDescribeableDrawerItem
- () {
var isSwitchEnabled = true
override var isChecked = false
var onCheckedChangeListener: OnCheckedChangeListener? = null
override val type: Int
get() = R.id.material_drawer_item_primary_switch
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_switch
private val checkedChangeListener = object : CompoundButton.OnCheckedChangeListener {
override fun onCheckedChanged(buttonView: CompoundButton, ic: Boolean) {
if (isEnabled) {
isChecked = ic
onCheckedChangeListener?.onCheckedChanged(this@AbstractSwitchableDrawerItem, buttonView, ic)
} else {
buttonView.setOnCheckedChangeListener(null)
buttonView.isChecked = !ic
buttonView.setOnCheckedChangeListener(this)
}
}
}
@Deprecated("Please consider to replace with the actual property setter")
fun withSwitchEnabled(switchEnabled: Boolean): Item {
this.isSwitchEnabled = switchEnabled
return this as Item
}
@Deprecated("Please consider to replace with the actual property setter")
fun withOnCheckedChangeListener(onCheckedChangeListener: OnCheckedChangeListener): Item {
this.onCheckedChangeListener = onCheckedChangeListener
return this as Item
}
override fun bindView(holder: ViewHolder, payloads: List
) {
super.bindView(holder, payloads)
//bind the basic view parts
bindViewHelper(holder)
//handle the switch
holder.switchView.setOnCheckedChangeListener(null)
holder.switchView.isChecked = isChecked
holder.switchView.setOnCheckedChangeListener(checkedChangeListener)
holder.switchView.isEnabled = isSwitchEnabled
//add a onDrawerItemClickListener here to be able to check / uncheck if the drawerItem can't be selected
withOnDrawerItemClickListener { v, item, position ->
if (!isSelectable) {
isChecked = !isChecked
holder.switchView.isChecked = isChecked
}
false
}
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
open class ViewHolder internal constructor(view: View) : BaseViewHolder(view) {
internal val switchView: SwitchCompat = view.findViewById(R.id.material_drawer_switch)
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractToggleableDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.view.View
import android.widget.CompoundButton
import android.widget.ToggleButton
import androidx.annotation.LayoutRes
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.interfaces.OnCheckedChangeListener
import com.mikepenz.materialdrawer.model.interfaces.Checkable
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
/**
* An abstract [IDrawerItem] implementation describing a drawerItem with support for a toggle
*/
open class AbstractToggleableDrawerItem- > : Checkable, BaseDescribeableDrawerItem
- () {
var isToggleEnabled = true
override var isChecked = false
var onCheckedChangeListener: OnCheckedChangeListener? = null
override val type: Int
get() = R.id.material_drawer_item_primary_toggle
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_toggle
private val checkedChangeListener = object : CompoundButton.OnCheckedChangeListener {
override fun onCheckedChanged(buttonView: CompoundButton, ic: Boolean) {
if (isEnabled) {
isChecked = ic
onCheckedChangeListener?.onCheckedChanged(this@AbstractToggleableDrawerItem, buttonView, ic)
} else {
buttonView.setOnCheckedChangeListener(null)
buttonView.isChecked = !ic
buttonView.setOnCheckedChangeListener(this)
}
}
}
@Deprecated("Please consider to replace with the actual property setter")
fun withToggleEnabled(toggleEnabled: Boolean): Item {
this.isToggleEnabled = toggleEnabled
return this as Item
}
@Deprecated("Please consider to replace with the actual property setter")
fun withOnCheckedChangeListener(onCheckedChangeListener: OnCheckedChangeListener): Item {
this.onCheckedChangeListener = onCheckedChangeListener
return this as Item
}
override fun bindView(holder: ViewHolder, payloads: List
) {
super.bindView(holder, payloads)
//bind the basic view parts
bindViewHelper(holder)
//handle the toggle
holder.toggle.setOnCheckedChangeListener(null)
holder.toggle.isChecked = isChecked
holder.toggle.setOnCheckedChangeListener(checkedChangeListener)
holder.toggle.isEnabled = isToggleEnabled
//add a onDrawerItemClickListener here to be able to check / uncheck if the drawerItem can't be selected
withOnDrawerItemClickListener { v, item, position ->
if (!isSelectable) {
isChecked = !isChecked
holder.toggle.isChecked = isChecked
}
false
}
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
open class ViewHolder internal constructor(view: View) : BaseViewHolder(view) {
internal val toggle: ToggleButton = view.findViewById(R.id.material_drawer_toggle)
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseDescribeableDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.content.Context
import android.content.res.ColorStateList
import android.view.View
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import com.google.android.material.shape.ShapeAppearanceModel
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.Describable
import com.mikepenz.materialdrawer.model.interfaces.DescribableColor
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.util.DrawerImageLoader
import com.mikepenz.materialdrawer.util.getSecondaryDrawerTextColor
import com.mikepenz.materialdrawer.util.setDrawerVerticalPadding
import com.mikepenz.materialdrawer.util.themeDrawerItem
/**
* An abstract [IDrawerItem] implementation describing a drawerItem with support for a description
*/
abstract class BaseDescribeableDrawerItem : BaseDrawerItem(), Describable, DescribableColor {
override var description: StringHolder? = null
override var descriptionTextColor: ColorStateList? = null
/**
* a helper method to have the logic for all secondaryDrawerItems only once
*
* @param viewHolder
*/
protected fun bindViewHelper(viewHolder: BaseViewHolder) {
val ctx = viewHolder.itemView.context
//set the identifier from the drawerItem here. It can be used to run tests
viewHolder.itemView.id = hashCode()
//get the correct color for the background
val selectedColor = this.selectedColor?.color(ctx) ?: getSelectedColor(ctx)
//get the correct color for the text
val textColor = this.textColor ?: getColor(ctx)
val textColorSecondary = this.descriptionTextColor ?: ctx.getSecondaryDrawerTextColor()
//get the correct color for the icon
val iconColor = this.iconColor ?: getIconColor(ctx)
val shapeAppearanceModel = getShapeAppearanceModel(ctx)
//set the background for the item
applyDrawerItemTheme(ctx, viewHolder.view, selectedColor, isSelectedBackgroundAnimated, shapeAppearanceModel)
//set the text for the name
StringHolder.applyTo(this.name, viewHolder.name)
//set the text for the description or hide
StringHolder.applyToOrHide(this.description, viewHolder.description)
//set the colors for textViews
viewHolder.name.setTextColor(textColor)
//set the description text color
viewHolder.description.setTextColor(textColorSecondary)
//define the typeface for our textViews
if (typeface != null) {
viewHolder.name.typeface = typeface
viewHolder.description.typeface = typeface
}
// check if we should load from a url, false if normal icon
val loaded = icon?.uri?.let {
DrawerImageLoader.instance.setImage(viewHolder.icon, it, DrawerImageLoader.Tags.PRIMARY_ITEM.name)
} ?: false
if (!loaded) {
// get the drawables for our icon and set it
val icon = ImageHolder.decideIcon(icon, ctx, iconColor, isIconTinted, 1)
val selectedIcon = ImageHolder.decideIcon(selectedIcon, ctx, iconColor, isIconTinted, 1)
ImageHolder.applyMultiIconTo(icon, selectedIcon, iconColor, isIconTinted, viewHolder.icon)
}
if (viewHolder.icon.isVisible) {
viewHolder.name.updatePadding(left = 0)
viewHolder.description.updatePadding(left = 0)
} else {
viewHolder.name.updatePadding(left = ctx.resources.getDimensionPixelSize(R.dimen.material_drawer_item_primary_icon_padding_left))
viewHolder.description.updatePadding(left = ctx.resources.getDimensionPixelSize(R.dimen.material_drawer_item_primary_icon_padding_left))
}
//for android API 17 --> Padding not applied via xml
viewHolder.view.setDrawerVerticalPadding(level)
//set the item selected if it is
viewHolder.itemView.isSelected = isSelected
viewHolder.name.isSelected = isSelected
viewHolder.description.isSelected = isSelected
viewHolder.icon.isSelected = isSelected
//set the item enabled if it is
viewHolder.itemView.isEnabled = isEnabled
viewHolder.name.isEnabled = isEnabled
viewHolder.description.isEnabled = isEnabled
viewHolder.icon.isEnabled = isEnabled
}
override fun unbindView(holder: VH) {
super.unbindView(holder)
// reset image loading for the item
DrawerImageLoader.instance.cancelImage(holder.icon)
holder.icon.setImageBitmap(null)
}
/**
* will apply and theme the drawer item using the standard logic, overwrite this in your custom item to redefine the algorithm to do so
*/
protected open fun applyDrawerItemTheme(ctx: Context, view: View, selected_color: Int, animate: Boolean, shapeAppearanceModel: ShapeAppearanceModel) {
themeDrawerItem(ctx, view, selected_color, animate, shapeAppearanceModel, isSelected = isSelected)
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.content.Context
import android.content.res.ColorStateList
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.util.getPrimaryDrawerIconColor
/**
* An abstract [IDrawerItem] implementation providing the base properties with their default value
*/
abstract class BaseDrawerItem : AbstractDrawerItem(), Nameable, NameableColor, Iconable, SelectIconable, Tagable {
override var icon: ImageHolder? = null
override var iconColor: ColorStateList? = null
override var selectedIcon: ImageHolder? = null
override var name: StringHolder? = null
override var textColor: ColorStateList? = null
override var isIconTinted = false
/** Allows to set the 'level' of this item */
var level = 1
@Deprecated("Please consider to replace with the actual property setter")
fun withLevel(level: Int): T {
this.level = level
return this as T
}
/**
* helper method to decide for the correct color
*
* @param ctx
* @return
*/
open fun getIconColor(ctx: Context): ColorStateList {
return ctx.getPrimaryDrawerIconColor()
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseViewHolder.kt
================================================
package com.mikepenz.materialdrawer.model
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.R
/**
*the base [RecyclerView.ViewHolder] for drawerItems
*/
open class BaseViewHolder(internal var view: View) : RecyclerView.ViewHolder(view) {
internal var icon: ImageView = view.findViewById(R.id.material_drawer_icon)
internal var name: TextView = view.findViewById(R.id.material_drawer_name)
internal var description: TextView = view.findViewById(R.id.material_drawer_description)
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/ContainerDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.holder.DimenHolder
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.util.getDividerColor
/**
* Describes a [IDrawerItem] acting as a container for generic views
*/
open class ContainerDrawerItem : AbstractDrawerItem() {
var height: DimenHolder? = null
var view: View? = null
var viewPosition = Position.TOP
var divider = true
override val type: Int
get() = R.id.material_drawer_item_container
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_container
@Deprecated("Please consider to replace with the actual property setter")
fun withHeight(height: DimenHolder?): ContainerDrawerItem {
this.height = height
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun withView(view: View): ContainerDrawerItem {
this.view = view
return this
}
/**
* Defines the position for the divider
*/
enum class Position {
TOP,
BOTTOM,
NONE
}
@Deprecated("Please consider to replace with the actual property setter")
fun withViewPosition(position: Position): ContainerDrawerItem {
this.viewPosition = position
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun withDivider(_divider: Boolean): ContainerDrawerItem {
this.divider = _divider
return this
}
override fun bindView(holder: ViewHolder, payloads: List) {
super.bindView(holder, payloads)
val ctx = holder.itemView.context
//set the identifier from the drawerItem here. It can be used to run tests
holder.itemView.id = hashCode()
//define how the divider should look like
holder.view.isEnabled = false
//make sure our view is not used in another parent
view?.parent?.let {
(it as ViewGroup).removeView(view)
}
//set the height
var height = ViewGroup.LayoutParams.WRAP_CONTENT
this.height?.let {
val lp = holder.view.layoutParams as RecyclerView.LayoutParams
height = it.asPixel(ctx)
lp.height = height
holder.view.layoutParams = lp
}
//make sure the header view is empty
(holder.view as ViewGroup).removeAllViews()
var dividerHeight = 0
if (divider) {
dividerHeight = ctx.resources.getDimensionPixelSize(R.dimen.material_drawer_container_divider)
}
val divider = View(ctx)
divider.minimumHeight = dividerHeight
divider.setBackgroundColor(ctx.getDividerColor())
val dividerParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dividerHeight)
val viewParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, if (this.height != null) height - dividerHeight else height)
//depending on the position we add the view
when (viewPosition) {
Position.TOP -> {
holder.view.addView(view, viewParams)
dividerParams.bottomMargin = ctx.resources.getDimensionPixelSize(R.dimen.material_drawer_padding)
holder.view.addView(divider, dividerParams)
}
Position.BOTTOM -> {
dividerParams.topMargin = ctx.resources.getDimensionPixelSize(R.dimen.material_drawer_padding)
holder.view.addView(divider, dividerParams)
holder.view.addView(view, viewParams)
}
else -> holder.view.addView(view, viewParams)
}
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
class ViewHolder internal constructor(internal val view: View) : RecyclerView.ViewHolder(view)
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/DividerDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.view.View
import androidx.annotation.LayoutRes
import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.util.getDividerColor
/**
* Describes a [IDrawerItem] acting as a divider in between items
*/
open class DividerDrawerItem : AbstractDrawerItem() {
override val type: Int
get() = R.id.material_drawer_item_divider
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_divider
override fun bindView(holder: ViewHolder, payloads: List) {
super.bindView(holder, payloads)
val ctx = holder.itemView.context
//set the identifier from the drawerItem here. It can be used to run tests
holder.itemView.id = hashCode()
//define how the divider should look like
holder.itemView.isClickable = false
holder.itemView.isEnabled = false
holder.itemView.minimumHeight = 1
ViewCompat.setImportantForAccessibility(holder.itemView, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO)
//set the color for the divider
holder.itemView.setBackgroundColor(ctx.getDividerColor())
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
class ViewHolder internal constructor(view: View) : RecyclerView.ViewHolder(view)
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/ExpandableBadgeDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.content.res.ColorStateList
import android.os.Build
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.LayoutRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.view.ViewCompat
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.ColorHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.ColorfulBadgeable
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.util.FixStateListDrawable
/**
* Describes a [IDrawerItem] supporting child items (an expandable hierarchy) and badges.
*/
open class ExpandableBadgeDrawerItem : BaseDescribeableDrawerItem(), ColorfulBadgeable {
private var mOnDrawerItemClickListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null
var arrowColor: ColorHolder? = null
var arrowRotationAngleStart = 0
var arrowRotationAngleEnd = 180
override var badge: StringHolder? = null
override var badgeStyle: BadgeStyle? = BadgeStyle()
override val type: Int
get() = R.id.material_drawer_item_expandable_badge
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_expandable_badge
/**
* our internal onDrawerItemClickListener which will handle the arrow animation
*/
override var onDrawerItemClickListener: ((View?, IDrawerItem<*>, Int) -> Boolean)? = { view, drawerItem, position ->
if (drawerItem is AbstractDrawerItem<*, *> && drawerItem.isEnabled) {
if (drawerItem.subItems != null) {
view?.let {
if (drawerItem.isExpanded) {
ViewCompat.animate(view.findViewById(R.id.material_drawer_arrow)).rotation(this@ExpandableBadgeDrawerItem.arrowRotationAngleEnd.toFloat()).start()
} else {
ViewCompat.animate(view.findViewById(R.id.material_drawer_arrow))
.rotation(this@ExpandableBadgeDrawerItem.arrowRotationAngleStart.toFloat())
.start()
}
}
}
}
mOnDrawerItemClickListener?.invoke(view, drawerItem, position) ?: false
}
override fun bindView(holder: ExpandableBadgeDrawerItem.ViewHolder, payloads: List) {
super.bindView(holder, payloads)
val ctx = holder.itemView.context
//bind the basic view parts
bindViewHelper(holder)
//set the text for the badge or hide
val badgeVisible = StringHolder.applyToOrHide(badge, holder.badge)
//style the badge if it is visible
if (badgeVisible) {
badgeStyle?.style(holder.badge, getColor(ctx))
holder.badge.visibility = View.VISIBLE
} else {
holder.badge.visibility = View.GONE
}
//define the typeface for our textViews
if (typeface != null) {
holder.badge.typeface = typeface
}
val arrowColor = this.arrowColor?.color(ctx)?.let { ColorStateList.valueOf(it) } ?: getIconColor(ctx)
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> {
holder.arrow.imageTintList = arrowColor
}
holder.arrow.drawable is FixStateListDrawable -> {
(holder.arrow.drawable as FixStateListDrawable).color = arrowColor
}
else -> {
holder.arrow.setImageDrawable(FixStateListDrawable(holder.arrow.drawable, arrowColor))
}
}
//make sure all animations are stopped
holder.arrow.clearAnimation()
if (!isExpanded) {
holder.arrow.rotation = this.arrowRotationAngleStart.toFloat()
} else {
holder.arrow.rotation = this.arrowRotationAngleEnd.toFloat()
}
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
@Deprecated("Please consider to replace with the actual property setter")
override fun withOnDrawerItemClickListener(onDrawerItemClickListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)?): ExpandableBadgeDrawerItem {
mOnDrawerItemClickListener = onDrawerItemClickListener
return this
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
class ViewHolder(view: View) : BaseViewHolder(view) {
val arrow: ImageView = view.findViewById(R.id.material_drawer_arrow)
val badge: TextView = view.findViewById(R.id.material_drawer_badge)
init {
arrow.setImageDrawable(AppCompatResources.getDrawable(view.context, R.drawable.material_drawer_ico_chevron_down))
}
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/ExpandableDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.content.res.ColorStateList
import android.os.Build
import android.view.View
import android.widget.ImageView
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.LayoutRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.view.ViewCompat
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.holder.ColorHolder
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.util.FixStateListDrawable
/**
* Describes a [IDrawerItem] supporting child items.
*/
open class ExpandableDrawerItem : BaseDescribeableDrawerItem() {
var mOnDrawerItemClickListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null
var arrowColor: ColorHolder? = null
var arrowRotationAngleStart = 0
var arrowRotationAngleEnd = 180
override val type: Int
get() = R.id.material_drawer_item_expandable
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_expandable
/**
* our internal onDrawerItemClickListener which will handle the arrow animation
*/
override var onDrawerItemClickListener: ((View?, IDrawerItem<*>, Int) -> Boolean)? = { view, drawerItem, position ->
if (drawerItem is AbstractDrawerItem<*, *> && drawerItem.isEnabled) {
view?.let {
if (drawerItem.subItems != null) {
if (drawerItem.isExpanded) {
ViewCompat.animate(view.findViewById(R.id.material_drawer_arrow)).rotation(this@ExpandableDrawerItem.arrowRotationAngleEnd.toFloat()).start()
} else {
ViewCompat.animate(view.findViewById(R.id.material_drawer_arrow)).rotation(this@ExpandableDrawerItem.arrowRotationAngleStart.toFloat()).start()
}
}
}
}
mOnDrawerItemClickListener?.invoke(view, drawerItem, position) ?: false
}
@Deprecated("Please consider to replace with the actual property setter")
fun withArrowColor(@ColorInt arrowColor: Int): ExpandableDrawerItem {
this.arrowColor = ColorHolder.fromColor(arrowColor)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun withArrowColorRes(@ColorRes arrowColorRes: Int): ExpandableDrawerItem {
this.arrowColor = ColorHolder.fromColorRes(arrowColorRes)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun withArrowRotationAngleStart(angle: Int): ExpandableDrawerItem {
this.arrowRotationAngleStart = angle
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun withArrowRotationAngleEnd(angle: Int): ExpandableDrawerItem {
this.arrowRotationAngleEnd = angle
return this
}
@Deprecated("Please consider to replace with the actual property setter")
override fun withOnDrawerItemClickListener(onDrawerItemClickListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)?): ExpandableDrawerItem {
mOnDrawerItemClickListener = onDrawerItemClickListener
return this
}
override fun bindView(holder: ViewHolder, payloads: List) {
super.bindView(holder, payloads)
val ctx = holder.itemView.context
//bind the basic view parts
bindViewHelper(holder)
val arrowColor = this.arrowColor?.color(ctx)?.let { ColorStateList.valueOf(it) } ?: getIconColor(ctx)
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> {
holder.arrow.imageTintList = arrowColor
}
holder.arrow.drawable is FixStateListDrawable -> {
(holder.arrow.drawable as FixStateListDrawable).color = arrowColor
}
else -> {
holder.arrow.setImageDrawable(FixStateListDrawable(holder.arrow.drawable, arrowColor))
}
}
//make sure all animations are stopped
holder.arrow.clearAnimation()
if (!isExpanded) {
holder.arrow.rotation = this.arrowRotationAngleStart.toFloat()
} else {
holder.arrow.rotation = this.arrowRotationAngleEnd.toFloat()
}
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
class ViewHolder(view: View) : BaseViewHolder(view) {
var arrow: ImageView = view.findViewById(R.id.material_drawer_arrow)
init {
arrow.setImageDrawable(AppCompatResources.getDrawable(view.context, R.drawable.material_drawer_ico_chevron_down))
}
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/MiniDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.DimenRes
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.DimenHolder
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.util.DrawerImageLoader
import com.mikepenz.materialdrawer.util.themeDrawerItem
/**
* Describes a [IDrawerItem] being used for the [com.mikepenz.materialdrawer.widget.MiniDrawerSliderView]
*/
open class MiniDrawerItem : BaseDrawerItem {
var badge: StringHolder? = null
var badgeStyle: BadgeStyle? = BadgeStyle()
var enableSelectedBackground = false
var customHeight: DimenHolder? = null
override val type: Int
get() = R.id.material_drawer_item_mini
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_mini
constructor(primaryDrawerItem: PrimaryDrawerItem) {
this.identifier = primaryDrawerItem.identifier
this.tag = primaryDrawerItem.tag
this.badge = primaryDrawerItem.badge
this.badgeStyle = primaryDrawerItem.badgeStyle
this.isEnabled = primaryDrawerItem.isEnabled
this.isSelectable = primaryDrawerItem.isSelectable
this.isSelected = primaryDrawerItem.isSelected
this.icon = primaryDrawerItem.icon
this.selectedIcon = primaryDrawerItem.selectedIcon
this.isIconTinted = primaryDrawerItem.isIconTinted
this.selectedColor = primaryDrawerItem.selectedColor
this.iconColor = primaryDrawerItem.iconColor
}
constructor(secondaryDrawerItem: SecondaryDrawerItem) {
this.identifier = secondaryDrawerItem.identifier
this.tag = secondaryDrawerItem.tag
this.badge = secondaryDrawerItem.badge
this.badgeStyle = secondaryDrawerItem.badgeStyle
this.isEnabled = secondaryDrawerItem.isEnabled
this.isSelectable = secondaryDrawerItem.isSelectable
this.isSelected = secondaryDrawerItem.isSelected
this.icon = secondaryDrawerItem.icon
this.selectedIcon = secondaryDrawerItem.selectedIcon
this.isIconTinted = secondaryDrawerItem.isIconTinted
this.selectedColor = secondaryDrawerItem.selectedColor
this.iconColor = secondaryDrawerItem.iconColor
}
@Deprecated("Please consider to replace with the actual property setter")
fun withCustomHeightRes(@DimenRes customHeightRes: Int): MiniDrawerItem {
this.customHeight = DimenHolder.fromResource(customHeightRes)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun withCustomHeightDp(customHeightDp: Int): MiniDrawerItem {
this.customHeight = DimenHolder.fromDp(customHeightDp)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun withCustomHeightPx(customHeightPx: Int): MiniDrawerItem {
this.customHeight = DimenHolder.fromPixel(customHeightPx)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun withCustomHeight(customHeight: DimenHolder): MiniDrawerItem {
this.customHeight = customHeight
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun withEnableSelectedBackground(enableSelectedBackground: Boolean): MiniDrawerItem {
this.enableSelectedBackground = enableSelectedBackground
return this
}
override fun bindView(holder: ViewHolder, payloads: List) {
super.bindView(holder, payloads)
val ctx = holder.itemView.context
//set a different height for this item
customHeight?.let {
val lp = holder.itemView.layoutParams as RecyclerView.LayoutParams
lp.height = it.asPixel(ctx)
holder.itemView.layoutParams = lp
}
//set the identifier from the drawerItem here. It can be used to run tests
holder.itemView.id = hashCode()
//set the item enabled if it is
holder.itemView.isEnabled = isEnabled
holder.icon.isEnabled = isEnabled
//set the item selected if it is
holder.itemView.isSelected = isSelected
holder.icon.isSelected = isSelected
//
holder.itemView.tag = this
//get the correct color for the icon
val iconColor = getIconColor(ctx)
val shapeAppearanceModel = getShapeAppearanceModel(ctx)
if (enableSelectedBackground) {
//get the correct color for the background
val selectedColor = selectedColor?.color(ctx) ?: getSelectedColor(ctx)
//set the background for the item
themeDrawerItem(ctx, holder.view, selectedColor, isSelectedBackgroundAnimated, shapeAppearanceModel, isSelected = isSelected)
}
//set the text for the badge or hide
val badgeVisible = StringHolder.applyToOrHide(badge, holder.badge)
//style the badge if it is visible
if (badgeVisible) {
badgeStyle?.style(holder.badge)
}
// check if we should load from a url, false if normal icon
val loaded = icon?.uri?.let {
DrawerImageLoader.instance.setImage(holder.icon, it, DrawerImageLoader.Tags.MINI_ITEM.name)
} ?: false
if (!loaded) {
// get the drawables for our icon and set it
val icon = ImageHolder.decideIcon(icon, ctx, iconColor, isIconTinted, 1)
val selectedIcon = ImageHolder.decideIcon(selectedIcon, ctx, iconColor, isIconTinted, 1)
ImageHolder.applyMultiIconTo(icon, selectedIcon, iconColor, isIconTinted, holder.icon)
}
//for android API 17 --> Padding not applied via xml
val verticalPadding = ctx.resources.getDimensionPixelSize(R.dimen.material_drawer_padding)
val topBottomPadding = ctx.resources.getDimensionPixelSize(R.dimen.material_mini_drawer_item_padding)
holder.itemView.setPadding(verticalPadding, topBottomPadding, verticalPadding, topBottomPadding)
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun unbindView(holder: ViewHolder) {
super.unbindView(holder)
// reset image loading for the item
DrawerImageLoader.instance.cancelImage(holder.icon)
holder.icon.setImageBitmap(null)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
class ViewHolder(internal val view: View) : RecyclerView.ViewHolder(view) {
internal val icon: ImageView = view.findViewById(R.id.material_drawer_icon)
internal val badge: TextView = view.findViewById(R.id.material_drawer_badge)
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/MiniProfileDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.content.res.ColorStateList
import android.view.View
import android.widget.ImageView
import androidx.annotation.DimenRes
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.holder.DimenHolder
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.IProfile
import com.mikepenz.materialdrawer.util.DrawerImageLoader
/**
* Describes a [IProfile] being used for the [com.mikepenz.materialdrawer.widget.MiniDrawerSliderView]
*/
open class MiniProfileDrawerItem : AbstractDrawerItem, IProfile {
override var icon: ImageHolder? = null
override var iconColor: ColorStateList? = null // not supported for this item
override var name: StringHolder? = null
override var description: StringHolder? = null
var customHeight: DimenHolder? = null
override val type: Int
get() = R.id.material_drawer_item_mini_profile
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_mini_profile
constructor() {
this.isSelectable = false
}
constructor(profile: ProfileDrawerItem) {
this.icon = profile.icon
this.isEnabled = profile.isEnabled
this.isSelectable = false
}
@Deprecated("Please consider to replace with the actual property setter")
fun withCustomHeightRes(@DimenRes customHeightRes: Int): MiniProfileDrawerItem {
this.customHeight = DimenHolder.fromResource(customHeightRes)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun withCustomHeightDp(customHeightDp: Int): MiniProfileDrawerItem {
this.customHeight = DimenHolder.fromDp(customHeightDp)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun withCustomHeightPx(customHeightPx: Int): MiniProfileDrawerItem {
this.customHeight = DimenHolder.fromPixel(customHeightPx)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun withCustomHeight(customHeight: DimenHolder): MiniProfileDrawerItem {
this.customHeight = customHeight
return this
}
override fun bindView(holder: ViewHolder, payloads: List) {
super.bindView(holder, payloads)
customHeight?.let {
val lp = holder.itemView.layoutParams as RecyclerView.LayoutParams
lp.height = it.asPixel(holder.itemView.context)
holder.itemView.layoutParams = lp
}
//set the identifier from the drawerItem here. It can be used to run tests
holder.itemView.id = hashCode()
//set the item enabled if it is
holder.itemView.isEnabled = isEnabled
//set the icon
ImageHolder.applyToOrSetInvisible(icon, holder.icon)
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun unbindView(holder: ViewHolder) {
super.unbindView(holder)
// reset image loading for the item
DrawerImageLoader.instance.cancelImage(holder.icon)
holder.icon.setImageBitmap(null)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
internal val icon: ImageView = view.findViewById(R.id.material_drawer_icon)
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/PrimaryDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
/**
* Describes the main [IDrawerItem] bing used as primary item, following the guidelines
*/
open class PrimaryDrawerItem : AbstractBadgeableDrawerItem()
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/ProfileDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.content.res.ColorStateList
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.util.DrawerImageLoader
import com.mikepenz.materialdrawer.util.setDrawerVerticalPadding
import com.mikepenz.materialdrawer.util.themeDrawerItem
/**
* Describes a [IProfile] being used with the [com.mikepenz.materialdrawer.widget.AccountHeaderView]
*/
open class ProfileDrawerItem : AbstractDrawerItem(), IProfile, Tagable, ColorfulBadgeable, NameableColor, DescribableColor {
override var icon: ImageHolder? = null
override var iconColor: ColorStateList? = null // not supported for this item
override var name: StringHolder? = null
override var textColor: ColorStateList? = null
override var description: StringHolder? = null
override var descriptionTextColor: ColorStateList? = null
var isNameShown = false
override var badge: StringHolder? = null
override var badgeStyle: BadgeStyle? = BadgeStyle()
override val type: Int
get() = R.id.material_drawer_item_profile
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_profile
/**
* Whether to show the profile name in the account switcher.
*
* @param nameShown show name in switcher
* @return the [ProfileDrawerItem]
*/
@Deprecated("Please consider to replace with the actual property setter")
fun withNameShown(nameShown: Boolean): ProfileDrawerItem {
this.isNameShown = nameShown
return this
}
override fun bindView(holder: ViewHolder, payloads: List) {
super.bindView(holder, payloads)
val ctx = holder.itemView.context
//set the identifier from the drawerItem here. It can be used to run tests
holder.itemView.id = hashCode()
//set the item enabled if it is
holder.itemView.isEnabled = isEnabled
holder.name.isEnabled = isEnabled
holder.email.isEnabled = isEnabled
holder.profileIcon.isEnabled = isEnabled
//set the item selected if it is
holder.itemView.isSelected = isSelected
holder.name.isSelected = isSelected
holder.email.isSelected = isSelected
holder.profileIcon.isSelected = isSelected
//get the correct color for the background
val selectedColor = this.selectedColor?.color(ctx) ?: getSelectedColor(ctx)
//get the correct color for the text
val color = this.textColor ?: getColor(ctx)
val descriptionColor = this.descriptionTextColor ?: getColor(ctx)
val shapeAppearanceModel = getShapeAppearanceModel(ctx)
//set the background for the item
themeDrawerItem(ctx, holder.view, selectedColor, isSelectedBackgroundAnimated, shapeAppearanceModel, isSelected = isSelected)
if (isNameShown) {
holder.name.visibility = View.VISIBLE
StringHolder.applyTo(this.name, holder.name)
} else {
holder.name.visibility = View.GONE
}
//the MaterialDrawer follows the Google Apps. those only show the e-mail
//within the profile switcher. The problem this causes some confusion for
//some developers. And if you only set the name, the item would be empty
//so here's a small fallback which will prevent this issue of empty items ;)
if (!isNameShown && this.description == null && this.name != null) {
StringHolder.applyTo(this.name, holder.email)
} else {
StringHolder.applyTo(this.description, holder.email)
}
if (typeface != null) {
holder.name.typeface = typeface
holder.email.typeface = typeface
}
if (isNameShown) {
holder.name.setTextColor(color)
}
holder.email.setTextColor(descriptionColor)
//set the text for the badge or hide
val badgeVisible = StringHolder.applyToOrHide(badge, holder.badge)
//style the badge if it is visible
if (badgeVisible) {
badgeStyle?.style(holder.badge, getColor(ctx))
holder.badge.visibility = View.VISIBLE
} else {
holder.badge.visibility = View.GONE
}
//define the typeface for our textViews
if (typeface != null) {
holder.badge.typeface = typeface
}
//set the icon
ImageHolder.applyToOrSetInvisible(icon, holder.profileIcon, DrawerImageLoader.Tags.PROFILE_DRAWER_ITEM.name)
//for android API 17 --> Padding not applied via xml
setDrawerVerticalPadding(holder.view)
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun unbindView(holder: ViewHolder) {
super.unbindView(holder)
// reset image loading for the item
DrawerImageLoader.instance.cancelImage(holder.profileIcon)
holder.profileIcon.setImageBitmap(null)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
open class ViewHolder internal constructor(internal val view: View) : RecyclerView.ViewHolder(view) {
internal val profileIcon: ImageView = view.findViewById(R.id.material_drawer_profileIcon)
internal val name: TextView = view.findViewById(R.id.material_drawer_name)
internal val email: TextView = view.findViewById(R.id.material_drawer_email)
internal val badge: TextView = view.findViewById(R.id.material_drawer_badge)
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/ProfileSettingDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.content.res.ColorStateList
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.util.getPrimaryDrawerIconColor
import com.mikepenz.materialdrawer.util.setDrawerVerticalPadding
import com.mikepenz.materialdrawer.util.themeDrawerItem
/**
* Describes a [IProfile] being used with the [com.mikepenz.materialdrawer.widget.AccountHeaderView]
*/
open class ProfileSettingDrawerItem : AbstractDrawerItem(), IProfile, Tagable, Typefaceable, ColorfulBadgeable, NameableColor, DescribableColor, SelectIconable {
override var icon: ImageHolder? = null
override var iconColor: ColorStateList? = null
override var selectedIcon: ImageHolder? = null
override var name: StringHolder? = null
override var textColor: ColorStateList? = null
override var description: StringHolder? = null
override var descriptionTextColor: ColorStateList? = null
override var isIconTinted = false
override var badge: StringHolder? = null
override var badgeStyle: BadgeStyle? = BadgeStyle()
override var isSelectable = false
override val type: Int
get() = R.id.material_drawer_item_profile_setting
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_profile_setting
override fun bindView(holder: ViewHolder, payloads: List) {
super.bindView(holder, payloads)
//get the context
val ctx = holder.itemView.context
//set the identifier from the drawerItem here. It can be used to run tests
holder.itemView.id = hashCode()
//set the item enabled if it is
holder.itemView.isEnabled = isEnabled
holder.name.isEnabled = isEnabled
holder.description.isEnabled = isEnabled
holder.icon.isEnabled = isEnabled
//set the item selected if it is
holder.itemView.isSelected = isSelected
holder.name.isSelected = isSelected
holder.description.isSelected = isSelected
holder.icon.isSelected = isSelected
//get the correct color for the background
val selectedColor = this.selectedColor?.color(ctx) ?: getSelectedColor(ctx)
//get the correct color for the text
val color = this.textColor ?: getColor(ctx)
val iconColor = this.iconColor ?: ctx.getPrimaryDrawerIconColor()
val descriptionColor = this.descriptionTextColor ?: getColor(ctx)
val shapeAppearanceModel = getShapeAppearanceModel(ctx)
//set the background for the item
themeDrawerItem(ctx, holder.view, selectedColor, isSelectedBackgroundAnimated, shapeAppearanceModel, isSelected = isSelected)
StringHolder.applyTo(this.name, holder.name)
holder.name.setTextColor(color)
StringHolder.applyToOrHide(this.description, holder.description)
holder.description.setTextColor(descriptionColor)
if (typeface != null) {
holder.name.typeface = typeface
holder.description.typeface = typeface
}
//set the text for the badge or hide
val badgeVisible = StringHolder.applyToOrHide(badge, holder.badge)
//style the badge if it is visible
if (badgeVisible) {
badgeStyle?.style(holder.badge, getColor(ctx))
holder.badge.visibility = View.VISIBLE
} else {
holder.badge.visibility = View.GONE
}
//define the typeface for our textViews
if (typeface != null) {
holder.badge.typeface = typeface
}
//set the correct icon
val icon = ImageHolder.decideIcon(icon, ctx, iconColor, isIconTinted, 2)
val selectedIcon = ImageHolder.decideIcon(selectedIcon, ctx, iconColor, isIconTinted, 2)
ImageHolder.applyMultiIconTo(icon, selectedIcon, iconColor, isIconTinted, holder.icon)
//for android API 17 --> Padding not applied via xml
setDrawerVerticalPadding(holder.view)
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
open class ViewHolder internal constructor(internal val view: View) : RecyclerView.ViewHolder(view) {
internal val icon: ImageView = view.findViewById(R.id.material_drawer_icon)
internal val name: TextView = view.findViewById(R.id.material_drawer_name)
internal val description: TextView = view.findViewById(R.id.material_drawer_description)
internal val badge: TextView = view.findViewById(R.id.material_drawer_badge)
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondaryDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.content.Context
import android.content.res.ColorStateList
import androidx.annotation.LayoutRes
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.util.getSecondaryDrawerIconColor
import com.mikepenz.materialdrawer.util.getSecondaryDrawerTextColor
/**
* Describes the secondary [IDrawerItem] bing used as primary item. Slightly smaller than the primary items
*/
open class SecondaryDrawerItem : AbstractBadgeableDrawerItem() {
override val type: Int
get() = R.id.material_drawer_item_secondary
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_secondary
override fun getColor(ctx: Context): ColorStateList {
return ctx.getSecondaryDrawerTextColor()
}
override fun getIconColor(ctx: Context): ColorStateList {
return ctx.getSecondaryDrawerIconColor()
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondarySwitchDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.content.Context
import android.content.res.ColorStateList
import androidx.annotation.LayoutRes
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.util.getSecondaryDrawerIconColor
import com.mikepenz.materialdrawer.util.getSecondaryDrawerTextColor
/**
* Describes a [IDrawerItem] bing used as secondary item, offering a switch.
*/
open class SecondarySwitchDrawerItem : AbstractSwitchableDrawerItem() {
override val type: Int
get() = R.id.material_drawer_item_secondary_switch
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_secondary_switch
override fun getColor(ctx: Context): ColorStateList {
return ctx.getSecondaryDrawerTextColor()
}
override fun getIconColor(ctx: Context): ColorStateList {
return ctx.getSecondaryDrawerIconColor()
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondaryToggleDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.content.Context
import android.content.res.ColorStateList
import androidx.annotation.LayoutRes
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.util.getSecondaryDrawerIconColor
import com.mikepenz.materialdrawer.util.getSecondaryDrawerTextColor
/**
* Describes a [IDrawerItem] bing used as secondary item, offering a toggle.
*/
open class SecondaryToggleDrawerItem : AbstractToggleableDrawerItem() {
override val type: Int
get() = R.id.material_drawer_item_secondary_toggle
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_secondary_toggle
override fun getColor(ctx: Context): ColorStateList {
return ctx.getSecondaryDrawerTextColor()
}
override fun getIconColor(ctx: Context): ColorStateList {
return ctx.getSecondaryDrawerIconColor()
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/SectionDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.content.res.ColorStateList
import android.view.View
import android.widget.TextView
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.Nameable
import com.mikepenz.materialdrawer.model.interfaces.NameableColor
import com.mikepenz.materialdrawer.model.interfaces.Typefaceable
import com.mikepenz.materialdrawer.util.getDividerColor
import com.mikepenz.materialdrawer.util.getSecondaryDrawerTextColor
/**
* Describes a [IDrawerItem] acting as a divider with description to describe a section.
*/
open class SectionDrawerItem : AbstractDrawerItem(), Nameable, NameableColor, Typefaceable {
var divider = true
override var name: StringHolder? = null
override var textColor: ColorStateList? = null
override var isEnabled: Boolean = false
override var isSelected: Boolean = false
override val type: Int
get() = R.id.material_drawer_item_section
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_section
@Deprecated("Please consider to replace with the actual property setter")
fun withDivider(divider: Boolean): SectionDrawerItem {
this.divider = divider
return this
}
override fun bindView(holder: ViewHolder, payloads: List) {
super.bindView(holder, payloads)
val ctx = holder.itemView.context
//set the identifier from the drawerItem here. It can be used to run tests
holder.itemView.id = hashCode()
//define this item to be not clickable nor enabled
holder.view.isClickable = false
holder.view.isEnabled = false
val color = textColor ?: ctx.getSecondaryDrawerTextColor()
//define the text color
holder.name.setTextColor(color)
//set the text for the name
StringHolder.applyTo(this.name, holder.name)
//define the typeface for our textViews
if (typeface != null) {
holder.name.typeface = typeface
}
//hide the divider if we do not need one
if (this.divider) {
holder.divider.visibility = View.VISIBLE
} else {
holder.divider.visibility = View.GONE
}
//set the color for the divider
holder.divider.setBackgroundColor(ctx.getDividerColor())
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
class ViewHolder internal constructor(internal val view: View) : RecyclerView.ViewHolder(view) {
internal val divider: View = view.findViewById(R.id.material_drawer_divider)
internal val name: TextView = view.findViewById(R.id.material_drawer_name)
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/SwitchDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
/**
* Describes the main [IDrawerItem] bing used as primary item, offering a switch.
*/
open class SwitchDrawerItem : AbstractSwitchableDrawerItem()
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/ToggleDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
/**
* Describes the main [IDrawerItem] bing used as primary item, offering a toggle.
*/
open class ToggleDrawerItem : AbstractToggleableDrawerItem()
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Badgeable.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
import com.mikepenz.materialdrawer.holder.StringHolder
/**
* Defines a [IDrawerItem] which allows to have a badge
*/
interface Badgeable {
/** the badge text to show for this item */
var badge: StringHolder?
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withBadge(badge: String): T {
this.badge = StringHolder(badge)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withBadge(badgeRes: Int): T {
this.badge = StringHolder(badgeRes)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withBadge(badge: StringHolder?): T {
this.badge = badge
return this
}
/** Set the badge name */
var Badgeable.badgeRes: Int
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
badge = StringHolder(value)
}
/** Set the badge name */
var Badgeable.badgeText: CharSequence
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
badge = StringHolder(value)
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Checkable.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
/**
* Defines a [IDrawerItem] with support for being selected
*/
interface Checkable : Selectable {
var isChecked: Boolean
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withChecked(checked: Boolean): T {
this.isChecked = checked
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withCheckable(checkable: Boolean): T {
this.isSelectable = checkable
return this
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/ColorfulBadgeable.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
import com.mikepenz.materialdrawer.holder.BadgeStyle
/**
* Defines a [IDrawerItem] which allows to have a colorful badge
*/
interface ColorfulBadgeable : Badgeable {
/** defines the style for the badge in the item */
var badgeStyle: BadgeStyle?
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withBadgeStyle(badgeStyle: BadgeStyle?): T {
this.badgeStyle = badgeStyle
return this
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Describable.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
import androidx.annotation.StringRes
import com.mikepenz.materialdrawer.holder.StringHolder
/**
* Defines a [IDrawerItem] with support for defining a description
*/
interface Describable {
/** The text to show as description */
var description: StringHolder?
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withDescription(description: String): T {
this.description = StringHolder(description)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withDescription(@StringRes descriptionRes: Int): T {
this.description = StringHolder(descriptionRes)
return this
}
/** Set the description */
var Describable.descriptionRes: Int
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
description = StringHolder(value)
}
/** Set the description */
var Describable.descriptionText: CharSequence
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
description = StringHolder(value)
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/DescribableColor.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
import android.content.res.ColorStateList
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
/**
* Defines a [IDrawerItem] with support for defining a description
*/
interface DescribableColor {
/** The color for the description text */
var descriptionTextColor: ColorStateList?
}
/**
* Set the description color as color resource.
*
* This method is deprecated and no-op.
*
* @deprecated
*/
var DescribableColor.descriptionTextColorRes: Int
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
@Deprecated(
"Please use `descriptionTextColor` directly, as [ColorStateList] can't be resolved without [Context].",
level = DeprecationLevel.WARNING,
replaceWith = ReplaceWith("descriptionTextColor")
)
set(@ColorRes value) {
// no-op
}
/** Set the description color as color int */
var DescribableColor.descriptionTextColorInt: Int
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(@ColorInt value) {
descriptionTextColor = ColorStateList.valueOf(value)
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/IDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.fastadapter.*
/**
* Defines a general [IDrawerItem] to be displayed in the [com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView]
*/
interface IDrawerItem : IItem, IItemVHFactory, IItemViewGenerator, IExpandable, IIdentifyable, Selectable, Tagable {
override var isEnabled: Boolean
override var isSelected: Boolean
override val type: Int
val layoutRes: Int
override var identifier: Long
override var isExpanded: Boolean
override val isAutoExpanding: Boolean
override fun generateView(ctx: Context): View
override fun generateView(ctx: Context, parent: ViewGroup): View
override fun getViewHolder(parent: ViewGroup): VH
override fun unbindView(holder: VH)
override fun bindView(holder: VH, payloads: List)
fun equals(id: Long): Boolean
}
@Deprecated("Please consider to replace with the actual property setter")
fun > T.withIdentifier(identifier: Long): T {
this.identifier = identifier
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun > T.withEnabled(enabled: Boolean): T {
this.isEnabled = enabled
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun > T.withSelected(selected: Boolean): T {
this.isSelected = selected
return this
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/IProfile.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
import androidx.annotation.StringRes
import com.mikepenz.fastadapter.IIdentifyable
import com.mikepenz.materialdrawer.holder.StringHolder
/**
* Defines a general [IProfile] to be displayed in the [com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView] with the [com.mikepenz.materialdrawer.widget.AccountHeaderView]
*/
interface IProfile : IIdentifyable, Nameable, Iconable, Selectable, Tagable, Describable
@Deprecated("Please consider to replace with the actual property setter")
fun T.withEmail(@StringRes emailRes: Int): T {
this.description = StringHolder(emailRes)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withEmail(email: String?): T {
this.description = StringHolder(email)
return this
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Iconable.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.net.Uri
import androidx.annotation.DrawableRes
import com.mikepenz.materialdrawer.holder.ImageHolder
/**
* Defines a [IDrawerItem] with support for an icon
*/
interface Iconable {
/** the icon to show in the drawer */
var icon: ImageHolder?
/** the color of the icon */
var iconColor: ColorStateList?
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withIconColor(iconColor: ColorStateList): T {
this.iconColor = iconColor
return this
}
var T.iconDrawable: Drawable?
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
if (value != null) {
this.icon = ImageHolder(value)
} else {
this.icon = null
}
}
var T.iconBitmap: Bitmap
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
this.icon = ImageHolder(value)
}
var T.iconRes: Int
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(@DrawableRes value) {
this.icon = ImageHolder(value)
}
var T.iconUrl: String
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
this.icon = ImageHolder(value)
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withIcon(icon: Drawable?): T {
this.icon = ImageHolder(icon)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withIcon(icon: Bitmap): T {
this.icon = ImageHolder(icon)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withIcon(@DrawableRes imageRes: Int): T {
this.icon = ImageHolder(imageRes)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withIcon(url: String): T {
this.icon = ImageHolder(url)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withIcon(uri: Uri): T {
this.icon = ImageHolder(uri)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withIcon(icon: ImageHolder?): T {
this.icon = icon
return this
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Nameable.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
import androidx.annotation.StringRes
import com.mikepenz.materialdrawer.holder.StringHolder
/**
* Defines a [IDrawerItem] with support for defining a name
*/
interface Nameable {
/** the name to show for the item */
var name: StringHolder?
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withName(name: String?): T {
this.name = StringHolder(name)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withName(@StringRes name: Int): T {
this.name = StringHolder(name)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withName(name: StringHolder?): T {
this.name = name
return this
}
/** Set the name */
var Nameable.nameRes: Int
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
name = StringHolder(value)
}
/** Set the name */
var Nameable.nameText: CharSequence
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
name = StringHolder(value)
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/NameableColor.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
import android.content.res.ColorStateList
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
/**
* Defines a [IDrawerItem] with support for defining a name
*/
interface NameableColor {
/** defines the color for the text */
var textColor: ColorStateList?
}
/**
* Set the selected text color as color resource.
*
* This method is deprecated and no-op.
*
* @deprecated
*/
var NameableColor.textColorRes: Int
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
@Deprecated(
"Please use `textColor` directly, as [ColorStateList] can't be resolved without [Context].",
level = DeprecationLevel.WARNING,
replaceWith = ReplaceWith("descriptionTextColor")
)
set(@ColorRes value) {
// no-op
}
/** Set the selected text color as color int */
var NameableColor.textColorInt: Int
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(@ColorInt value) {
textColor = ColorStateList.valueOf(value)
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/SelectIconable.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import androidx.annotation.DrawableRes
import com.mikepenz.materialdrawer.holder.ImageHolder
/**
* Defines a [IDrawerItem] with support for an icon
*/
interface SelectIconable {
/** the icon to show when this item gets selected */
var selectedIcon: ImageHolder?
/** defines if the icon should get proper tinting with the defined color */
var isIconTinted: Boolean
}
var T.selectedIconDrawable: Drawable
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
this.icon = ImageHolder(value)
}
var T.selectedIconBitmap: Bitmap
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
this.icon = ImageHolder(value)
}
var T.selectedIconRes: Int
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(@DrawableRes value) {
this.icon = ImageHolder(value)
}
var T.selectedIconUrl: String
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
this.icon = ImageHolder(value)
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withSelectedIcon(selectedIcon: Drawable): T {
this.selectedIcon = ImageHolder(selectedIcon)
return this as T
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withSelectedIcon(@DrawableRes selectedIconRes: Int): T {
this.selectedIcon = ImageHolder(selectedIconRes)
return this as T
}
/** will tint the icon with the default (or set) colors (default and selected state) */
@Deprecated("Please consider to replace with the actual property setter")
fun T.withIconTintingEnabled(iconTintingEnabled: Boolean): T {
this.isIconTinted = iconTintingEnabled
return this as T
}
/** will tint the icon with the default (or set) colors (default and selected state) */
@Deprecated("Please consider to replace with the actual property setter")
fun T.withIconTinted(iconTintingEnabled: Boolean): T {
this.isIconTinted = iconTintingEnabled
return this as T
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Selectable.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
/**
* Defines a [IDrawerItem] with support for being selected
*/
interface Selectable {
/** If the item is selectable */
var isSelectable: Boolean
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withSelectable(selectable: Boolean): T {
this.isSelectable = selectable
return this
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/SelectableColor.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import com.mikepenz.materialdrawer.holder.ColorHolder
/**
* Defines a [IDrawerItem] with support for having a different color when selected
*/
interface SelectableColor {
/** The background color of a selectable item */
var selectedColor: ColorHolder?
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withSelectedColor(@ColorInt selectedColor: Int): T {
this.selectedColor = ColorHolder.fromColor(selectedColor)
return this
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withSelectedColorRes(@ColorRes selectedColorRes: Int): T {
this.selectedColor = ColorHolder.fromColorRes(selectedColorRes)
return this
}
/** Set the selected color as color resource */
var SelectableColor.selectedColorRes: Int
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(@ColorRes value) {
selectedColor = ColorHolder.fromColorRes(value)
}
/** Set the selected color as color int */
var SelectableColor.selectedColorInt: Int
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(@ColorInt value) {
selectedColor = ColorHolder.fromColor(value)
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Tagable.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
/**
* Defines a [IDrawerItem] with support for being tagged
*/
interface Tagable {
/** specify a tag attached to the item to use for different situations */
var tag: Any?
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withTag(tag: Any?): T {
this.tag = tag
return this
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Typefaceable.kt
================================================
package com.mikepenz.materialdrawer.model.interfaces
import android.graphics.Typeface
/**
* Defines a [IDrawerItem] with support for defining the [Typeface]
*/
interface Typefaceable {
/** the typeface used for texts */
var typeface: Typeface?
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withTypeface(typeface: Typeface?): T {
this.typeface = typeface
return this
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/utils/BadgeDrawableBuilder.kt
================================================
package com.mikepenz.materialdrawer.model.utils
import android.content.Context
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.StateListDrawable
import android.util.StateSet
import androidx.appcompat.content.res.AppCompatResources
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.ColorHolder
/**
* Builder to construct the [StateListDrawable] given a [BadgeStyle]
*/
class BadgeDrawableBuilder(private val style: BadgeStyle) {
/** creates the [StateListDrawable] given the provided [BadgeStyle] */
fun build(ctx: Context): StateListDrawable {
val stateListDrawable = StateListDrawable()
val normal = AppCompatResources.getDrawable(ctx, style.gradientDrawable) as GradientDrawable?
val selected = normal?.constantState?.newDrawable()?.mutate() as GradientDrawable?
ColorHolder.applyToOrTransparent(style.color, ctx, normal)
if (style.colorPressed == null) {
ColorHolder.applyToOrTransparent(style.color, ctx, selected)
} else {
ColorHolder.applyToOrTransparent(style.colorPressed, ctx, selected)
}
style.corners?.let {
normal?.cornerRadius = it.asPixel(ctx).toFloat()
selected?.cornerRadius = it.asPixel(ctx).toFloat()
}
stateListDrawable.addState(intArrayOf(android.R.attr.state_pressed), selected)
stateListDrawable.addState(StateSet.WILD_CARD, normal)
return stateListDrawable
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/model/utils/DrawerItemExtensions.kt
================================================
package com.mikepenz.materialdrawer.model.utils
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
/** Define tags associated to specific tag items */
private val drawerItemTags = mutableMapOf()
/** Define if this IDrawerItem should / can be shown inside the MiniDrawer */
var > T.hiddenInMiniDrawer: Boolean
get() = drawerItemTags[this] as? Boolean == true
set(value) {
drawerItemTags[this] = value
}
@Deprecated("Please consider to replace with the actual property setter")
fun > T.withIsHiddenInMiniDrawer(hidden: Boolean): T {
hiddenInMiniDrawer = hidden
return this
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/util/AbstractDrawerImageLoader.kt
================================================
package com.mikepenz.materialdrawer.util
import android.content.Context
import android.graphics.drawable.Drawable
import android.net.Uri
import android.util.Log
import android.widget.ImageView
/**
* This abstract class provides functionality to add custom image loader implementations, based on the image loader used in the app.
*/
abstract class AbstractDrawerImageLoader : DrawerImageLoader.IDrawerImageLoader {
/**
* Start loading the image [uri] for the given [imageView] providing the [placeholder], allowing to identify the location it is gonna be used via the [tag]
*/
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) {
//this won't do anything
Log.i("MaterialDrawer", "You have not specified a ImageLoader implementation through the DrawerImageLoader.init() method, or you are still overriding the deprecated method set(ImageView iv, Uri u, Drawable d) instead of set(ImageView iv, Uri u, Drawable d, String tag)")
}
/**
* Cancel loading images for the imageView
*/
override fun cancel(imageView: ImageView) {}
/**
* Retrieve the placeholder to display
*/
override fun placeholder(ctx: Context): Drawable {
return getPlaceHolder(ctx)
}
/**
* Retrieve the placeholder to display, using the [tag] to identify the location it is gonna be used
*/
override fun placeholder(ctx: Context, tag: String?): Drawable {
return placeholder(ctx)
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerImageLoader.kt
================================================
package com.mikepenz.materialdrawer.util
import android.content.Context
import android.graphics.drawable.Drawable
import android.net.Uri
import android.widget.ImageView
import com.mikepenz.materialdrawer.util.DrawerImageLoader.IDrawerImageLoader
/**
* The general management class for the [IDrawerImageLoader] support, to offer support for any image loading library.
*/
open class DrawerImageLoader private constructor(var imageLoader: IDrawerImageLoader?) {
/**
* defines if we accept any protocol
*/
var handleAllProtocols = false
/**
* supported protocols
*/
var handledProtocols = listOf("http", "https")
/**
* The possible tags we currently support.
*/
enum class Tags {
PRIMARY_ITEM,
MINI_ITEM,
PROFILE,
PROFILE_DRAWER_ITEM,
ACCOUNT_HEADER
}
/**
* @param imageView
* @param uri
* @param tag
* @return false if not consumed
*/
open fun setImage(imageView: ImageView, uri: Uri, tag: String?): Boolean {
// If we do not handle this protocol we keep the original behavior
return if (handleAllProtocols || uri.scheme in handledProtocols) {
imageLoader?.let {
val placeHolder = it.placeholder(imageView.context, tag)
it[imageView, uri, placeHolder] = tag
}
true
} else false
}
/**
* Cancel loading for the given [ImageView]
*/
fun cancelImage(imageView: ImageView) {
imageLoader?.cancel(imageView)
}
interface IDrawerImageLoader {
/**
* Start loading the image [uri] for the given [imageView] providing the [placeholder], allowing to identify the location it is gonna be used via the [tag]
*/
operator fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?)
/**
* Cancel loading images for the imageView
*/
fun cancel(imageView: ImageView)
@Deprecated("Please use the placeholder method with the provided tag instead")
fun placeholder(ctx: Context): Drawable
/**
* Retrieve the placeholder to display, using the [tag] to identify the location it is gonna be used
*/
fun placeholder(ctx: Context, tag: String?): Drawable
}
companion object {
private var SINGLETON: DrawerImageLoader? = null
fun init(loaderImpl: IDrawerImageLoader): DrawerImageLoader {
SINGLETON = DrawerImageLoader(loaderImpl)
return SINGLETON as DrawerImageLoader
}
val instance: DrawerImageLoader
get() {
if (SINGLETON == null) {
SINGLETON = DrawerImageLoader(object : AbstractDrawerImageLoader() {})
}
return SINGLETON as DrawerImageLoader
}
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerItemViewHelper.kt
================================================
package com.mikepenz.materialdrawer.util
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import java.util.*
/**
* Custom helper class allowing to construct a view hierarchy just by using [IDrawerItem]s
*/
open class DrawerItemViewHelper(private val context: Context) {
val drawerItems = ArrayList>()
var divider = true
var onDrawerItemClickListener: ((View, IDrawerItem<*>) -> Unit)? = null
fun build(): View {
//create the container view
val linearLayout = LinearLayout(context)
linearLayout.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
linearLayout.orientation = LinearLayout.VERTICAL
//create the divider
if (divider) {
val divider = LinearLayout(context)
divider.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
divider.minimumHeight = context.resources.getDimensionPixelSize(R.dimen.material_drawer_sticky_footer_divider)
divider.orientation = LinearLayout.VERTICAL
divider.setBackgroundColor(context.getDividerColor())
linearLayout.addView(divider)
}
//add all drawer items
for (drawerItem in drawerItems) {
val view = drawerItem.generateView(context)
view.tag = drawerItem
if (drawerItem.isEnabled) {
view.setBackgroundResource(context.getSelectableBackgroundRes())
view.setOnClickListener { v ->
onDrawerItemClickListener?.invoke(v, v.getTag(R.id.material_drawer_item) as IDrawerItem<*>)
}
}
linearLayout.addView(view)
}
return linearLayout
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerUtils.kt
================================================
@file:JvmName("DrawerUtils")
package com.mikepenz.materialdrawer.util
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.drawable.*
import android.os.Build
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.RelativeLayout
import androidx.annotation.AttrRes
import androidx.annotation.DimenRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.ViewCompat
import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.model.AbstractDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
/**
* helpful functions for working with the [MaterialDrawerSliderView]
*/
/**
* helper function to handle the onClick of the footer
*/
internal fun onFooterDrawerItemClick(sliderView: MaterialDrawerSliderView, drawerItem: IDrawerItem<*>, v: View, fireOnClick: Boolean?) {
val checkable = drawerItem.isSelectable
if (checkable) {
sliderView.resetStickyFooterSelection()
v.isActivated = true
v.isSelected = true
//remove the selection in the list
sliderView.selectExtension.deselect()
//find the position of the clicked footer item
if (sliderView.stickyFooterView != null && sliderView.stickyFooterView is LinearLayout) {
val footer = sliderView.stickyFooterView as LinearLayout
for (i in 0 until footer.childCount) {
if (footer.getChildAt(i) === v) {
sliderView.currentStickyFooterSelection = i
break
}
}
}
}
if (fireOnClick != null) {
var consumed = false
if (fireOnClick) {
if (drawerItem is AbstractDrawerItem<*, *> && drawerItem.onDrawerItemClickListener != null) {
consumed = drawerItem.onDrawerItemClickListener?.invoke(v, drawerItem, -1)
?: false
}
if (sliderView.onDrawerItemClickListener != null) {
consumed = sliderView.onDrawerItemClickListener?.invoke(v, drawerItem, -1)
?: false
}
}
if (!consumed) {
//close the drawer after click
sliderView.closeDrawerDelayed()
}
}
}
/**
* helper function to handle the headerView
*/
internal fun handleHeaderView(sliderView: MaterialDrawerSliderView) {
//use the AccountHeader if set
sliderView.accountHeader?.let {
if (sliderView.accountHeaderSticky) {
sliderView.stickyHeaderView = it
} else {
sliderView._headerDivider = it.dividerBelowHeader
sliderView._headerPadding = it.paddingBelowHeader
sliderView.headerView = it
}
}
//sticky header view
sliderView.stickyHeaderView?.let {
sliderView.findViewById(R.id.material_drawer_sticky_header)?.let { header ->
sliderView.removeView(header)
}
//add the sticky footer view and align it to the bottom
val layoutParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT)
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, 1)
it.id = R.id.material_drawer_sticky_header
sliderView.addView(it, 0, layoutParams)
//now align the recyclerView below the stickyFooterView ;)
val layoutParamsListView = sliderView.recyclerView.layoutParams as RelativeLayout.LayoutParams
layoutParamsListView.addRule(RelativeLayout.BELOW, R.id.material_drawer_sticky_header)
sliderView.recyclerView.layoutParams = layoutParamsListView
if (sliderView.stickyHeaderShadow) {
//add a shadow
if (Build.VERSION.SDK_INT >= 21) {
it.background = ColorDrawable(Color.WHITE) // set a background color or the elevation will not work, this is meant to be
it.elevation = sliderView.context.resources.getDimensionPixelSize(R.dimen.material_drawer_sticky_header_elevation).toFloat()
} else {
val view = View(sliderView.context)
view.setBackgroundResource(R.drawable.material_drawer_shadow_bottom)
sliderView.addView(view, RelativeLayout.LayoutParams.MATCH_PARENT, sliderView.context.resources.getDimensionPixelSize(R.dimen.material_drawer_sticky_header_elevation))
//now align the shadow below the stickyHeader ;)
val lps = view.layoutParams as RelativeLayout.LayoutParams
lps.addRule(RelativeLayout.BELOW, R.id.material_drawer_sticky_header)
view.layoutParams = lps
}
}
if (Build.VERSION.SDK_INT >= 21) {
sliderView.elevation = 0f
}
//remove the padding of the recyclerView again we have the header on top of it
sliderView.recyclerView.setPadding(0, 0, 0, 0)
}
}
/**
* small helper to rebuild the FooterView
*/
internal fun rebuildStickyFooterView(sliderView: MaterialDrawerSliderView) {
sliderView.stickyFooterView?.let {
it.removeAllViews()
//create the divider
if (sliderView.stickyFooterDivider) {
addStickyFooterDivider(it.context, it)
}
//fill the footer with items
fillStickyDrawerItemFooter(sliderView, it) { v ->
(v.getTag(R.id.material_drawer_item) as? IDrawerItem<*>)?.let { drawerItem ->
onFooterDrawerItemClick(sliderView, drawerItem, v, true)
}
}
it.visibility = View.VISIBLE
} ?: run {
//there was no footer yet. now just create one
handleFooterView(sliderView) { v ->
(v.getTag(R.id.material_drawer_item) as? IDrawerItem<*>)?.let { drawerItem ->
onFooterDrawerItemClick(sliderView, drawerItem, v, true)
}
}
}
sliderView.setStickyFooterSelection(sliderView.currentStickyFooterSelection, false)
}
/**
* helper function to handle the footerView
*/
internal fun handleFooterView(sliderView: MaterialDrawerSliderView, onClickListener: View.OnClickListener) {
val ctx = sliderView.context
//use the StickyDrawerItems if set
if (sliderView.stickyDrawerItems.size > 0) {
sliderView._stickyFooterView = buildStickyDrawerItemFooter(sliderView, onClickListener)
}
//sticky footer view
sliderView.stickyFooterView?.let {
//add the sticky footer view and align it to the bottom
val layoutParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT)
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 1)
it.id = R.id.material_drawer_sticky_footer
sliderView.addView(it, layoutParams)
/**
if ((sliderView.mTranslucentNavigationBar || drawer.mFullscreen) && Build.VERSION.SDK_INT >= 19) {
it.setPadding(0, 0, 0, UIUtils.getNavigationBarHeight(ctx))
}
**/
//now align the recyclerView above the stickyFooterView ;)
val layoutParamsListView = sliderView.recyclerView.layoutParams as RelativeLayout.LayoutParams
layoutParamsListView.addRule(RelativeLayout.ABOVE, R.id.material_drawer_sticky_footer)
sliderView.recyclerView.layoutParams = layoutParamsListView
//handle shadow on top of the sticky footer
if (sliderView.stickyFooterShadow) {
sliderView.stickyFooterShadowView = View(ctx).also { stickyFooterShadowView ->
stickyFooterShadowView.setBackgroundResource(R.drawable.material_drawer_shadow_top)
sliderView.addView(stickyFooterShadowView, RelativeLayout.LayoutParams.MATCH_PARENT, ctx.resources.getDimensionPixelSize(R.dimen.material_drawer_sticky_footer_elevation))
//now align the shadow below the stickyHeader ;)
val lps = stickyFooterShadowView.layoutParams as RelativeLayout.LayoutParams
lps.addRule(RelativeLayout.ABOVE, R.id.material_drawer_sticky_footer)
stickyFooterShadowView.layoutParams = lps
}
}
//remove the padding of the recyclerView again we have the footer below it
sliderView.recyclerView.setPadding(sliderView.recyclerView.paddingLeft, sliderView.recyclerView.paddingTop, sliderView.recyclerView.paddingRight, ctx.resources.getDimensionPixelSize(R.dimen.material_drawer_padding))
}
}
/**
* build the sticky footer item view
*/
internal fun buildStickyDrawerItemFooter(sliderView: MaterialDrawerSliderView, onClickListener: View.OnClickListener): ViewGroup {
//create the container view
val linearLayout = LinearLayout(sliderView.context)
linearLayout.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
linearLayout.orientation = LinearLayout.VERTICAL
//set the background color to the drawer background color (if it has alpha the shadow won't be visible)
//linearLayout.background = sliderView.background
//create the divider
if (sliderView.stickyFooterDivider) {
addStickyFooterDivider(sliderView.context, linearLayout)
}
fillStickyDrawerItemFooter(sliderView, linearLayout, onClickListener)
return linearLayout
}
/**
* adds the shadow to the stickyFooter
*
* @param ctx
* @param footerView
*/
private fun addStickyFooterDivider(ctx: Context, footerView: ViewGroup) {
val divider = LinearLayout(ctx)
val dividerParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
divider.minimumHeight = ctx.resources.getDimensionPixelSize(R.dimen.material_drawer_sticky_footer_divider)
divider.orientation = LinearLayout.VERTICAL
divider.setBackgroundColor(ctx.getDividerColor())
footerView.addView(divider, dividerParams)
}
/**
* helper function to fill the sticky footer with its elements
*/
internal fun fillStickyDrawerItemFooter(sliderView: MaterialDrawerSliderView, container: ViewGroup, onClickListener: View.OnClickListener) {
//add all drawer items
for (drawerItem in sliderView.stickyDrawerItems) {
val view = drawerItem.generateView(container.context, container)
view.tag = drawerItem
if (drawerItem.isEnabled) {
//UIUtils.setBackground(view, UIUtils.getSelectableBackground(container.getContext(), selected_color, drawerItem.isSelectedBackgroundAnimated()));
view.setOnClickListener(onClickListener)
}
container.addView(view)
//for android API 17 --> Padding not applied via xml
setDrawerVerticalPadding(view)
}
//and really. don't ask about this. it won't set the padding if i don't set the padding for the container
container.setPadding(0, 0, 0, 0)
}
/**
* helper to extend the layoutParams of the drawer
*
* @param params
* @return
*/
@SuppressLint("RtlHardcoded")
fun processDrawerLayoutParams(drawer: MaterialDrawerSliderView, params: DrawerLayout.LayoutParams?): DrawerLayout.LayoutParams? {
if (params != null) {
val drawerLayout = drawer.drawerLayout ?: return null
val ctx = drawerLayout.context
val lp = drawerLayout.layoutParams as DrawerLayout.LayoutParams
if (lp.gravity == Gravity.RIGHT || lp.gravity == Gravity.END) {
params.rightMargin = 0
if (Build.VERSION.SDK_INT >= 17) {
params.marginEnd = 0
}
params.leftMargin = ctx.resources.getDimensionPixelSize(R.dimen.material_drawer_margin)
if (Build.VERSION.SDK_INT >= 17) {
params.marginEnd = ctx.resources.getDimensionPixelSize(R.dimen.material_drawer_margin)
}
}
val customWidth = drawer.customWidth ?: -1
if (customWidth > -1) {
params.width = customWidth
} else {
params.width = getOptimalDrawerWidth(ctx)
}
}
return params
}
/**
* helper function to get a person placeHolder drawable
*/
fun getPlaceHolder(context: Context): Drawable {
val accountDrawable = AppCompatResources.getDrawable(context, R.drawable.material_drawer_ico_account_layer) as LayerDrawable
val placeholderSize = context.resources.getDimensionPixelSize(R.dimen.material_drawer_profile_icon_placeholder)
if (Build.VERSION.SDK_INT >= 23) {
accountDrawable.setLayerWidth(0, placeholderSize)
accountDrawable.setLayerHeight(0, placeholderSize)
}
DrawableCompat.wrap(accountDrawable.getDrawable(0)).let {
DrawableCompat.setTint(it, context.getThemeColor(android.R.attr.colorPrimary))
accountDrawable.setDrawableByLayerId(R.id.background, it)
}
val iconSize = context.resources.getDimensionPixelSize(R.dimen.material_drawer_profile_icon_placeholder_icon)
if (Build.VERSION.SDK_INT >= 23) {
accountDrawable.setLayerWidth(1, iconSize)
accountDrawable.setLayerHeight(1, iconSize)
accountDrawable.setLayerGravity(1, Gravity.CENTER)
}
DrawableCompat.wrap(accountDrawable.getDrawable(1)).let {
DrawableCompat.setTint(it, context.getThemeColor(android.R.attr.colorAccent))
accountDrawable.setDrawableByLayerId(R.id.account, it)
}
return accountDrawable
//IconicsDrawable(ctx, MaterialDrawerFont.Icon.mdf_person).color(IconicsColor.colorInt(ctx.getThemeColor(R.attr.colorAccent))).backgroundColor(IconicsColor.colorInt(ctx.getThemeColor(R.attr.colorPrimary))).size(IconicsSize.dp(56)).padding(IconicsSize.dp(16))
}
/**
* helper to set the vertical padding to the DrawerItems
* this is required because on API Level 17 the padding is ignored which is set via the XML
*/
fun setDrawerVerticalPadding(view: View) {
val verticalPadding = view.context.resources.getDimensionPixelSize(R.dimen.material_drawer_vertical_padding)
view.setPadding(verticalPadding, 0, verticalPadding, 0)
}
/**
* Util method to theme the drawer item view's background (and foreground if possible)
*
* @param ctx the context to use
* @param view the view to theme
* @param selectedColor the selected color to use
* @param animate true if we want to animate the StateListDrawable
* @param shapeAppearanceModel defines the shape appearance to use for items starting API 21
* @param paddingTopBottomRes padding on top and bottom of the drawable for selection drawable
* @param paddingStartRes padding to the beginning of the selection drawable
* @param paddingEndRes padding to the end of the selection drawable
* @param highlightColorRes the color for the highlight to use (e.g. touch the item, when it get's filled)
*/
fun themeDrawerItem(
ctx: Context,
view: View,
selectedColor: Int,
animate: Boolean,
shapeAppearanceModel: ShapeAppearanceModel,
@DimenRes paddingTopBottomRes: Int = R.dimen.material_drawer_item_background_padding_top_bottom,
@DimenRes paddingStartRes: Int = R.dimen.material_drawer_item_background_padding_start,
@DimenRes paddingEndRes: Int = R.dimen.material_drawer_item_background_padding_end,
@AttrRes highlightColorRes: Int = android.R.attr.colorControlHighlight,
/* a hint for the drawable if it should already be selected at the very moment */
isSelected: Boolean = false
) {
val selected: Drawable
val unselected: Drawable
// Material 3.0 styling
val paddingTopBottom = ctx.resources.getDimensionPixelSize(paddingTopBottomRes)
val paddingStart = ctx.resources.getDimensionPixelSize(paddingStartRes)
val paddingEnd = ctx.resources.getDimensionPixelSize(paddingEndRes)
// define normal selected background
val gradientDrawable = MaterialShapeDrawable(shapeAppearanceModel)
gradientDrawable.fillColor = ColorStateList.valueOf(selectedColor)
selected = InsetDrawable(gradientDrawable, paddingStart, paddingTopBottom, paddingEnd, paddingTopBottom)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// define mask for ripple
val gradientMask = MaterialShapeDrawable(shapeAppearanceModel)
gradientMask.fillColor = ColorStateList.valueOf(Color.BLACK)
val mask = InsetDrawable(gradientMask, paddingStart, paddingTopBottom, paddingEnd, paddingTopBottom)
unselected = RippleDrawable(ColorStateList(arrayOf(intArrayOf()), intArrayOf(ctx.getThemeColor(highlightColorRes))), null, mask)
} else {
// define touch drawable
val touchDrawable = MaterialShapeDrawable(shapeAppearanceModel)
touchDrawable.fillColor = ColorStateList.valueOf(ctx.getThemeColor(highlightColorRes))
val touchInsetDrawable = InsetDrawable(touchDrawable, paddingStart, paddingTopBottom, paddingEnd, paddingTopBottom)
val unselectedStates = StateListDrawable()
//if possible and wanted we enable animating across states
if (animate) {
val duration = ctx.resources.getInteger(android.R.integer.config_shortAnimTime)
unselectedStates.setEnterFadeDuration(duration)
unselectedStates.setExitFadeDuration(duration)
}
unselectedStates.addState(intArrayOf(android.R.attr.state_pressed), touchInsetDrawable)
unselectedStates.addState(intArrayOf(), ColorDrawable(Color.TRANSPARENT))
unselected = unselectedStates
}
val states = StateListDrawable()
//if possible and wanted we enable animating across states
if (animate) {
val duration = ctx.resources.getInteger(android.R.integer.config_shortAnimTime)
states.setEnterFadeDuration(duration)
states.setExitFadeDuration(duration)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
states.addState(intArrayOf(android.R.attr.state_selected), selected)
states.addState(intArrayOf(), ColorDrawable(Color.TRANSPARENT))
ViewCompat.setBackground(view, states)
view.foreground = unselected
} else {
states.addState(intArrayOf(android.R.attr.state_selected), selected)
states.addState(intArrayOf(), unselected)
ViewCompat.setBackground(view, states)
}
if (isSelected && animate) {
states.state = intArrayOf(android.R.attr.state_selected)
states.jumpToCurrentState()
}
}
/**
* helper to create a stateListDrawable for the icon
*/
internal fun getIconStateList(icon: Drawable, selectedIcon: Drawable): StateListDrawable {
val iconStateListDrawable = StateListDrawable()
iconStateListDrawable.addState(intArrayOf(android.R.attr.state_selected), selectedIcon)
iconStateListDrawable.addState(intArrayOf(), icon)
return iconStateListDrawable
}
/**
* helper to calculate the optimal drawer width
*/
fun getOptimalDrawerWidth(context: Context): Int {
val possibleMinDrawerWidth = context.getScreenWidth() - context.getActionBarHeight()
val maxDrawerWidth = context.resources.getDimensionPixelSize(R.dimen.material_drawer_width)
return possibleMinDrawerWidth.coerceAtMost(maxDrawerWidth)
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/util/Extensions.kt
================================================
package com.mikepenz.materialdrawer.util
import android.os.Build
import android.view.View
import android.widget.LinearLayout
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
/**
* helper to set the vertical padding including the extra padding for deeper item hirachy level to the DrawerItems
* this is required because on API Level 17 the padding is ignored which is set via the XML
*/
internal fun View.setDrawerVerticalPadding(level: Int) {
val verticalPadding = context.resources.getDimensionPixelSize(R.dimen.material_drawer_vertical_padding)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
setPaddingRelative(verticalPadding * level, 0, verticalPadding, 0)
} else {
setPadding(verticalPadding * level, 0, verticalPadding, 0)
}
}
/**
* helper method to set the selection of the footer
*/
fun MaterialDrawerSliderView.setStickyFooterSelection(position: Int, fireOnClick: Boolean?) {
if (position > -1) {
var position = position
if (stickyFooterView != null && stickyFooterView is LinearLayout) {
val footer = stickyFooterView as LinearLayout
if (stickyFooterDivider) {
position += 1
}
if (footer.childCount > position && position >= 0) {
val drawerItem = footer.getChildAt(position).getTag(R.id.material_drawer_item) as IDrawerItem<*>
onFooterDrawerItemClick(this, drawerItem, footer.getChildAt(position), fireOnClick)
}
}
}
}
/**
* calculates the position of an drawerItem. searching by its identifier
*/
@Deprecated("Please use getPosition instead", ReplaceWith("getPosition"))
fun MaterialDrawerSliderView.getPositionByIdentifier(identifier: Long): Int {
if (identifier != -1L) {
for (i in 0 until adapter.itemCount) {
if (adapter.getItem(i)?.identifier == identifier) {
return i
}
}
}
return -1
}
/**
* gets the drawerItem with the specific identifier from a drawerItem list
*/
fun List>.getDrawerItem(identifier: Long): IDrawerItem<*>? {
if (identifier != -1L) {
for (drawerItem in this) {
if (drawerItem.identifier == identifier) {
return drawerItem
}
}
}
return null
}
/**
* gets the drawerItem by a defined tag from a drawerItem list
*/
fun List>.getDrawerItem(tag: Any?): IDrawerItem<*>? {
if (tag != null) {
for (drawerItem in this) {
if (tag == drawerItem.tag) {
return drawerItem
}
}
}
return null
}
/**
* calculates the position of an drawerItem inside the footer. searching by its identifier
*/
fun MaterialDrawerSliderView.getStickyFooterPositionByIdentifier(identifier: Long): Int {
if (identifier != -1L) {
if (stickyFooterView != null && stickyFooterView is LinearLayout) {
val footer = stickyFooterView as LinearLayout
var shadowOffset = 0
for (i in 0 until footer.childCount) {
val o = footer.getChildAt(i).getTag(R.id.material_drawer_item)
//count up the shadowOffset to return the correct position of the given item
if (o == null && stickyFooterDivider) {
shadowOffset += 1
}
if (o != null && o is IDrawerItem<*> && o.identifier == identifier) {
return i - shadowOffset
}
}
}
}
return -1
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/util/FixStateListDrawable.kt
================================================
package com.mikepenz.materialdrawer.util
import android.R
import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.graphics.drawable.StateListDrawable
/**
* http://stackoverflow.com/questions/7979440/android-cloning-a-drawable-in-order-to-make-a-statelistdrawable-with-filters
* http://stackoverflow.com/users/2075875/malachiasz
*/
@SuppressLint("InlinedApi")
class FixStateListDrawable : StateListDrawable {
var color: ColorStateList?
constructor(drawable: Drawable, color: ColorStateList?) : super() {
var drawable = drawable
drawable = drawable.mutate()
addState(intArrayOf(R.attr.state_selected), drawable)
addState(intArrayOf(), drawable)
this.color = color
}
constructor(drawable: Drawable, selectedDrawable: Drawable, color: ColorStateList?) : super() {
var drawable = drawable
var selectedDrawable = selectedDrawable
drawable = drawable.mutate()
selectedDrawable = selectedDrawable.mutate()
addState(intArrayOf(R.attr.state_selected), selectedDrawable)
addState(intArrayOf(), drawable)
this.color = color
}
override fun onStateChange(states: IntArray): Boolean {
val color = color
if (color != null) {
super.setColorFilter(color.getColorForState(states, color.defaultColor), PorterDuff.Mode.SRC_IN)
}
return super.onStateChange(states)
}
override fun isStateful(): Boolean {
return true
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/util/MaterialDrawerSliderViewExtensions.kt
================================================
package com.mikepenz.materialdrawer.util
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
import java.util.*
/**
* Add a drawerItem at a specific position
*
* @param drawerItem
* @param position
*/
fun MaterialDrawerSliderView.addItemAtPosition(position: Int, drawerItem: IDrawerItem<*>) {
itemAdapter.add(position, drawerItem)
}
/**
* Set a drawerItem at a specific position
*/
fun MaterialDrawerSliderView.setItemAtPosition(position: Int, drawerItem: IDrawerItem<*>) {
itemAdapter[position] = drawerItem
}
/**
* Remove a drawerItem at a specific position
*/
fun MaterialDrawerSliderView.removeItemByPosition(position: Int) {
if (checkDrawerItem(position, false)) {
itemAdapter.remove(position)
}
}
/**
* remove a list of drawerItems by their identifiers
*/
fun MaterialDrawerSliderView.removeItems(vararg identifiers: Long) {
identifiers.forEach {
itemAdapter.removeByIdentifier(it)
}
}
/**
* remove all items from this drawer
*/
fun MaterialDrawerSliderView.removeAllItems() {
itemAdapter.clear()
}
/**
* remove single ore more drawerItems via their identifier
*/
fun MaterialDrawerSliderView.removeItems(vararg drawerItems: IDrawerItem<*>) {
drawerItems.forEach {
itemAdapter.removeByIdentifier(it.identifier)
}
}
/**
* set single ore more DrawerItems to the Drawer
*/
fun MaterialDrawerSliderView.setItems(vararg drawerItems: IDrawerItem<*>) {
itemAdapter.set(drawerItems.asList())
}
/**
* add single ore more DrawerItems to the Drawer
*/
fun MaterialDrawerSliderView.addItems(vararg drawerItems: IDrawerItem<*>) {
itemAdapter.add(*drawerItems)
}
/**
* add single ore more DrawerItems to the Drawer
*/
fun MaterialDrawerSliderView.addItemsAtPosition(position: Int, vararg drawerItems: IDrawerItem<*>) {
itemAdapter.add(position, *drawerItems)
}
/**
* calculates the position of an drawerItem. searching by its identifier
*
* @param drawerItem
* @return
*/
fun MaterialDrawerSliderView.getPosition(drawerItem: IDrawerItem<*>): Int {
return getPosition(drawerItem.identifier)
}
/**
* calculates the position of an drawerItem. searching by its identifier
*
* @param identifier
* @return
*/
fun MaterialDrawerSliderView.getPosition(identifier: Long): Int {
return this.getPositionByIdentifier(identifier)
}
/**
* returns the DrawerItem by the given identifier
*
* @param identifier
* @return
*/
fun MaterialDrawerSliderView.getDrawerItem(identifier: Long): IDrawerItem<*>? {
val res = adapter.getItemById(identifier)
return res?.first
}
/**
* returns the found drawerItem by the given tag
*
* @param tag
* @return
*/
fun MaterialDrawerSliderView.getDrawerItem(tag: Any): IDrawerItem<*>? {
return itemAdapter.adapterItems.getDrawerItem(tag)
}
/**
* update a specific drawer item :D
* automatically identified by its id
*
* @param drawerItem
*/
fun MaterialDrawerSliderView.updateItem(drawerItem: IDrawerItem<*>) {
updateItemAtPosition(drawerItem, getPosition(drawerItem))
}
/**
* update the badge for a specific drawerItem
* identified by its id
*
* @param identifier
* @param badge
*/
fun MaterialDrawerSliderView.updateBadge(identifier: Long, badge: StringHolder) {
val drawerItem = getDrawerItem(identifier)
if (drawerItem is Badgeable) {
drawerItem.withBadge(badge)
updateItem(drawerItem)
}
}
/**
* update the name for a specific drawerItem
* identified by its id
*
* @param identifier
* @param name
*/
fun MaterialDrawerSliderView.updateName(identifier: Long, name: StringHolder) {
val drawerItem = getDrawerItem(identifier)
if (drawerItem is Nameable) {
drawerItem.withName(name)
updateItem(drawerItem)
}
}
/**
* update the name for a specific drawerItem
* identified by its id
*
* @param identifier
* @param image
*/
fun MaterialDrawerSliderView.updateIcon(identifier: Long, image: ImageHolder) {
val drawerItem = getDrawerItem(identifier)
if (drawerItem is Iconable) {
drawerItem.withIcon(image)
updateItem(drawerItem)
}
}
/**
* Update a drawerItem at a specific position
*
* @param drawerItem
* @param position
*/
fun MaterialDrawerSliderView.updateItemAtPosition(drawerItem: IDrawerItem<*>, position: Int) {
if (checkDrawerItem(position, false)) {
itemAdapter[position] = drawerItem
}
}
/**
* check if the item is within the bounds of the list
*
* @param position
* @param includeOffset
* @return
*/
internal fun MaterialDrawerSliderView.checkDrawerItem(position: Int, includeOffset: Boolean): Boolean {
return adapter.getItem(position) != null
}
/**
* calculates the position of an drawerItem. searching by it's identifier
*
* @param drawerItem
* @return
*/
fun MaterialDrawerSliderView.getStickyFooterPosition(drawerItem: IDrawerItem<*>): Int {
return getStickyFooterPositionByIdentifier(drawerItem.identifier)
}
/**
* update a specific footerDrawerItem :D
* automatically identified by its id
*
* @param drawerItem
*/
fun MaterialDrawerSliderView.updateStickyFooterItem(drawerItem: IDrawerItem<*>) {
updateStickyFooterItemAtPosition(drawerItem, getStickyFooterPosition(drawerItem))
}
/**
* update a footerDrawerItem at a specific position
*
* @param drawerItem
* @param position
*/
fun MaterialDrawerSliderView.updateStickyFooterItemAtPosition(drawerItem: IDrawerItem<*>, position: Int) {
if (stickyDrawerItems.size > position) {
stickyDrawerItems[position] = drawerItem
}
handleStickyFooterView()
}
/**
* Add a footerDrawerItem at the end
*
* @param drawerItem
*/
fun MaterialDrawerSliderView.addStickyFooterItem(drawerItem: IDrawerItem<*>) {
stickyDrawerItems.add(drawerItem)
handleStickyFooterView()
}
/**
* Add an initial DrawerItem or a DrawerItem Array for the StickyDrawerFooter
*/
fun MaterialDrawerSliderView.addStickyDrawerItems(vararg stickyDrawerItems: IDrawerItem<*>) {
Collections.addAll(this.stickyDrawerItems, *stickyDrawerItems)
handleStickyFooterView()
}
/**
* Add a footerDrawerItem at a specific position
*
* @param drawerItem
* @param position
*/
fun MaterialDrawerSliderView.addStickyFooterItemAtPosition(drawerItem: IDrawerItem<*>, position: Int) {
stickyDrawerItems.add(position, drawerItem)
handleStickyFooterView()
}
/**
* Set a footerDrawerItem at a specific position
*
* @param drawerItem
* @param position
*/
fun MaterialDrawerSliderView.setStickyFooterItemAtPosition(drawerItem: IDrawerItem<*>, position: Int) {
if (stickyDrawerItems.size > position) {
stickyDrawerItems[position] = drawerItem
}
handleStickyFooterView()
}
/**
* Remove a footerDrawerItem at a specific position
*
* @param position
*/
fun MaterialDrawerSliderView.removeStickyFooterItemAtPosition(position: Int) {
if (stickyDrawerItems.size > position) {
stickyDrawerItems.removeAt(position)
}
handleStickyFooterView()
}
/**
* Removes all footerItems from drawer
*/
fun MaterialDrawerSliderView.removeAllStickyFooterItems() {
stickyDrawerItems.clear()
handleStickyFooterView()
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/util/MenuDrawerUtils.kt
================================================
package com.mikepenz.materialdrawer.util
import android.annotation.SuppressLint
import android.view.Menu
import androidx.annotation.MenuRes
import androidx.appcompat.view.SupportMenuInflater
import androidx.appcompat.view.menu.MenuBuilder
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.model.DividerDrawerItem
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.iconDrawable
import com.mikepenz.materialdrawer.model.interfaces.nameText
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
/**
* Inflates the DrawerItems from a menu.xml
*/
@SuppressLint("RestrictedApi")
fun MaterialDrawerSliderView.inflateMenu(@MenuRes menuRes: Int) {
val menuInflater = SupportMenuInflater(context)
val mMenu = MenuBuilder(context)
menuInflater.inflate(menuRes, mMenu)
addMenuItems(mMenu, false)
}
/**
* helper method to init the drawerItems from a menu
*/
private fun MaterialDrawerSliderView.addMenuItems(mMenu: Menu?, subMenu: Boolean) {
mMenu ?: return
var groupId = R.id.material_drawer_menu_default_group
for (i in 0 until mMenu.size()) {
val mMenuItem = mMenu.getItem(i)
var iDrawerItem: IDrawerItem<*>
if (!subMenu && mMenuItem.groupId != groupId && mMenuItem.groupId != 0) {
groupId = mMenuItem.groupId
iDrawerItem = DividerDrawerItem()
itemAdapter.add(iDrawerItem)
}
if (mMenuItem.hasSubMenu()) {
iDrawerItem = PrimaryDrawerItem().apply {
nameText = mMenuItem.title ?: ""
iconDrawable = mMenuItem.icon
identifier = mMenuItem.itemId.toLong()
isEnabled = mMenuItem.isEnabled
isSelectable = false
}
itemAdapter.add(iDrawerItem)
addMenuItems(mMenuItem.subMenu, true)
} else if (mMenuItem.groupId != 0 || subMenu) {
iDrawerItem = SecondaryDrawerItem().apply {
nameText = mMenuItem.title ?: ""
iconDrawable = mMenuItem.icon
identifier = mMenuItem.itemId.toLong()
isEnabled = mMenuItem.isEnabled
}
itemAdapter.add(iDrawerItem)
} else {
iDrawerItem = PrimaryDrawerItem().apply {
nameText = mMenuItem.title ?: ""
iconDrawable = mMenuItem.icon
identifier = mMenuItem.itemId.toLong()
isEnabled = mMenuItem.isEnabled
}
itemAdapter.add(iDrawerItem)
}
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/util/Utils.kt
================================================
package com.mikepenz.materialdrawer.util
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.TypedArray
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.StateListDrawable
import android.util.TypedValue
import androidx.annotation.*
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import com.mikepenz.materialdrawer.R
private val CHECKED_STATE_SET = intArrayOf(android.R.attr.state_checked)
internal val SELECTED_STATE_SET = intArrayOf(android.R.attr.state_selected)
private val DISABLED_STATE_SET = intArrayOf(-android.R.attr.state_enabled)
private val EMPTY_STATE_SET = intArrayOf()
internal fun Context.getPrimaryDrawerTextColor(): ColorStateList {
return createDrawerItemColorStateList(this, R.styleable.MaterialDrawerSliderView_materialDrawerPrimaryText)!!
}
internal fun Context.getPrimaryDrawerIconColor(): ColorStateList {
return createDrawerItemColorStateList(this, R.styleable.MaterialDrawerSliderView_materialDrawerPrimaryIcon)!!
}
internal fun Context.getSecondaryDrawerTextColor(): ColorStateList {
return createDrawerItemColorStateList(this, R.styleable.MaterialDrawerSliderView_materialDrawerSecondaryText)!!
}
internal fun Context.getSecondaryDrawerIconColor(): ColorStateList {
return createDrawerItemColorStateList(this, R.styleable.MaterialDrawerSliderView_materialDrawerSecondaryIcon)!!
}
fun createDrawerItemColorStateList(ctx: Context, @StyleableRes styleableRes: Int): ColorStateList? {
val a = ctx.obtainStyledAttributes(null, R.styleable.MaterialDrawerSliderView, R.attr.materialDrawerStyle, R.style.Widget_MaterialDrawerStyle)
val baseColorResId = a.getResourceId(styleableRes, -1)
a.recycle()
return if (baseColorResId != -1) {
AppCompatResources.getColorStateList(ctx, baseColorResId)
} else {
null
}
}
@ColorInt
fun Context.getDividerColor(): Int {
return resolveStyledValue {
it.getColor(
R.styleable.MaterialDrawerSliderView_materialDrawerDividerColor,
getThemeColor(R.attr.materialDrawerDividerColor, getSupportColor(R.color.material_drawer_divider))
)
}
}
@ColorInt
internal fun Context.getSelectedColor(): Int {
val color = resolveStyledValue {
it.getColor(
R.styleable.MaterialDrawerSliderView_materialDrawerSelectedBackgroundColor,
getThemeColor(R.attr.materialDrawerSelectedBackgroundColor, getSupportColor(R.color.material_drawer_selected))
)
}
return color
}
internal fun Context.getHeaderSelectionTextColor(): ColorStateList {
return resolveStyledHeaderValue {
val resId = it.getResourceId(R.styleable.AccountHeaderView_materialDrawerHeaderSelectionText, -1)
AppCompatResources.getColorStateList(this, resId)
}
}
internal fun Context.getHeaderSelectionSubTextColor(): ColorStateList {
return resolveStyledHeaderValue {
val resId = it.getResourceId(R.styleable.AccountHeaderView_materialDrawerHeaderSelectionSubtext, -1)
AppCompatResources.getColorStateList(this, resId)
}
}
internal fun Context.resolveStyledHeaderValue(resolver: (typedArray: TypedArray) -> T): T {
return resolveStyledValue(R.styleable.AccountHeaderView, R.attr.materialDrawerHeaderStyle, R.style.Widget_MaterialDrawerHeaderStyle, resolver)
}
internal fun Context.resolveStyledValue(
attrs: IntArray = R.styleable.MaterialDrawerSliderView,
defStyleAttr: Int = R.attr.materialDrawerStyle,
defStyleRes: Int = R.style.Widget_MaterialDrawerStyle,
resolver: (typedArray: TypedArray) -> T
): T {
val a = obtainStyledAttributes(null, attrs, defStyleAttr, defStyleRes)
val value = resolver.invoke(a)
a.recycle()
return value
}
/**
* a helper method to get the color from the context
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
internal fun Context.getSupportColor(@ColorRes def: Int = 0): Int {
return ContextCompat.getColor(this, def)
}
/**
* helper to retrieve a float from the resources class
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
internal fun Context.getSupportFloat(@DimenRes dimen: Int): Float {
return ResourcesCompat.getFloat(this.resources, dimen)
}
/**
* a helper method to get the color from an attribute
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
internal fun Context.getThemeColor(@AttrRes attr: Int, @ColorInt def: Int = 0): Int {
val tv = TypedValue()
return if (theme.resolveAttribute(attr, tv, true)) {
if (tv.resourceId != 0) ResourcesCompat.getColor(resources, tv.resourceId, theme) else tv.data
} else def
}
/**
* helper method to get the color by attr (which is defined in the style) or by resource.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
internal fun Context.getThemeColorFromAttrOrRes(@AttrRes attr: Int, @ColorRes res: Int): Int {
var color = getThemeColor(attr)
if (color == 0) {
color = ResourcesCompat.getColor(resources, res, theme)
}
return color
}
/**
* helper to get the system default selectable background res
*/
internal fun Context.getSelectableBackgroundRes(): Int {
val outValue = TypedValue()
//it is important here to not use the android.R because this wouldn't add the latest drawable
this.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true)
return outValue.resourceId
}
/**
* helper to get the system default selectable background
*/
internal fun Context.getSelectableBackground(): Drawable? {
val selectableBackgroundRes = getSelectableBackgroundRes()
return ContextCompat.getDrawable(this, selectableBackgroundRes)
}
/**
* helper to get the system default selectable background inclusive an active state
*
* @param selected_color the selected color
* @param animate true if you want to fade over the states (only animates if API newer than Build.VERSION_CODES.HONEYCOMB)
* @return the StateListDrawable
*/
internal fun Context.getSelectableBackground(selected_color: Int, animate: Boolean): StateListDrawable? {
val states = StateListDrawable()
val clrActive = ColorDrawable(selected_color)
states.addState(intArrayOf(android.R.attr.state_selected), clrActive)
states.addState(intArrayOf(), getSelectableBackground())
//if possible and wanted we enable animating across states
if (animate) {
val duration = resources.getInteger(android.R.integer.config_shortAnimTime)
states.setEnterFadeDuration(duration)
states.setExitFadeDuration(duration)
}
return states
}
/**
* Returns the screen width in pixels
*
* @return the screen width in pixels
*/
internal fun Context.getScreenWidth(): Int {
val metrics = resources.displayMetrics
return metrics.widthPixels
}
/**
* helper to calculate the actionBar height
*/
internal fun Context.getActionBarHeight(): Int {
var actionBarHeight: Int = getThemeAttributeDimensionSize(android.R.attr.actionBarSize)
if (actionBarHeight == 0) {
actionBarHeight = resources.getDimensionPixelSize(androidx.appcompat.R.dimen.abc_action_bar_default_height_material)
}
return actionBarHeight
}
/**
* Returns the size in pixels of an attribute dimension
*
* @param attr is the attribute dimension we want to know the size from
* @return the size in pixels of an attribute dimension
*/
internal fun Context.getThemeAttributeDimensionSize(@AttrRes attr: Int): Int {
var a: TypedArray? = null
return try {
a = theme.obtainStyledAttributes(intArrayOf(attr))
a.getDimensionPixelSize(0, 0)
} finally {
a?.recycle()
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/view/BezelImageView.kt
================================================
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* The original is from Google you can find here:
* https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/widget/BezelImageView.java
*
* Modified and improved with additional functionality by Mike Penz
*/
package com.mikepenz.materialdrawer.view
import android.annotation.TargetApi
import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import android.os.Build
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewOutlineProvider
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.view.ViewCompat
import com.mikepenz.materialdrawer.R
/**
* An [android.widget.ImageView] that draws its contents inside a mask and draws a border
* drawable on top. This is useful for applying a beveled look to image contents, but is also
* flexible enough for use with other desired aesthetics.
*/
open class BezelImageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : AppCompatImageView(context, attrs, defStyle) {
private val mBlackPaint: Paint
private val mMaskedPaint: Paint
private var mBounds: Rect? = null
private var mBoundsF: RectF? = null
private val mMaskDrawable: Drawable?
private var mDrawCircularShadow = true
private var mDesaturateColorFilter: ColorMatrixColorFilter? = null
private val mSelectorAlpha = 150
private var mSelectorColor: Int = 0
private var mSelectorFilter: ColorFilter? = null
private var mCacheValid = false
private var mCacheBitmap: Bitmap
private var mCachedWidth: Int = 0
private var mCachedHeight: Int = 0
private var mIsPressed = false
private var mIsSelected: Boolean = false
private var mTempDesaturateColorFilter: ColorMatrixColorFilter? = null
private var mTempSelectorFilter: ColorFilter? = null
init {
// Attribute initialization
val a = context.obtainStyledAttributes(attrs, R.styleable.BezelImageView, defStyle, R.style.BezelImageView)
mMaskDrawable = a.getDrawable(R.styleable.BezelImageView_materialDrawerMaskDrawable)
if (mMaskDrawable != null) {
mMaskDrawable.callback = this
}
mDrawCircularShadow = a.getBoolean(R.styleable.BezelImageView_materialDrawerDrawCircularShadow, true)
mSelectorColor = a.getColor(R.styleable.BezelImageView_materialDrawerSelectorOnPress, 0)
a.recycle()
// Other initialization
mBlackPaint = Paint()
mBlackPaint.color = -0x1000000
mMaskedPaint = Paint()
mMaskedPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
// Always want a cache allocated.
mCacheBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
// Create a desaturate color filter for pressed state.
val cm = ColorMatrix()
cm.setSaturation(0f)
mDesaturateColorFilter = ColorMatrixColorFilter(cm)
//create a selectorFilter if we already have a color
if (mSelectorColor != 0) {
this.mSelectorFilter = PorterDuffColorFilter(Color.argb(mSelectorAlpha, Color.red(mSelectorColor), Color.green(mSelectorColor), Color.blue(mSelectorColor)), PorterDuff.Mode.SRC_ATOP)
}
}
override fun onSizeChanged(w: Int, h: Int, old_w: Int, old_h: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (mDrawCircularShadow) {
outlineProvider = CustomOutline(w, h)
}
}
}
@TargetApi(21)
private inner class CustomOutline internal constructor(internal var width: Int, internal var height: Int) : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
outline.setOval(0, 0, width, height)
}
}
override fun setFrame(l: Int, t: Int, r: Int, b: Int): Boolean {
val changed = super.setFrame(l, t, r, b)
mBounds = Rect(0, 0, r - l, b - t).also {
mBoundsF = RectF(it)
if (mMaskDrawable != null) {
mMaskDrawable.bounds = it
}
}
if (changed) {
mCacheValid = false
}
return changed
}
override fun onDraw(canvas: Canvas) {
val bounds = mBounds ?: return
val width = bounds.width()
val height = bounds.height()
if (width == 0 || height == 0) {
return
}
if (!mCacheValid || width != mCachedWidth || height != mCachedHeight || mIsSelected != mIsPressed) {
// Need to redraw the cache
if (width == mCachedWidth && height == mCachedHeight) {
// Have a correct-sized bitmap cache already allocated. Just erase it.
mCacheBitmap.eraseColor(0)
} else {
// Allocate a new bitmap with the correct dimensions.
mCacheBitmap.recycle()
mCacheBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
mCachedWidth = width
mCachedHeight = height
}
val cacheCanvas = Canvas(mCacheBitmap)
when {
mMaskDrawable != null -> {
val sc = cacheCanvas.save()
mMaskDrawable.draw(cacheCanvas)
if (mIsSelected) {
if (mSelectorFilter != null) {
mMaskedPaint.colorFilter = mSelectorFilter
} else {
mMaskedPaint.colorFilter = mDesaturateColorFilter
}
} else {
mMaskedPaint.colorFilter = null
}
cacheCanvas.saveLayer(mBoundsF, mMaskedPaint, Canvas.ALL_SAVE_FLAG)
super.onDraw(cacheCanvas)
cacheCanvas.restoreToCount(sc)
}
mIsSelected -> {
val sc = cacheCanvas.save()
cacheCanvas.drawRect(0f, 0f, mCachedWidth.toFloat(), mCachedHeight.toFloat(), mBlackPaint)
if (mSelectorFilter != null) {
mMaskedPaint.colorFilter = mSelectorFilter
} else {
mMaskedPaint.colorFilter = mDesaturateColorFilter
}
cacheCanvas.saveLayer(mBoundsF, mMaskedPaint, Canvas.ALL_SAVE_FLAG)
super.onDraw(cacheCanvas)
cacheCanvas.restoreToCount(sc)
}
else -> super.onDraw(cacheCanvas)
}
}
// Draw from cache
canvas.drawBitmap(mCacheBitmap, bounds.left.toFloat(), bounds.top.toFloat(), null)
//remember the previous press state
mIsPressed = isPressed
}
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
// Check for clickable state and do nothing if disabled
if (!this.isClickable) {
this.mIsSelected = false
return super.onTouchEvent(event)
}
// Set selected state based on Motion Event
when (event.action) {
MotionEvent.ACTION_DOWN -> this.mIsSelected = true
MotionEvent.ACTION_UP, MotionEvent.ACTION_SCROLL, MotionEvent.ACTION_OUTSIDE, MotionEvent.ACTION_CANCEL -> this.mIsSelected = false
}
// Redraw image and return super type
this.invalidate()
return super.dispatchTouchEvent(event)
}
override fun drawableStateChanged() {
super.drawableStateChanged()
if (mMaskDrawable != null && mMaskDrawable.isStateful) {
mMaskDrawable.state = drawableState
}
if (isDuplicateParentStateEnabled) {
ViewCompat.postInvalidateOnAnimation(this)
}
}
override fun invalidateDrawable(who: Drawable) {
if (who === mMaskDrawable) {
invalidate()
} else {
super.invalidateDrawable(who)
}
}
override fun verifyDrawable(who: Drawable): Boolean {
return who === mMaskDrawable || super.verifyDrawable(who)
}
/**
* Sets the color of the selector to be draw over the
* CircularImageView. Be sure to provide some opacity.
*
* @param selectorColor The color (including alpha) to set for the selector overlay.
*/
fun setSelectorColor(selectorColor: Int) {
this.mSelectorColor = selectorColor
this.mSelectorFilter = PorterDuffColorFilter(Color.argb(mSelectorAlpha, Color.red(mSelectorColor), Color.green(mSelectorColor), Color.blue(mSelectorColor)), PorterDuff.Mode.SRC_ATOP)
this.invalidate()
}
fun disableTouchFeedback(disable: Boolean) {
if (disable) {
mTempDesaturateColorFilter = this.mDesaturateColorFilter
mTempSelectorFilter = this.mSelectorFilter
this.mSelectorFilter = null
this.mDesaturateColorFilter = null
} else {
if (mTempDesaturateColorFilter != null) {
this.mDesaturateColorFilter = mTempDesaturateColorFilter
}
if (mTempSelectorFilter != null) {
this.mSelectorFilter = mTempSelectorFilter
}
}
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/widget/AccountHeaderView.kt
================================================
package com.mikepenz.materialdrawer.widget
import android.content.Context
import android.graphics.Typeface
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.text.TextUtils
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.View.OnClickListener
import android.view.View.OnLongClickListener
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.Guideline
import androidx.core.view.ViewCompat
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.holder.DimenHolder
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.ColorfulBadgeable
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.IProfile
import com.mikepenz.materialdrawer.util.*
import com.mikepenz.materialdrawer.view.BezelImageView
import java.util.*
/**
* This view offers support to add an account switcher to the [MaterialDrawerSliderView]
* It will hook onto the [MaterialDrawerSliderView] and coordinate updating and showing the proper set of elements
*/
open class AccountHeaderView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, compact: Boolean? = null) :
ConstraintLayout(context, attrs, defStyleAttr) {
var savedInstanceKey: String = ""
// global references to views we need later
val accountHeader: View
val statusBarGuideline: Guideline
val accountHeaderBackground: ImageView
val currentProfileView: BezelImageView
val currentProfileBadgeView: TextView
val accountSwitcherArrow: ImageView
val currentProfileName: TextView
val currentProfileEmail: TextView
val profileFirstView: BezelImageView
val profileFirstBadgeView: TextView
val profileSecondView: BezelImageView
val profileSecondBadgeView: TextView
val profileThirdView: BezelImageView
val profileThirdBadgeView: TextView
/** Temporarily disable invalidation for optimizations */
private var invalidationEnabled: Boolean = true
private var invalidateHeader: Boolean = false
private var invalidateList: Boolean = false
/**
* Selects the given profile and sets it to the new active profile
*
* @param profile
*/
var activeProfile: IProfile?
get() = currentProfile
set(profile) {
profile?.also { setActiveProfile(it, false) }
}
// global references to the profiles
internal var currentProfile: IProfile? = null
internal var profileFirst: IProfile? = null
internal var profileSecond: IProfile? = null
internal var profileThird: IProfile? = null
// global stuff
internal var _selectionListShown = false
var selectionListShown: Boolean
get() = _selectionListShown
set(value) {
if (value != _selectionListShown) {
toggleSelectionList()
}
}
var accountHeaderTextSectionBackgroundResource = -1
set(value) {
field = value
buildProfiles()
}
// defines if we use the compactStyle
internal var compactStyle = false
// the typeface used for textViews within the AccountHeader
var typeface: Typeface? = null
set(value) {
field = value
reconstructHeader()
}
// the typeface used for name textView only. overrides typeface
var nameTypeface: Typeface? = null
set(value) {
field = value
reconstructHeader()
}
// the typeface used for email textView only. overrides typeface
var emailTypeface: Typeface? = null
set(value) {
field = value
reconstructHeader()
}
// set the account header height
var height: DimenHolder? = null
set(value) {
field = value
reconstructHeader()
}
//the current selected profile is visible in the list
var currentHiddenInList = false
//set to hide the first or second line
var selectionFirstLineShown = true
set(value) {
field = value
updateHeaderAndList()
}
var selectionSecondLineShown = true
set(value) {
field = value
updateHeaderAndList()
}
//set one of these to define the text in the first or second line with in the account selector
var selectionFirstLine: String? = null
set(value) {
field = value
updateHeaderAndList()
}
var selectionSecondLine: String? = null
set(value) {
field = value
updateHeaderAndList()
}
// set no divider below the header
var paddingBelowHeader = true
set(value) {
field = value
sliderView?.headerPadding = paddingBelowHeader
}
// set no divider below the header
var dividerBelowHeader = true
set(value) {
field = value
sliderView?.headerDivider = dividerBelowHeader
}
//the background for the header
var headerBackground: ImageHolder? = null
set(value) {
value?.applyTo(accountHeaderBackground, DrawerImageLoader.Tags.ACCOUNT_HEADER.name)
field = value
}
//background scale type
var headerBackgroundScaleType: ImageView.ScaleType?
get() = accountHeaderBackground.scaleType
set(value) {
if (value != null) {
accountHeaderBackground.scaleType = value
}
}
//profile images in the header are shown or not
var profileImagesVisible = true
set(value) {
field = value
buildProfiles()
}
//only the main profile image is visible
var onlyMainProfileImageVisible = false
set(value) {
field = value
buildProfiles()
}
//show small profile images but hide MainProfileImage
var onlySmallProfileImagesVisible = false
set(value) {
field = value
buildProfiles()
}
//close the drawer after a profile was clicked in the list
var closeDrawerOnProfileListClick: Boolean? = null
//reset the drawer list to the main drawer list after the profile was clicked in the list
var resetDrawerOnProfileListClick = true
// set the profile images clickable or not
var profileImagesClickable = true
set(value) {
field = value
buildProfiles()
}
// set to use the alternative profile header switching
var alternativeProfileHeaderSwitching = false
// enable 3 small header previews
var threeSmallProfileImages = false
set(value) {
field = value
buildProfiles()
}
// enable to show badges on current profile images
var displayBadgesOnCurrentProfileImage = true
set(value) {
field = value
buildProfiles()
}
// enable to show badges on small profile images
var displayBadgesOnSmallProfileImages = false
set(value) {
field = value
buildProfiles()
}
//the delay which is waited before the drawer is closed
var onProfileClickDrawerCloseDelay = 100
// the onAccountHeaderProfileImageListener to set
var onAccountHeaderProfileImageListener: ((view: View, profile: IProfile, current: Boolean) -> Boolean)? = null
// the onAccountHeaderSelectionListener to set
var onAccountHeaderSelectionViewClickListener: ((view: View, profile: IProfile) -> Boolean)? = null
//set the selection list enabled if there is only a single profile
var selectionListEnabledForSingleProfile = true
set(value) {
field = value
buildProfiles()
}
//set the selection enabled disabled
var selectionListEnabled = true
set(value) {
field = value
buildProfiles()
}
// the profiles to display
var profiles: MutableList? = null
set(value) {
field = value
value?.mapNotNull { it as? IDrawerItem<*> }?.forEach { item ->
sliderView?.idDistributor?.checkId(item)
}
updateHeaderAndList()
}
// the click listener to be fired on profile or selection click
var onAccountHeaderListener: ((view: View?, profile: IProfile, current: Boolean) -> Boolean)? = null
//the on long click listener to be fired on profile longClick inside the list
var onAccountHeaderItemLongClickListener: ((view: View?, profile: IProfile, current: Boolean) -> Boolean)? = null
// the drawer to set the AccountSwitcher for
var sliderView: MaterialDrawerSliderView? = null
set(value) {
field = value
if (field?.accountHeader != this) {
field?.accountHeader = this
}
}
// miniDrawer
val miniDrawer: MiniDrawerSliderView?
get() = sliderView?.miniDrawer
/**
* onProfileClickListener to notify onClick on the current profile image
*/
private val onCurrentProfileClickListener = OnClickListener { v -> onProfileImageClick(v, true) }
/**
* onProfileClickListener to notify onClick on a profile image
*/
private val onProfileClickListener = OnClickListener { v -> onProfileImageClick(v, false) }
/**
* onProfileLongClickListener to call the onProfileImageLongClick on the current profile image
*/
private val onCurrentProfileLongClickListener = OnLongClickListener { v ->
if (onAccountHeaderProfileImageListener != null) {
val profile = v.getTag(R.id.material_drawer_profile_header) as IProfile
return@OnLongClickListener onAccountHeaderProfileImageListener?.invoke(v, profile, true)
?: false
}
false
}
/**
* onProfileLongClickListener to call the onProfileImageLongClick on a profile image
*/
private val onProfileLongClickListener = OnLongClickListener { v ->
if (onAccountHeaderProfileImageListener != null) {
val profile = v.getTag(R.id.material_drawer_profile_header) as IProfile
return@OnLongClickListener onAccountHeaderProfileImageListener?.invoke(v, profile, false)
?: false
}
false
}
/**
* get the current selection
*
* @return
*/
internal val currentSelection: Int
get() {
val mProfiles = this.profiles ?: return -1
val mCurrentProfile = this.currentProfile ?: return -1
for ((i, profile) in mProfiles.withIndex()) {
if (profile === mCurrentProfile) {
return i
}
}
return -1
}
/**
* onDrawerItemClickListener to catch the selection for the new profile!
*/
private val onDrawerItemClickListener: ((View?, IDrawerItem<*>, Int) -> Boolean) = { view: View?, drawerItem: IDrawerItem<*>, position: Int ->
val isCurrentSelectedProfile: Boolean = if (drawerItem is IProfile && drawerItem.isSelectable) {
switchProfiles(drawerItem as IProfile)
} else {
false
}
if (resetDrawerOnProfileListClick) {
sliderView?.onDrawerItemClickListener = null
}
//wrap the onSelection call and the reset stuff within a handler to prevent lag
if (resetDrawerOnProfileListClick && sliderView != null) {
resetDrawerContent()
}
//notify the MiniDrawer about the clicked profile (only if one exists and is hooked to the Drawer
miniDrawer?.onProfileClick()
var consumed = false
if (drawerItem is IProfile) {
consumed = onAccountHeaderListener?.invoke(view, drawerItem as IProfile, isCurrentSelectedProfile) ?: false
}
//if a custom behavior was chosen via the CloseDrawerOnProfileListClick then use this. else react on the result of the onProfileChanged listener
closeDrawerOnProfileListClick?.let {
consumed = consumed && (!it)
}
//totally custom handling of the drawer behavior as otherwise the selection of the profile list is set to the Drawer
if (!consumed) {
//close the drawer after click
sliderView?.closeDrawerDelayed()
}
//consume the event to prevent setting the clicked item as selected in the already switched item list
true
}
/**
* onDrawerItemLongClickListener to catch the longClick for a profile
*/
private val onDrawerItemLongClickListener: ((View?, IDrawerItem<*>, Int) -> Boolean) = { view: View?, drawerItem: IDrawerItem<*>, position: Int ->
//if a longClickListener was defined use it
if (onAccountHeaderItemLongClickListener != null) {
val isCurrentSelectedProfile: Boolean = drawerItem.isSelected
if (drawerItem is IProfile) {
onAccountHeaderItemLongClickListener?.invoke(view, drawerItem as IProfile, isCurrentSelectedProfile) ?: false
} else {
false
}
} else {
false
}
}
init {
val headerLayout = context.resolveStyledHeaderValue {
compactStyle = compact ?: it.getBoolean(R.styleable.AccountHeaderView_materialDrawerCompactStyle, false)
it.getResourceId(
R.styleable.AccountHeaderView_materialDrawerHeaderLayout,
if (compactStyle) R.layout.material_drawer_compact_header else R.layout.material_drawer_header
)
}
// the account header
accountHeader = LayoutInflater.from(context).inflate(headerLayout, this, true)
// get the header view within the container
statusBarGuideline = findViewById(R.id.material_drawer_statusbar_guideline)
// get the background view
accountHeaderBackground = findViewById(R.id.material_drawer_account_header_background)
// set the arrow :D
accountSwitcherArrow = findViewById(R.id.material_drawer_account_header_text_switcher)
//get the fields for the name
currentProfileView = findViewById(R.id.material_drawer_account_header_current)
currentProfileBadgeView = findViewById(R.id.material_drawer_account_header_current_badge)
currentProfileName = findViewById(R.id.material_drawer_account_header_name)
currentProfileEmail = findViewById(R.id.material_drawer_account_header_email)
profileFirstView = findViewById(R.id.material_drawer_account_header_small_first)
profileFirstBadgeView = findViewById(R.id.material_drawer_account_header_small_first_badge)
profileSecondView = findViewById(R.id.material_drawer_account_header_small_second)
profileSecondBadgeView = findViewById(R.id.material_drawer_account_header_small_second_badge)
profileThirdView = findViewById(R.id.material_drawer_account_header_small_third)
profileThirdBadgeView = findViewById(R.id.material_drawer_account_header_small_third_badge)
reconstructHeader()
//the default min header height by default 148dp
val defaultHeaderMinHeight = context.resources.getDimensionPixelSize(R.dimen.material_drawer_account_header_height)
// set the insets
ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets ->
// handle everything if we have a translucent status bar which only is possible on API >= 19
val topInset = insets.systemWindowInsetTop ?: 0
statusBarGuideline.setGuidelineBegin(topInset)
val height = resolveHeight()
var newHeight = height
//in fact it makes no difference if we have a translucent statusBar or not. we want 9/16 just if we are not compact
if (compactStyle) {
newHeight += topInset
} else if (newHeight - topInset <= defaultHeaderMinHeight) {
//if the height + statusBar of the header is lower than the required 148dp + statusBar we change the height to be able to display all the data
newHeight = defaultHeaderMinHeight + topInset
}
//set the height for the header
setHeaderHeight(newHeight)
insets
}
}
private fun resolveHeight(): Int {
// handle the height for the header
var height = 0
this.height?.let {
height = it.asPixel(context)
} ?: run {
height = if (compactStyle) {
context.resources.getDimensionPixelSize(R.dimen.material_drawer_account_header_height_compact)
} else {
//calculate the header height by getting the optimal drawer width and calculating it * 9 / 16
(getOptimalDrawerWidth(context) * NAVIGATION_DRAWER_ACCOUNT_ASPECT_RATIO).toInt()
}
}
return height
}
private fun reconstructHeader() {
if (!invalidationEnabled) {
invalidateHeader = true
return
}
invalidateHeader = false
//set the height for the header
setHeaderHeight(resolveHeight())
// set the background
headerBackground?.applyTo(accountHeaderBackground, DrawerImageLoader.Tags.ACCOUNT_HEADER.name)
// get the text color to use for the text section
val textColor =
context.getHeaderSelectionTextColor() // textColor.applyColor(context, R.attr.materialDrawerHeaderSelectionText, R.color.material_drawer_header_selection_text)
val subTextColor =
context.getHeaderSelectionSubTextColor() // this.textColor.applyColor(context, R.attr.materialDrawerHeaderSelectionSubtext, R.color.material_drawer_header_selection_subtext)
if (accountHeaderTextSectionBackgroundResource == -1) {
accountHeaderTextSectionBackgroundResource = context.getSelectableBackgroundRes()
}
handleSelectionView(currentProfile, true)
// set the arrow
val drawable = AppCompatResources.getDrawable(context, R.drawable.material_drawer_ico_menu_down)
if (drawable != null) {
val size = context.resources.getDimensionPixelSize(R.dimen.material_drawer_account_header_dropdown)
accountSwitcherArrow.setImageDrawable(FixStateListDrawable(drawable, subTextColor).apply {
setBounds(0, 0, size, size)
})
}
//IconicsDrawable(context, MaterialDrawerFont.Icon.mdf_arrow_drop_down).size(IconicsSize.res(R.dimen.material_drawer_account_header_dropdown)).padding(IconicsSize.res(R.dimen.material_drawer_account_header_dropdown_padding)).color(IconicsColor.colorList(subTextColor))
//set the typeface for the AccountHeader
if (nameTypeface != null) {
currentProfileName.typeface = nameTypeface
} else if (typeface != null) {
currentProfileName.typeface = typeface
}
if (emailTypeface != null) {
currentProfileEmail.typeface = emailTypeface
} else if (typeface != null) {
currentProfileEmail.typeface = typeface
}
currentProfileName.setTextColor(textColor)
currentProfileEmail.setTextColor(subTextColor)
//calculate the profiles to set
calculateProfiles()
//process and build the profiles
buildProfiles()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
requestApplyInsets()
}
}
/**
* onSelectionClickListener to notify the onClick on the checkbox
*/
private val onSelectionClickListener = OnClickListener { v ->
val consumed = onAccountHeaderSelectionViewClickListener?.invoke(v, v.getTag(R.id.material_drawer_profile_header) as IProfile)
?: false
if (accountSwitcherArrow.visibility == View.VISIBLE && !consumed) {
toggleSelectionList()
}
}
/**
* Add a new profile at a specific position to the list
*
* @param profile
* @param position
*/
fun addProfile(profile: IProfile, position: Int) {
if (profiles == null) {
profiles = ArrayList()
}
profiles?.add(position, profile)
updateHeaderAndList()
}
/**
* add single ore more DrawerItems to the Drawer
*/
fun addProfiles(vararg profiles: IProfile) {
if (this.profiles == null) {
this.profiles = ArrayList()
}
this.profiles?.let {
it.mapNotNull { di -> di as? IDrawerItem<*> }.forEach { item ->
sliderView?.idDistributor?.checkId(item)
}
Collections.addAll(it, *profiles)
}
updateHeaderAndList()
}
/**
* remove a profile from the given position
*/
fun removeProfile(position: Int) {
if (this.profiles != null && this.profiles?.size ?: 0 > position) {
this.profiles?.removeAt(position)
}
updateHeaderAndList()
}
/**
* remove the profile with the given identifier
*/
fun removeProfileByIdentifier(identifier: Long) {
val found = getPositionByIdentifier(identifier)
if (found > -1) {
this.profiles?.removeAt(found)
}
this.updateHeaderAndList()
}
/**
* try to remove the given profile
*/
fun removeProfile(profile: IProfile) {
removeProfileByIdentifier(profile.identifier)
}
/**
* Clear the header
*/
fun clear() {
this.profiles = null
//calculate the profiles to set
calculateProfiles()
//process and build the profiles
buildProfiles()
}
/**
* @param drawer
* @return
*/
fun attachToSliderView(sliderView: MaterialDrawerSliderView) {
this.sliderView = sliderView
//set the top padding to 0 as this would happen when the AccountHeader is created during Drawer build time
sliderView.recyclerView.setPadding(sliderView.recyclerView.paddingLeft, 0, sliderView.recyclerView.paddingRight, sliderView.recyclerView.paddingBottom)
//everything created. now set the header
sliderView.headerView = this
this.sliderView?.accountHeader = this
}
/**
* create the drawer with the values of a savedInstance
*
* @param savedInstance
* @return
*/
fun withSavedInstance(savedInstance: Bundle?) {
savedInstance ?: return
// try to restore all saved values again
val selection = savedInstance.getInt(BUNDLE_SELECTION_HEADER + savedInstanceKey, -1)
if (selection != -1) {
//predefine selection (should be the first element
profiles?.let {
if (selection > -1 && selection < it.size) {
switchProfiles(it[selection])
}
}
}
}
/**
* helper method to set the height for the header!
*
* @param height
*/
private fun setHeaderHeight(height: Int) {
this.layoutParams?.let {
it.height = height
this.layoutParams = it
}
val lp = accountHeader.layoutParams
if (lp != null) {
lp.height = height
accountHeader.layoutParams = lp
}
val p = accountHeaderBackground.layoutParams
p.height = height
accountHeaderBackground.layoutParams = p
}
/**
* a small helper to handle the selectionView
*
* @param on
*/
private fun handleSelectionView(profile: IProfile?, on: Boolean) {
if (on) {
if (Build.VERSION.SDK_INT >= 23) {
this.foreground = AppCompatResources.getDrawable(this.context, accountHeaderTextSectionBackgroundResource)
} else {
// todo foreground thing?
}
this.setOnClickListener(onSelectionClickListener)
this.setTag(R.id.material_drawer_profile_header, profile)
} else {
if (Build.VERSION.SDK_INT >= 23) {
this.foreground = null
} else {
// TODO foreground reset
}
this.setOnClickListener(null)
}
}
/**
* helper method to calculate the order of the profiles
*/
internal fun calculateProfiles() {
if (profiles == null) {
profiles = ArrayList()
}
profiles?.let { mProfiles ->
if (currentProfile == null) {
var setCount = 0
val size = mProfiles.size
for (i in 0 until size) {
if (mProfiles.size > i && mProfiles[i].isSelectable) {
if (setCount == 0 && currentProfile == null) {
currentProfile = mProfiles[i]
} else if (setCount == 1 && profileFirst == null) {
profileFirst = mProfiles[i]
} else if (setCount == 2 && profileSecond == null) {
profileSecond = mProfiles[i]
} else if (setCount == 3 && profileThird == null) {
profileThird = mProfiles[i]
}
setCount++
}
}
return
}
val previousActiveProfiles = arrayOf(currentProfile, profileFirst, profileSecond, profileThird)
val newActiveProfiles = arrayOfNulls(4)
val unusedProfiles = Stack()
// try to keep existing active profiles in the same positions
for (i in mProfiles.indices) {
val p = mProfiles[i]
if (p.isSelectable) {
var used = false
for (j in 0..3) {
if (previousActiveProfiles[j] === p) {
newActiveProfiles[j] = p
used = true
break
}
}
if (!used) {
unusedProfiles.push(p)
}
}
}
val activeProfiles = Stack()
// try to fill the gaps with new available profiles
for (i in 0..3) {
if (newActiveProfiles[i] != null) {
activeProfiles.push(newActiveProfiles[i])
} else if (!unusedProfiles.isEmpty()) {
activeProfiles.push(unusedProfiles.pop())
}
}
val reversedActiveProfiles = Stack()
while (!activeProfiles.empty()) {
reversedActiveProfiles.push(activeProfiles.pop())
}
// reassign active profiles
currentProfile = if (reversedActiveProfiles.isEmpty()) {
null
} else {
reversedActiveProfiles.pop()
}
profileFirst = if (reversedActiveProfiles.isEmpty()) {
null
} else {
reversedActiveProfiles.pop()
}
profileSecond = if (reversedActiveProfiles.isEmpty()) {
null
} else {
reversedActiveProfiles.pop()
}
profileThird = if (reversedActiveProfiles.isEmpty()) {
null
} else {
reversedActiveProfiles.pop()
}
}
}
/**
* helper method to switch the profiles
*
* @param newSelection
* @return true if the new selection was the current profile
*/
internal fun switchProfiles(newSelection: IProfile?): Boolean {
if (newSelection == null) {
return false
}
if (currentProfile === newSelection) {
return true
}
if (alternativeProfileHeaderSwitching) {
var prevSelection = -1
when {
profileFirst === newSelection -> prevSelection = 1
profileSecond === newSelection -> prevSelection = 2
profileThird === newSelection -> prevSelection = 3
}
val tmp = currentProfile
currentProfile = newSelection
when (prevSelection) {
1 -> profileFirst = tmp
2 -> profileSecond = tmp
3 -> profileThird = tmp
}
} else {
if (profiles != null) {
val previousActiveProfiles = ArrayList(Arrays.asList(currentProfile, profileFirst, profileSecond, profileThird))
if (previousActiveProfiles.contains(newSelection)) {
var position = -1
for (i in 0..3) {
if (previousActiveProfiles[i] === newSelection) {
position = i
break
}
}
if (position != -1) {
previousActiveProfiles.removeAt(position)
previousActiveProfiles.add(0, newSelection)
currentProfile = previousActiveProfiles[0]
profileFirst = previousActiveProfiles[1]
profileSecond = previousActiveProfiles[2]
profileThird = previousActiveProfiles[3]
}
} else {
profileThird = profileSecond
profileSecond = profileFirst
profileFirst = currentProfile
currentProfile = newSelection
}
}
}
//if we only show the small profile images we have to make sure the first (would be the current selected) profile is also shown
if (onlySmallProfileImagesVisible) {
profileThird = profileSecond
profileSecond = profileFirst
profileFirst = currentProfile
//currentProfile = profileThird;
}
buildProfiles()
return false
}
/**
* helper method to build the views for the ui
*/
internal fun buildProfiles() {
if (!invalidationEnabled) {
invalidateList = true
return
}
invalidateList = false
currentProfileView.visibility = View.GONE
currentProfileBadgeView.visibility = View.GONE
accountSwitcherArrow.visibility = View.GONE
profileFirstView.visibility = View.GONE
profileFirstView.setOnClickListener(null)
profileFirstBadgeView.visibility = View.GONE
profileSecondView.visibility = View.GONE
profileSecondView.setOnClickListener(null)
profileSecondBadgeView.visibility = View.GONE
profileThirdView.visibility = View.GONE
profileThirdView.setOnClickListener(null)
profileThirdBadgeView.visibility = View.GONE
currentProfileName.text = ""
currentProfileEmail.text = ""
handleSelectionView(currentProfile, true)
val mCurrentProfile = this.currentProfile
val mProfiles = this.profiles
if (mCurrentProfile != null) {
if ((profileImagesVisible || onlyMainProfileImageVisible) && !onlySmallProfileImagesVisible) {
currentProfileView.contentDescription = mCurrentProfile.description?.getText(context) ?: mCurrentProfile.name?.getText(context)
?: currentProfileView.context.getString(R.string.material_drawer_profile_content_description)
setImageOrPlaceholder(currentProfileView, mCurrentProfile.icon)
if (profileImagesClickable) {
currentProfileView.setOnClickListener(onCurrentProfileClickListener)
currentProfileView.setOnLongClickListener(onCurrentProfileLongClickListener)
currentProfileView.disableTouchFeedback(false)
} else {
currentProfileView.disableTouchFeedback(true)
}
currentProfileView.visibility = View.VISIBLE
currentProfileView.invalidate()
var badgeVisible = false
if (displayBadgesOnCurrentProfileImage) {
(mCurrentProfile as? ColorfulBadgeable)?.let { badgeable ->
badgeVisible = StringHolder.applyToOrHide(badgeable.badge, currentProfileBadgeView)
if (badgeVisible) {
badgeable.badgeStyle?.style(currentProfileBadgeView, context.getPrimaryDrawerTextColor())
typeface?.let { typeface -> currentProfileBadgeView.typeface = typeface }
}
}
}
currentProfileBadgeView.visibility = if (badgeVisible) View.VISIBLE else View.GONE
} else if (compactStyle) {
currentProfileView.visibility = View.GONE
currentProfileBadgeView.visibility = View.GONE
}
handleSelectionView(mCurrentProfile, true)
accountSwitcherArrow.visibility = View.VISIBLE
currentProfileView.setTag(R.id.material_drawer_profile_header, mCurrentProfile)
StringHolder.applyTo(mCurrentProfile.name, currentProfileName)
StringHolder.applyTo(mCurrentProfile.description, currentProfileEmail)
/**
* Apply the profile information to the provided imageView
*/
fun IProfile?.applyProfile(imageView: BezelImageView, badgeView: TextView) {
this ?: return
setImageOrPlaceholder(imageView, this.icon)
imageView.setTag(R.id.material_drawer_profile_header, this)
imageView.contentDescription = this.description?.getText(context) ?: this.name?.getText(context)
?: imageView.context.getString(R.string.material_drawer_profile_content_description)
if (profileImagesClickable) {
imageView.setOnClickListener(onProfileClickListener)
imageView.setOnLongClickListener(onProfileLongClickListener)
imageView.disableTouchFeedback(false)
} else {
imageView.disableTouchFeedback(true)
}
imageView.visibility = View.VISIBLE
imageView.invalidate()
var badgeVisible = false
if (displayBadgesOnSmallProfileImages) {
(this as? ColorfulBadgeable)?.let { badgeable ->
badgeVisible = StringHolder.applyToOrHide(badgeable.badge, badgeView)
if (badgeVisible) {
badgeable.badgeStyle?.style(badgeView, context.getPrimaryDrawerTextColor())
typeface?.let { typeface -> badgeView.typeface = typeface }
}
}
}
badgeView.visibility = if (badgeVisible) View.VISIBLE else View.GONE
}
if (profileImagesVisible && !onlyMainProfileImageVisible) {
profileFirst.applyProfile(profileFirstView, profileFirstBadgeView)
profileSecond.applyProfile(profileSecondView, profileSecondBadgeView)
if (threeSmallProfileImages) {
profileThird.applyProfile(profileThirdView, profileThirdBadgeView)
}
}
} else if (mProfiles != null && mProfiles.size > 0) {
val profile = mProfiles[0]
setTag(R.id.material_drawer_profile_header, profile)
handleSelectionView(mCurrentProfile, true)
accountSwitcherArrow.visibility = View.VISIBLE
}
if (!selectionFirstLineShown) {
currentProfileName.visibility = View.GONE
}
if (!TextUtils.isEmpty(selectionFirstLine)) {
currentProfileName.text = selectionFirstLine
}
if (!selectionSecondLineShown) {
currentProfileEmail.visibility = View.GONE
}
if (!TextUtils.isEmpty(selectionSecondLine)) {
currentProfileEmail.text = selectionSecondLine
}
//if we disabled the list
if (!selectionListEnabled || !selectionListEnabledForSingleProfile && profileFirst == null && (mProfiles == null || mProfiles?.size == 1)) {
accountSwitcherArrow.visibility = View.GONE
handleSelectionView(null, false)
}
//if we disabled the list but still have set a custom listener
if (onAccountHeaderSelectionViewClickListener != null) {
handleSelectionView(mCurrentProfile, true)
}
}
/**
* small helper method to set an profile image or a placeholder
*
* @param iv
* @param imageHolder
*/
private fun setImageOrPlaceholder(iv: ImageView, imageHolder: ImageHolder?) {
//cancel previous started image loading processes
DrawerImageLoader.instance.cancelImage(iv)
//set the placeholder
iv.setImageDrawable(DrawerImageLoader.instance.imageLoader?.placeholder(iv.context, DrawerImageLoader.Tags.PROFILE.name))
//set the real image (probably also the uri)
imageHolder?.applyTo(iv, DrawerImageLoader.Tags.PROFILE.name)
}
/**
* calls the mOnAccountHEaderProfileImageListener and continues with the actions afterwards
*
* @param v
* @param current
*/
private fun onProfileImageClick(v: View, current: Boolean) {
val profile = v.getTag(R.id.material_drawer_profile_header) as IProfile
val consumed = onAccountHeaderProfileImageListener?.invoke(v, profile, current)
?: false
//if the event was already consumed by the click don't continue. note that this will also stop the profile change event
if (!consumed) {
onProfileClick(v, current)
}
}
internal fun onProfileClick(v: View, current: Boolean) {
val profile = v.getTag(R.id.material_drawer_profile_header) as IProfile
switchProfiles(profile)
//reset the drawer content
resetDrawerContent()
//notify the MiniDrawer about the clicked profile (only if one exists and is hooked to the Drawer
miniDrawer?.onProfileClick()
//notify about the changed profile
val consumed = onAccountHeaderListener?.invoke(v, profile, current) ?: false
if (!consumed) {
if (onProfileClickDrawerCloseDelay > 0) {
Handler().postDelayed({
sliderView?.drawerLayout?.closeDrawers()
}, onProfileClickDrawerCloseDelay.toLong())
} else {
sliderView?.drawerLayout?.closeDrawers()
}
}
}
/**
* helper method to toggle the collection
*/
internal fun toggleSelectionList() {
val sliderView = sliderView ?: return
//if we already show the list. reset everything instead
_selectionListShown = if (sliderView.switchedDrawerContent()) {
resetDrawerContent()
false
} else {
//build and set the drawer selection list
buildDrawerSelectionList()
// update the arrow image within the drawer
accountSwitcherArrow.clearAnimation()
ViewCompat.animate(accountSwitcherArrow).rotation(180f).start()
true
}
}
/**
* helper method to build and set the drawer selection list
*/
internal fun buildDrawerSelectionList() {
var selectedPosition = -1
var position = 0
val profileDrawerItems = ArrayList>()
profiles?.let { mProfiles ->
for (profile in mProfiles) {
if (profile === currentProfile) {
if (currentHiddenInList) {
continue
} else {
selectedPosition = sliderView?.itemAdapter?.getGlobalPosition(position)
?: 0
}
}
if (profile is IDrawerItem<*>) {
(profile as IDrawerItem<*>).isSelected = false
profileDrawerItems.add(profile as IDrawerItem<*>)
}
position += 1
}
}
sliderView?.switchDrawerContent(onDrawerItemClickListener, onDrawerItemLongClickListener, profileDrawerItems, selectedPosition)
}
/**
* helper method to reset the drawer content
*/
private fun resetDrawerContent() {
sliderView?.resetDrawerContent()
accountSwitcherArrow.clearAnimation()
ViewCompat.animate(accountSwitcherArrow).rotation(0f).start()
}
/**
* Updates the header and also rebuids the list.
* This is called after modifications to the items were made
*/
fun updateHeaderAndList() {
if (!invalidationEnabled) {
invalidateList = true
return
}
invalidateList = false
//recalculate the profiles
calculateProfiles()
//update the profiles in the header
buildProfiles()
//if we currently show the list add the new item directly to it
if (selectionListShown) {
buildDrawerSelectionList()
}
}
/**
* add the values to the bundle for saveInstanceState
*
* @param savedInstanceState
* @return
*/
fun saveInstanceState(savedInstanceState: Bundle): Bundle {
savedInstanceState.putInt(BUNDLE_SELECTION_HEADER + savedInstanceKey, currentSelection)
return savedInstanceState
}
/**
* Selects the given profile and sets it to the new active profile
*
* @param profile
*/
fun setActiveProfile(profile: IProfile, fireOnProfileChanged: Boolean) {
val isCurrentSelectedProfile = switchProfiles(profile)
//if the selectionList is shown we should also update the current selected profile in the list
if (sliderView != null && selectionListShown) {
sliderView?.setSelection(profile.identifier, false)
}
//fire the event if enabled and a listener is set
if (fireOnProfileChanged && onAccountHeaderListener != null) {
onAccountHeaderListener?.invoke(null, profile, isCurrentSelectedProfile)
}
}
/**
* Selects a profile by its identifier
*
* @param identifier
*/
@JvmOverloads
fun setActiveProfile(identifier: Long, fireOnProfileChanged: Boolean = false) {
profiles?.forEach { profile ->
if (profile.identifier == identifier) {
setActiveProfile(profile, fireOnProfileChanged)
return
}
}
}
/**
* Helper method to update a profile using its identifier
*
* @param newProfile
*/
fun updateProfile(newProfile: IProfile) {
val found = getPositionByIdentifier(newProfile.identifier)
if (found > -1) {
profiles?.set(found, newProfile)
updateHeaderAndList()
}
}
/**
* gets the position of a profile by its identifier
*
* @param identifier
* @return
*/
private fun getPositionByIdentifier(identifier: Long): Int {
if (identifier != -1L) {
profiles?.forEachIndexed { index, iProfile ->
if (iProfile.identifier == identifier) {
return index
}
}
}
return -1
}
/** Applies properties in an optimized form. Will disable invalidation of the AccountHeaderView for the inner property set operations */
fun apply(block: AccountHeaderView.() -> Unit): AccountHeaderView {
invalidationEnabled = false
block()
invalidationEnabled = true
if (invalidateList) {
updateHeaderAndList()
}
if (invalidateHeader) {
reconstructHeader()
}
return this
}
companion object {
const val NAVIGATION_DRAWER_ACCOUNT_ASPECT_RATIO = 9.0 / 16.0
const val BUNDLE_SELECTION_HEADER = "bundle_selection_header"
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/widget/MaterialDrawerSliderView.kt
================================================
package com.mikepenz.materialdrawer.widget
import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.RelativeLayout
import androidx.core.view.GravityCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.drawerlayout.widget.DrawerLayout
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.IAdapter
import com.mikepenz.fastadapter.adapters.ItemAdapter
import com.mikepenz.fastadapter.adapters.ModelAdapter
import com.mikepenz.fastadapter.expandable.ExpandableExtension
import com.mikepenz.fastadapter.expandable.ExpandableExtensionFactory
import com.mikepenz.fastadapter.extensions.ExtensionsFactories
import com.mikepenz.fastadapter.select.SelectExtension
import com.mikepenz.fastadapter.select.SelectExtensionFactory
import com.mikepenz.fastadapter.select.getSelectExtension
import com.mikepenz.fastadapter.utils.DefaultIdDistributor
import com.mikepenz.fastadapter.utils.DefaultIdDistributorImpl
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.holder.DimenHolder
import com.mikepenz.materialdrawer.model.AbstractDrawerItem
import com.mikepenz.materialdrawer.model.ContainerDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.util.*
/**
* This view is a simple drop in view for the [DrawerLayout] offering a convenient API to provide a nice and flexible slider view following
* the material design guidelines v2.
*/
open class MaterialDrawerSliderView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.materialDrawerStyle) :
RelativeLayout(context, attrs, defStyleAttr) {
/** Temporarily disable invalidation for optimizations */
private var invalidationEnabled: Boolean = true
private var invalidateContent: Boolean = false
private var invalidateHeader: Boolean = false
private var invalidateFooter: Boolean = false
private var invalidateStickyFooter: Boolean = false
/** Specify the foreground color for the insets */
var insetForeground: Drawable? = null
set(value) {
field = value
invalidateThis()
}
private var insets: Rect? = null
private val tempRect = Rect()
/** Fires when the insets get set */
var onInsetsCallback: ((WindowInsetsCompat) -> Unit)? = null
/** Specify if the statusbar should be tinted. */
var tintStatusBar = false
set(value) {
field = value
invalidateThis()
}
/** Specify if the navigationbar should be tinted. */
var tintNavigationBar = true
set(value) {
field = value
invalidateThis()
}
/** Specify if the systemUI should be visible. */
var systemUIVisible = true
set(value) {
field = value
invalidateThis()
}
/** Defines the current sticky footer selection */
internal var currentStickyFooterSelection = -1
/** Allow to specify a custom string key postfix for the savedInstanceKey (for multiple sliders). */
var savedInstanceKey: String = ""
/** Specify if the layoutManager for the recyclerView. */
var layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(context)
set(value) {
field = value
createContent()
}
/** Specify if the [com.mikepenz.fastadapter.IIdDistributor] for the [FastAdapter] */
val idDistributor: DefaultIdDistributor> = DefaultIdDistributorImpl()
/** Defines if we want an inner shadow (used in with the MiniDrawer). */
var innerShadow = false
set(value) {
field = value
createContent()
}
/** Defines the [AccountHeaderView] bound to this slider */
var accountHeader: AccountHeaderView? = null
set(value) {
field = value
if (field?.sliderView != this) {
field?.attachToSliderView(this)
}
}
/** Defines if the account header shall be displayed as a sticky header. */
var accountHeaderSticky = false
set(value) {
field = value
handleHeaderView()
}
/** Defines the [MiniDrawerSliderView] bound to this [MaterialDrawerSliderView] */
var miniDrawer: MiniDrawerSliderView? = null
set(value) {
field = value
if (field?.drawer != this) {
field?.drawer = this
}
}
/** Defines if the drawer should scroll to top after click. */
var scrollToTopAfterClick = false
/** Defines the header view to display in this [MaterialDrawerSliderView]. Note this is not possible when a [AccountHeaderView] is used. */
var headerView: View? = null
set(value) {
field = value
headerAdapter.clear()
if (value != null) {
if (headerPadding) {
headerAdapter.add(ContainerDrawerItem().withView(value).withDivider(headerDivider).withHeight(headerHeight).withViewPosition(ContainerDrawerItem.Position.TOP))
} else {
headerAdapter.add(ContainerDrawerItem().withView(value).withDivider(headerDivider).withHeight(headerHeight).withViewPosition(ContainerDrawerItem.Position.NONE))
}
//we need to set the padding so the header starts on top
recyclerView.setPadding(recyclerView.paddingLeft, 0, recyclerView.paddingRight, recyclerView.paddingBottom)
}
}
internal var _headerDivider = true
/** Defines if there should be a divider below the header. */
var headerDivider: Boolean
get() = _headerDivider
set(value) {
_headerDivider = value
headerView = headerView // udpate the header view
}
internal var _headerPadding = true
/** Defines the apdding for the header divider. */
var headerPadding: Boolean
get() = _headerPadding
set(value) {
_headerPadding = value
headerView = headerView // udpate the header view
}
/** Defines the height of the header. */
var headerHeight: DimenHolder? = null
set(value) {
field = value
handleHeaderView()
}
/** Defines the [View] to be displayed as sticky header. Note this is not possible if a sticky [AccountHeaderView] is uesed. */
var stickyHeaderView: View? = null
set(value) {
field = value
handleHeaderView()
}
/** Defines if a shadow shown on the top of the sticky header. */
var stickyHeaderShadow = true
set(value) {
field = value
handleHeaderView()
}
/** Defines the footer we want to display with the [MaterialDrawerSliderView]. */
var footerView: View? = null
set(value) {
field = value
// set the footer (do this before the setAdapter because some devices will crash else
if (value != null) {
if (footerDivider) {
footerAdapter.add(ContainerDrawerItem().apply { view = value; viewPosition = ContainerDrawerItem.Position.BOTTOM })
} else {
footerAdapter.add(ContainerDrawerItem().apply { view = value; viewPosition = ContainerDrawerItem.Position.NONE })
}
}
}
/** Defines if the footer should show with a divider. */
var footerDivider = true
set(value) {
field = value
footerView = footerView // udpate the footer view
}
private val footerClickListener = OnClickListener { v ->
val drawerItem = v.getTag(R.id.material_drawer_item) as IDrawerItem<*>
onFooterDrawerItemClick(this, drawerItem, v, true)
}
internal var _stickyFooterView: ViewGroup? = null
/** Defines the [ViewGroup] to display as sticky footer. */
val stickyFooterView: ViewGroup?
get() = _stickyFooterView
/** Defines if the sticky footer should be displayed with a divider. */
var stickyFooterDivider = false
set(value) {
field = value
handleStickyFooterView()
}
/** Defines the shadow [View] to provide for the sticky footer. */
var stickyFooterShadowView: View? = null
set(value) {
field = value
handleStickyFooterView()
}
/** Defines if the sticky footer should display a shadow above. */
var stickyFooterShadow = true
set(value) {
field = value
handleFooterView()
}
/** Defines if multi select is enabled in this drawer. */
var multiSelect
set(value) {
this.selectExtension.multiSelect = value
this.selectExtension.allowDeselection = value
}
get() = this.selectExtension.multiSelect
// item to select
private var _selectedItemPosition: Int = 0
/** Defines the currently selected item position. */
var selectedItemPosition: Int
get() = _selectedItemPosition
set(value) {
_selectedItemPosition = if (value == 0 && headerView != null) 1 else value
this.selectExtension.deselect()
this.selectExtension.select(_selectedItemPosition)
}
/** Defines the currently selected item identifier. Note this will not be updated if there are new items selected. */
var selectedItemIdentifier: Long = 0
set(value) {
field = value
selectedItemPosition = this.getPosition(selectedItemIdentifier)
}
// the _drawerLayout owning this slider
internal var _drawerLayout: DrawerLayout? = null
/** Defines the [DrawerLayout] bound to this [MaterialDrawerSliderView]. */
val drawerLayout: DrawerLayout?
get() = _drawerLayout
/** Defines if we want to use a custom width with this [MaterialDrawerSliderView]. */
var customWidth: Int? = null
set(value) {
field = value
onAttachedToWindow()
}
/** Defines the [RecyclerView] used in this [MaterialDrawerSliderView]. */
lateinit var recyclerView: RecyclerView
/** Defines if the adapter should enable hasStableIds to improve performance and allow animations. */
var hasStableIds = true
set(value) {
field = value
recyclerView.adapter = null // disconnect the RV
adapter.setHasStableIds(hasStableIds)
attachAdapter() // reattach the RV
}
// an adapter to use for the list
internal lateinit var _adapter: FastAdapter>
/** Defines the adapter for the header items */
var headerAdapter: ModelAdapter, IDrawerItem<*>> = ItemAdapter()
internal set
/** Defines the adapter for the normal items */
var itemAdapter: ModelAdapter, IDrawerItem<*>> = ItemAdapter()
internal set
/** Defines the adapter for the account items (if we switch the set of elements) */
var secondaryItemAdapter: ModelAdapter, IDrawerItem<*>> = ItemAdapter()
internal set
/** Defines the adapter for the footer items */
var footerAdapter: ModelAdapter, IDrawerItem<*>> = ItemAdapter()
internal set
lateinit var expandableExtension: ExpandableExtension>
lateinit var selectExtension: SelectExtension>
/**
* Defines the [FastAdapter] used to display elements in the [MaterialDrawerSliderView]
*/
var adapter: FastAdapter>
get() {
if (!::_adapter.isInitialized) {
secondaryItemAdapter.active = false
_adapter = FastAdapter.with(listOf(headerAdapter, itemAdapter, secondaryItemAdapter, footerAdapter))
_adapter.setHasStableIds(hasStableIds)
initAdapter()
this.selectExtension.isSelectable = true
this.selectExtension.multiSelect = false
this.selectExtension.allowDeselection = false
}
return _adapter
}
set(value) {
secondaryItemAdapter.active = false
_adapter = value
this.selectExtension = _adapter.getOrCreateExtension(SelectExtension::class.java)!! // is definitely not null
//we have to rewrap as a different FastAdapter was provided
_adapter.addAdapter(0, headerAdapter)
_adapter.addAdapter(1, itemAdapter)
_adapter.addAdapter(2, secondaryItemAdapter)
_adapter.addAdapter(3, footerAdapter)
initAdapter()
}
/** Defines an Adapter which wraps the main Adapter used in the RecyclerView to allow extended navigation and other stuff */
var adapterWrapper: RecyclerView.Adapter<*>? = null
set(value) {
if (!::_adapter.isInitialized) {
throw RuntimeException("this adapter has to be set in conjunction to a normal adapter which is used inside this wrapper adapter")
}
field = value
createContent()
}
/** defines the itemAnimator to be used in conjunction with the RecyclerView */
var itemAnimator: RecyclerView.ItemAnimator = DefaultItemAnimator()
set(value) {
field = value
createContent()
}
/** Defines if the drawer should be closed on a click */
var closeOnClick = true
/** Defines the delay before the drawer gets closed after a click. This is meant to prevent lag */
var delayOnDrawerClose = 50
/** delay drawer click event to prevent lag (you should either choose DelayOnDrawerClose or this) */
var delayDrawerClickEvent = 0
/** defines if we want to keep the sticky items visible, upon switching to the profiles */
var keepStickyItemsVisible = false
/** Defines the set of stickyDrawerItems to show */
var stickyDrawerItems: MutableList> = ArrayList()
/** Defines the click listener to listen for clicks on drawer items */
var onDrawerItemClickListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null
/** Defines the long click listener to listen for long clicks on drawer items */
var onDrawerItemLongClickListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null
//variables to store and remember the original list of the drawer
private var originalOnDrawerItemClickListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null
private var originalOnDrawerItemLongClickListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null
private var originalDrawerState: Bundle? = null
init {
val a = context.obtainStyledAttributes(attrs, R.styleable.MaterialDrawerSliderView, defStyleAttr, R.style.Widget_MaterialDrawerStyle)
insetForeground = a.getDrawable(R.styleable.MaterialDrawerSliderView_materialDrawerInsetForeground)
background = a.getDrawable(R.styleable.MaterialDrawerSliderView_materialDrawerBackground)
a.recycle()
setWillNotDraw(true) // No need to draw until the insets are adjusted
adapter // call getter to setup
createContent()
ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets ->
if (null == this.insets) {
this.insets = Rect()
}
this.insets?.set(insets.systemWindowInsetLeft, insets.systemWindowInsetTop, insets.systemWindowInsetRight, insets.systemWindowInsetBottom)
if (headerView == null && accountHeader == null) {
if (stickyHeaderView == null) {
recyclerView.updatePadding(top = insets.systemWindowInsetTop + context.resources.getDimensionPixelSize(R.dimen.material_drawer_padding_top_bottom))
}
}
if (stickyFooterView == null) {
recyclerView.updatePadding(bottom = insets.systemWindowInsetBottom + context.resources.getDimensionPixelSize(R.dimen.material_drawer_padding_top_bottom))
} else {
stickyFooterView?.updatePadding(bottom = insets.systemWindowInsetBottom + context.resources.getDimensionPixelSize(R.dimen.material_drawer_padding_top_bottom))
}
setWillNotDraw(insetForeground == null)
ViewCompat.postInvalidateOnAnimation(this@MaterialDrawerSliderView)
onInsetsCallback?.invoke(insets)
insets
}
}
override fun draw(canvas: Canvas) {
super.draw(canvas)
val width = width
val height = height
val insets = insets
val insetForeground = insetForeground
if (insets != null && insetForeground != null) {
val sc = canvas.save()
canvas.translate(scrollX.toFloat(), scrollY.toFloat())
if (!systemUIVisible) {
insets.top = 0
insets.right = 0
insets.bottom = 0
insets.left = 0
}
// Top
if (tintStatusBar) {
tempRect.set(0, 0, width, insets.top)
insetForeground.bounds = tempRect
insetForeground.draw(canvas)
}
// Bottom
if (tintNavigationBar) {
tempRect.set(0, height - insets.bottom, width, height)
insetForeground.bounds = tempRect
insetForeground.draw(canvas)
}
// Left
if (tintNavigationBar) {
tempRect.set(0, insets.top, insets.left, height - insets.bottom)
insetForeground.bounds = tempRect
insetForeground.draw(canvas)
}
// Right
if (tintNavigationBar) {
tempRect.set(width - insets.right, insets.top, width, height - insets.bottom)
insetForeground.bounds = tempRect
insetForeground.draw(canvas)
}
canvas.restoreToCount(sc)
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
insetForeground?.callback = this
if (parent != null) {
_drawerLayout = parent as? DrawerLayout ?: parent.parent as? DrawerLayout
?: parent.parent.parent as? DrawerLayout // give it 3 parents chance to find the parent
layoutParams?.also {
// if this is a drawer from the right, change the margins :D & set the new params
it.width = customWidth ?: getOptimalDrawerWidth(context)
layoutParams = it
}
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
insetForeground?.callback = null
}
/**
* Set the Bundle (savedInstance) which is passed by the activity.
* No need to null-check everything is handled automatically
*/
@Deprecated("Replaced with setSavedInstance", ReplaceWith("setSavedInstance(savedInstance)"))
fun withSavedInstance(savedInstance: Bundle?) {
setSavedInstance(savedInstance)
}
/**
* Set the Bundle (savedInstance) which is passed by the activity.
* No need to null-check everything is handled automatically
*/
fun setSavedInstance(savedInstance: Bundle?) {
savedInstance ?: return
// try to restore all saved values again
this.selectExtension.deselect()
adapter.withSavedInstanceState(savedInstance, BUNDLE_SELECTION + savedInstanceKey)
this.setStickyFooterSelection(savedInstance.getInt(BUNDLE_STICKY_FOOTER_SELECTION + savedInstanceKey, -1), null)
//toggle selection list if we were previously on the account list
if (savedInstance.getBoolean(BUNDLE_DRAWER_CONTENT_SWITCHED + savedInstanceKey, false)) {
accountHeader?.toggleSelectionList()
}
}
private fun initAdapter() {
ExtensionsFactories.register(SelectExtensionFactory())
ExtensionsFactories.register(ExpandableExtensionFactory())
this.selectExtension = adapter.getOrCreateExtension(SelectExtension::class.java)!! // is definitely not null
headerAdapter.idDistributor = idDistributor
itemAdapter.idDistributor = idDistributor
footerAdapter.idDistributor = idDistributor
expandableExtension = adapter.getOrCreateExtension(ExpandableExtension::class.java)!! // is definitely not null
}
/**
* the helper method to create the content for the drawer
*/
private fun createContent() {
if (!invalidationEnabled) {
invalidateContent = true
return
}
invalidateContent = false
// if we have an adapter (either by defining a custom one or the included one add a list :D
val contentView: View
if (!::recyclerView.isInitialized) {
contentView = LayoutInflater.from(context).inflate(R.layout.material_drawer_recycler_view, this, false)
recyclerView = contentView.findViewById(R.id.material_drawer_recycler_view)
//some style improvements on older devices
recyclerView.setFadingEdgeLength(0)
recyclerView.clipToPadding = false
} else {
contentView = recyclerView
}
//set the itemAnimator
recyclerView.itemAnimator = itemAnimator
//additional stuff
recyclerView.layoutManager = layoutManager
val params = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
params.weight = 1f
this.removeView(contentView) // ensure the view is not already part
this.addView(contentView, params)
if (innerShadow) {
var innerShadow = this.findViewById(R.id.material_drawer_inner_shadow)
if (innerShadow == null) {
innerShadow = LayoutInflater.from(context).inflate(R.layout.material_drawer_inner_shadow, this, false)!!
this.addView(innerShadow)
}
innerShadow.visibility = View.VISIBLE
innerShadow.bringToFront()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && gravity == GravityCompat.END) {
innerShadow.setBackgroundResource(R.drawable.material_drawer_shadow_right)
} else {
innerShadow.setBackgroundResource(R.drawable.material_drawer_shadow_left)
}
} else {
removeView(this.findViewById(R.id.material_drawer_inner_shadow))
}
//handle the header
handleHeaderView()
//handle the footer
handleFooterView()
//set the adapter on the listView
attachAdapter()
//predefine selection (should be the first element)
selectedItemPosition = _selectedItemPosition
// add the onDrawerItemClickListener if set
adapter.onClickListener = { v: View?, _: IAdapter>, item: IDrawerItem<*>, position: Int ->
if (item.isSelectable) {
resetStickyFooterSelection()
currentStickyFooterSelection = -1
}
//call the listener
var consumed = false
//call the item specific listener
if (item is AbstractDrawerItem<*, *>) {
consumed = item.onDrawerItemClickListener?.invoke(v, item, position) ?: false
}
//we have to notify the miniDrawer if existing, and if the event was not consumed yet
if (!consumed) {
consumed = miniDrawer?.onItemClick(item) ?: false
}
//call the drawer listener
onDrawerItemClickListener?.let { mOnDrawerItemClickListener ->
if (delayDrawerClickEvent > 0) {
Handler().postDelayed({ mOnDrawerItemClickListener.invoke(v, item, position) }, delayDrawerClickEvent.toLong())
} else {
consumed = mOnDrawerItemClickListener.invoke(v, item, position)
}
}
//if we were a expandable item we consume the event closing makes no sense
if (item.subItems.isNotEmpty()) {
//we consume the event and want no further handling
true
} else {
if (!consumed) {
//close the drawer after click
closeDrawerDelayed()
}
consumed
}
}
// add the onDrawerItemLongClickListener if set
adapter.onLongClickListener = { v: View, _: IAdapter>, item: IDrawerItem<*>, position: Int ->
onDrawerItemLongClickListener?.invoke(v, item, position) ?: false
}
recyclerView.scrollToPosition(0)
}
/**
* Attaches the adapter to the recyclerView
*/
private fun attachAdapter() {
//set the adapter on the listView
if (adapterWrapper == null) {
recyclerView.adapter = adapter
} else {
recyclerView.adapter = adapterWrapper
}
}
/**
* simple helper method to reset the selection of the sticky footer
*/
internal fun resetStickyFooterSelection() {
val stickyFooterView = stickyFooterView ?: return
if (stickyFooterView is LinearLayout) {
for (i in 0 until stickyFooterView.childCount) {
stickyFooterView.getChildAt(i).isActivated = false
stickyFooterView.getChildAt(i).isSelected = false
}
}
}
/**
* helper method to close the drawer delayed
*/
internal fun closeDrawerDelayed() {
if (closeOnClick && _drawerLayout != null) {
if (delayOnDrawerClose > -1) {
Handler().postDelayed({
_drawerLayout?.closeDrawers()
if (scrollToTopAfterClick) {
recyclerView.smoothScrollToPosition(0)
}
}, delayOnDrawerClose.toLong())
} else {
_drawerLayout?.closeDrawers()
}
}
}
/**
* information if the current drawer content is switched by alternative content (profileItems)
*/
fun switchedDrawerContent(): Boolean {
return !(originalOnDrawerItemClickListener == null && originalDrawerState == null)
}
/**
* method to switch the drawer content to new elements
*/
fun switchDrawerContent(
onDrawerItemClickListenerInner: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)?,
onDrawerItemLongClickListenerInner: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)?,
drawerItemsInner: List>,
drawerSelection: Int,
) {
//just allow a single switched drawer
if (!switchedDrawerContent()) {
//save out previous values
originalOnDrawerItemClickListener = onDrawerItemClickListener
originalOnDrawerItemLongClickListener = onDrawerItemLongClickListener
originalDrawerState = adapter.saveInstanceState(Bundle())
expandableExtension.collapse(false)
secondaryItemAdapter.active = true
itemAdapter.active = false
}
//set the new items
onDrawerItemClickListener = onDrawerItemClickListenerInner
onDrawerItemLongClickListener = onDrawerItemLongClickListenerInner
secondaryItemAdapter.set(drawerItemsInner)
setSelectionAtPosition(drawerSelection, false)
if (!keepStickyItemsVisible) {
//hide stickyFooter and it's shadow
stickyFooterView?.visibility = View.GONE
stickyFooterShadowView?.visibility = View.GONE
}
}
/**
* helper method to reset to the original drawerContent
*/
fun resetDrawerContent() {
if (switchedDrawerContent()) {
//set the new items
onDrawerItemClickListener = originalOnDrawerItemClickListener
onDrawerItemLongClickListener = originalOnDrawerItemLongClickListener
adapter.withSavedInstanceState(originalDrawerState)
//remove the references
originalOnDrawerItemClickListener = null
originalOnDrawerItemLongClickListener = null
originalDrawerState = null
secondaryItemAdapter.active = false
itemAdapter.active = true
//if we switch back scroll back to the top
recyclerView.smoothScrollToPosition(0)
//show the stickyFooter and it's shadow again
stickyFooterView?.visibility = View.VISIBLE
stickyFooterShadowView?.visibility = View.VISIBLE
//if we currently show the accountHeader selection list make sure to reset this attr
accountHeader?._selectionListShown = false
}
}
/**
* set the current selection in the drawer
* NOTE: this also deselects all other selections. if you do not want this. use the direct api of the adater .getAdapter().select(position, fireOnClick)
* NOTE: This will trigger onDrawerItemSelected without a view if you pass fireOnClick = true;
*/
@JvmOverloads
fun setSelectionAtPosition(position: Int, fireOnClick: Boolean = true): Boolean {
selectExtension.deselect()
if (position >= 0) {
selectExtension.select(position, false)
notifySelect(position, fireOnClick)
}
return false
}
private fun notifySelect(position: Int, fireOnClick: Boolean) {
_selectedItemPosition = position
if (fireOnClick && position >= 0) {
adapter.getItem(position)?.let { item ->
if (item is AbstractDrawerItem<*, *>) {
item.onDrawerItemClickListener?.invoke(null, item, position)
}
onDrawerItemClickListener?.invoke(null, item, position)
}
}
//we set the selection on a normal item in the drawer so we have to deselect the items in the StickyDrawer
resetStickyFooterSelection()
}
/**
* set the current selection in the drawer
* NOTE: This will trigger onDrawerItemSelected without a view if you pass fireOnClick = true;
*
* @param identifier the identifier to search for
* @param fireOnClick true if the click listener should be called
*/
@JvmOverloads
fun setSelection(identifier: Long, fireOnClick: Boolean = true) {
val select = adapter.getSelectExtension()
select.selectByIdentifier(identifier, false, true)
//we also have to call the general notify
val res = adapter.getItemById(identifier)
if (res != null) {
val position = res.second
notifySelect(position ?: -1, fireOnClick)
}
}
/**
* add the values to the bundle for saveInstanceState
*/
fun saveInstanceState(_savedInstanceState: Bundle): Bundle {
adapter.saveInstanceState(_savedInstanceState, BUNDLE_SELECTION + savedInstanceKey).apply {
putInt(BUNDLE_STICKY_FOOTER_SELECTION + savedInstanceKey, currentStickyFooterSelection)
putBoolean(BUNDLE_DRAWER_CONTENT_SWITCHED + savedInstanceKey, switchedDrawerContent())
}
return _savedInstanceState
}
/**
* Invalidates the `IconicsDrawable` if invalidation is currently enabled
*/
private fun invalidateThis() {
if (invalidationEnabled) {
invalidate()
}
}
/**
* Invalidates the header view in an optimized form
*/
private fun handleHeaderView() {
if (!invalidationEnabled) {
invalidateHeader = true
return
}
invalidateHeader = false
handleHeaderView(this)
}
/**
* Invalidates the footer view in an optimized form
*/
private fun handleFooterView() {
if (!invalidationEnabled) {
invalidateFooter = true
return
}
invalidateFooter = false
handleFooterView(this, footerClickListener)
}
/**
* Invalidates the sticky footer view in an optimized form
*/
internal fun handleStickyFooterView() {
if (!invalidationEnabled) {
invalidateStickyFooter = true
return
}
invalidateStickyFooter = false
rebuildStickyFooterView(this)
}
/** Applies properties in an optimized form. Will disable invalidation of the MaterialDrawerSliderView for the inner property set operations */
fun apply(block: MaterialDrawerSliderView.() -> Unit): MaterialDrawerSliderView {
invalidationEnabled = false
block()
invalidationEnabled = true
if (invalidateContent) {
createContent()
}
if (invalidateHeader) {
handleHeaderView()
}
if (invalidateFooter) {
handleFooterView()
}
if (invalidateStickyFooter) {
handleStickyFooterView()
}
invalidate()
return this
}
companion object {
/**
* BUNDLE param to store the selection
*/
const val BUNDLE_SELECTION = "_selection"
const val BUNDLE_STICKY_FOOTER_SELECTION = "bundle_sticky_footer_selection"
const val BUNDLE_DRAWER_CONTENT_SWITCHED = "bundle_drawer_content_switched"
/**
* Defines globally if we should animat selection state changes of the background
*/
var DEFAULT_SELECTED_BACKGROUND_ANIMATED = true
}
}
================================================
FILE: materialdrawer/src/main/java/com/mikepenz/materialdrawer/widget/MiniDrawerSliderView.kt
================================================
package com.mikepenz.materialdrawer.widget
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.core.view.ViewCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.IAdapter
import com.mikepenz.fastadapter.adapters.ItemAdapter
import com.mikepenz.fastadapter.select.SelectExtension
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.interfaces.ICrossfader
import com.mikepenz.materialdrawer.model.*
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.withEnabled
import com.mikepenz.materialdrawer.model.utils.hiddenInMiniDrawer
import com.mikepenz.materialdrawer.util.getDrawerItem
/**
* This view is a simple drop in view for the [DrawerLayout] or as companion to a [MaterialDrawerSliderView] offering a convenient API to provide a nice and flexible mini slider view following
* the material design guidelines v2.
*/
open class MiniDrawerSliderView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.materialDrawerStyle) : LinearLayout(context, attrs, defStyleAttr) {
/**
* get the RecyclerView of this MiniDrawer
*
* @return
*/
val recyclerView: RecyclerView
/**
* get the FastAdapter of this MiniDrawer
*
* @return
*/
val adapter: FastAdapter>
/**
* get the ItemAdapter of this MiniDrawer
*
* @return
*/
val itemAdapter: ItemAdapter>
val selectExtension: SelectExtension>
/**
* get the Drawer used to fill this MiniDrawer
*
* @return
*/
var drawer: MaterialDrawerSliderView? = null
set(value) {
field = value
if (field?.miniDrawer != this) {
field?.miniDrawer = this
}
createItems()
}
/**
* get the AccountHeader used to fill the this MiniDrawer
*
* @return
*/
val accountHeader: AccountHeaderView?
get() = drawer?.accountHeader
/**
* get the Crossfader used for this MiniDrawer
*
* @return
*/
var crossFader: ICrossfader? = null
var innerShadow = false
set(value) {
field = value
updateInnerShadow()
}
var inRTL = false
set(value) {
field = value
updateInnerShadow()
}
var includeSecondaryDrawerItems = false
set(value) {
field = value
createItems()
}
var enableSelectedMiniDrawerItemBackground = false
set(value) {
field = value
createItems()
}
var enableProfileClick = true
set(value) {
field = value
createItems()
}
private var onMiniDrawerItemClickListener: ((view: View?, position: Int, drawerItem: IDrawerItem<*>, type: Int) -> Boolean)? = null
private var onMiniDrawerItemOnClickListener: ((v: View?, adapter: IAdapter>, item: IDrawerItem<*>, position: Int) -> Boolean)? = null
set(value) {
field = value
if (value == null) {
createItems()
} else {
adapter.onClickListener = value
}
}
private var onMiniDrawerItemLongClickListener: ((v: View, adapter: IAdapter>, item: IDrawerItem<*>, position: Int) -> Boolean)? = null
set(value) {
field = value
adapter.onLongClickListener = value
}
/**
* returns always the original drawerItems and not the switched content
*
* @return
*/
private val drawerItems: List>
get() = drawer?.itemAdapter?.adapterItems ?: ArrayList()
init {
val a = context.obtainStyledAttributes(attrs, R.styleable.MaterialDrawerSliderView, defStyleAttr, R.style.Widget_MaterialDrawerStyle)
background = a.getDrawable(R.styleable.MaterialDrawerSliderView_materialDrawerBackground)
a.recycle()
updateInnerShadow()
//create and append recyclerView
recyclerView = RecyclerView(context)
addView(recyclerView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
//set the itemAnimator
recyclerView.itemAnimator = DefaultItemAnimator()
//some style improvements on older devices
recyclerView.setFadingEdgeLength(0)
//set the drawing cache background to the same color as the slider to improve performance
recyclerView.clipToPadding = false
//additional stuff
recyclerView.layoutManager = LinearLayoutManager(context)
//adapter
itemAdapter = ItemAdapter()
adapter = FastAdapter.with(itemAdapter)
selectExtension = adapter.getOrCreateExtension(SelectExtension::class.java)!! // definitely not null
selectExtension.isSelectable = true
selectExtension.allowDeselection = false
recyclerView.adapter = adapter
// set the insets
ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets ->
recyclerView.setPadding(recyclerView.paddingLeft, insets.systemWindowInsetTop, recyclerView.paddingRight, insets.systemWindowInsetBottom)
insets
}
//set the adapter with the items
createItems()
}
private fun updateInnerShadow() {
if (innerShadow) {
if (!inRTL) {
setBackgroundResource(R.drawable.material_drawer_shadow_left)
} else {
setBackgroundResource(R.drawable.material_drawer_shadow_right)
}
} else {
background = null
}
}
/**
* call this method to trigger the onProfileClick on the MiniDrawer
*/
fun onProfileClick() {
//crossfade if we are cross faded
crossFader?.let {
if (it.isCrossfaded) {
it.crossfade()
}
}
//update the current profile
accountHeader?.let {
val profile = it.activeProfile
if (profile is IDrawerItem<*>) {
generateMiniDrawerItem(profile as IDrawerItem<*>)?.let { item ->
itemAdapter.set(0, item)
}
}
}
}
/**
* call this method to trigger the onItemClick on the MiniDrawer
*
* @param selectedDrawerItem
* @return
*/
fun onItemClick(selectedDrawerItem: IDrawerItem<*>): Boolean {
//We only need to clear if the new item is selectable
return if (selectedDrawerItem.isSelectable) {
// crossfade if we are cross faded
crossFader?.let {
if (drawer?.closeOnClick == true && it.isCrossfaded) {
it.crossfade()
}
}
// update everything
if (selectedDrawerItem.hiddenInMiniDrawer) {
selectExtension.deselect()
} else {
setSelection(selectedDrawerItem.identifier)
}
false
} else {
true
}
}
/**
* set the selection of the MiniDrawer
*
* @param identifier the identifier of the item which should be selected (-1 for none)
*/
fun setSelection(identifier: Long) {
if (identifier == -1L) {
selectExtension.deselect()
return
}
val count = adapter.itemCount
for (i in 0 until count) {
val item = adapter.getItem(i)
if (item?.identifier == identifier && !item.isSelected) {
selectExtension.deselect()
selectExtension.select(i)
}
}
}
/**
* update a MiniDrawerItem (after updating the main Drawer) via its identifier
*
* @param identifier the identifier of the item which was updated
*/
fun updateItem(identifier: Long) {
if (drawer != null && identifier != -1L) {
drawerItems.getDrawerItem(identifier)?.let { drawerItem ->
for (i in 0 until itemAdapter.adapterItems.size) {
if (itemAdapter.adapterItems[i].identifier == drawerItem.identifier) {
val miniDrawerItem = generateMiniDrawerItem(drawerItem)
if (miniDrawerItem != null) {
itemAdapter[i] = miniDrawerItem
}
}
}
}
}
}
/**
* creates the items for the MiniDrawer
*/
open fun createItems() {
itemAdapter.clear()
var profileOffset = 0
accountHeader?.let { accountHeader ->
val profile = accountHeader.activeProfile
if (profile is IDrawerItem<*>) {
generateMiniDrawerItem(profile as IDrawerItem<*>)?.let {
itemAdapter.add(it)
}
profileOffset = 1
}
}
var select = -1
if (drawer != null) {
//migrate to miniDrawerItems
val length = drawerItems.size
var position = 0
for (i in 0 until length) {
val miniDrawerItem = generateMiniDrawerItem(drawerItems[i])
if (miniDrawerItem != null) {
if (miniDrawerItem.isSelected) {
select = position
}
itemAdapter.add(miniDrawerItem)
position += 1
}
}
if (select >= 0) {
//+1 because of the profile
selectExtension.select(select + profileOffset)
}
}
//listener
if (this.onMiniDrawerItemOnClickListener != null) {
adapter.onClickListener = this.onMiniDrawerItemOnClickListener
} else {
adapter.onClickListener = { v: View?, _: IAdapter>, item: IDrawerItem<*>, position: Int ->
val type = getMiniDrawerType(item)
//if a listener is defined and we consume the event return
if (onMiniDrawerItemClickListener?.invoke(v, position, item, type) == true) {
false
} else {
if (type == ITEM) {
//fire the onClickListener also if the specific drawerItem is not Selectable
if (item.isSelectable) {
//make sure we are on the original drawerItemList
accountHeader?.let {
if (it.selectionListShown) {
it.toggleSelectionList()
}
}
val drawerItem = drawer?.getDrawerItem(item.identifier)
if (drawerItem != null && !drawerItem.isSelected) {
//set the selection
drawer?.selectExtension?.deselect()
drawer?.setSelection(item.identifier, true)
}
} else if (drawer?.onDrawerItemClickListener != null) {
drawerItems.getDrawerItem(item.identifier)?.let {
//get the original `DrawerItem` from the Drawer as this one will contain all information
drawer?.onDrawerItemClickListener?.invoke(v, it, position)
}
}
} else if (type == PROFILE) {
accountHeader?.let {
if (!it.selectionListShown) {
it.toggleSelectionList()
}
}
crossFader?.crossfade()
}
false
}
}
}
adapter.onLongClickListener = onMiniDrawerItemLongClickListener
recyclerView.scrollToPosition(0)
}
/**
* generates a MiniDrawerItem from a IDrawerItem
*
* @param drawerItem
* @return
*/
open fun generateMiniDrawerItem(drawerItem: IDrawerItem<*>): IDrawerItem<*>? {
return when (drawerItem) {
is SecondaryDrawerItem -> if (includeSecondaryDrawerItems && !drawerItem.hiddenInMiniDrawer) MiniDrawerItem(drawerItem).withEnableSelectedBackground(enableSelectedMiniDrawerItemBackground).withSelectedBackgroundAnimated(false) else null
is PrimaryDrawerItem -> if (!drawerItem.hiddenInMiniDrawer) MiniDrawerItem(drawerItem).withEnableSelectedBackground(enableSelectedMiniDrawerItemBackground).withSelectedBackgroundAnimated(false) else null
is ProfileDrawerItem -> MiniProfileDrawerItem(drawerItem).apply { withEnabled(enableProfileClick) }
else -> null
}
}
/**
* gets the type of a IDrawerItem
*
* @param drawerItem
* @return
*/
open fun getMiniDrawerType(drawerItem: IDrawerItem<*>): Int {
if (drawerItem is MiniProfileDrawerItem) {
return PROFILE
} else if (drawerItem is MiniDrawerItem) {
return ITEM
}
return -1
}
companion object {
val PROFILE = 1
val ITEM = 2
}
}
================================================
FILE: materialdrawer/src/main/res/color/color_drawer_item_text.xml
================================================
================================================
FILE: materialdrawer/src/main/res/drawable/material_drawer_badge.xml
================================================
================================================
FILE: materialdrawer/src/main/res/drawable/material_drawer_circle_mask.xml
================================================
================================================
FILE: materialdrawer/src/main/res/drawable/material_drawer_ico_account_layer.xml
================================================
-
================================================
FILE: materialdrawer/src/main/res/drawable/material_drawer_ico_chevron_down.xml
================================================
================================================
FILE: materialdrawer/src/main/res/drawable/material_drawer_ico_menu_down.xml
================================================
================================================
FILE: materialdrawer/src/main/res/drawable/material_drawer_rectangle_mask.xml
================================================
================================================
FILE: materialdrawer/src/main/res/drawable/material_drawer_shadow_bottom.xml
================================================
================================================
FILE: materialdrawer/src/main/res/drawable/material_drawer_shadow_top.xml
================================================
================================================
FILE: materialdrawer/src/main/res/drawable-v21/material_drawer_ico_account.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_compact_header.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_fits_not.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_header.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_inner_shadow.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_container.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_divider.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_expandable.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_expandable_badge.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_mini.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_mini_profile.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_primary.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_profile.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_profile_setting.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_secondary.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_secondary_switch.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_secondary_toggle.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_section.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_switch.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_item_toggle.xml
================================================
================================================
FILE: materialdrawer/src/main/res/layout/material_drawer_recycler_view.xml
================================================
================================================
FILE: materialdrawer/src/main/res/values/attrs.xml
================================================
================================================
FILE: materialdrawer/src/main/res/values/colors.xml
================================================
#1F000000
#1F2196F3
================================================
FILE: materialdrawer/src/main/res/values/dimens.xml
================================================
320dp
0dp
8dp
8dp
12dp
28dp
1dp
4dp
1dp
4dp
160dp
72dp
16dp
4dp
56dp
40dp
56dp
20sp
14sp
80dp
22dp
1dp
18dp
56dp
2dp
2dp
4dp
8dp
28dp
12dp
@dimen/material_drawer_item_background_padding_start_end
@dimen/material_drawer_item_background_padding_start_end
0dp
56dp
52dp
16dp
16dp
12dp
14sp
14sp
12sp
48dp
52dp
12dp
18dp
14dp
14sp
12sp
56dp
8dp
40dp
16dp
6dp
6dp
16dp
14sp
14sp
24dp
72dp
4dp
8dp
56dp
16dp
40dp
16dp
12sp
10sp
14sp
56dp
24dp
================================================
FILE: materialdrawer/src/main/res/values/ids.xml
================================================
================================================
FILE: materialdrawer/src/main/res/values/strings.xml
================================================
Open
Close
Profile icon
================================================
FILE: materialdrawer/src/main/res/values/styles.xml
================================================
================================================
FILE: materialdrawer/src/main/res/values-fr/strings.xml
================================================
Ouvrir
Fermer
Profile icon
================================================
FILE: materialdrawer/src/main/res/values-pt/strings.xml
================================================
Abrir
Fechar
Profile icon
================================================
FILE: materialdrawer/src/main/res/values-sw600dp/dimens.xml
================================================
24dp
================================================
FILE: materialdrawer-iconics/.gitignore
================================================
/build
================================================
FILE: materialdrawer-iconics/build.gradle.kts
================================================
plugins {
id("com.mikepenz.convention.android-library")
id("com.mikepenz.convention.kotlin")
id("com.mikepenz.convention.publishing")
}
android {
namespace = "com.mikepenz.materialdrawer.iconics"
}
dependencies {
implementation(project(":materialdrawer"))
// used to provide out of the box icon font support. simplifies development,
// and provides scalable icons. the core is very very light
// https://github.com/mikepenz/Android-Iconics
implementation(libs.iconics.core)
}
================================================
FILE: materialdrawer-iconics/gradle.properties
================================================
POM_NAME=MaterialDrawer Iconics-Extension
POM_DESCRIPTION=Extension for the MaterialDrawer library to add Android-Iconics support.
POM_ARTIFACT_ID=materialdrawer-iconics
POM_PACKAGING=aar
================================================
FILE: materialdrawer-iconics/src/main/AndroidManifest.xml
================================================
================================================
FILE: materialdrawer-iconics/src/main/java/com/mikepenz/materialdrawer/iconics/IconicsExtension.kt
================================================
package com.mikepenz.materialdrawer.iconics
import android.os.Build
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.materialdrawer.model.BaseDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.Iconable
import com.mikepenz.materialdrawer.model.interfaces.withIconTintingEnabled
var > T.iconicsIcon: IIcon
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
this.iconicsIconHolder = IconicsImageHolder(value)
}
var > T.iconicsIconHolder: IconicsImageHolder?
get() = icon as? IconicsImageHolder
set(value) {
this.icon = value
//if we are on api 21 or higher we use the IconicsDrawable for selection too and color it with the correct color
//else we use just the one drawable and enable tinting on press
if (Build.VERSION.SDK_INT >= 21) {
this.selectedIcon = value
} else if (value != null) {
this.withIconTintingEnabled(true)
}
}
@Deprecated("Please consider to replace with the actual property setter")
fun > T.withIcon(icon: IIcon): T {
this.iconicsIconHolder = IconicsImageHolder(icon)
return this
}
var T.iconicsIcon: IIcon
@Deprecated(level = DeprecationLevel.ERROR, message = "Not readable")
get() = throw UnsupportedOperationException("Please use the direct property")
set(value) {
this.icon = IconicsImageHolder(value)
}
@Deprecated("Please consider to replace with the actual property setter")
fun T.withIcon(icon: IIcon): T {
this.icon = IconicsImageHolder(icon)
return this
}
================================================
FILE: materialdrawer-iconics/src/main/java/com/mikepenz/materialdrawer/iconics/IconicsImageHolder.kt
================================================
package com.mikepenz.materialdrawer.iconics
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.widget.ImageView
import androidx.appcompat.content.res.AppCompatResources
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.iconics.utils.actionBar
import com.mikepenz.iconics.utils.paddingDp
import com.mikepenz.iconics.utils.sizeDp
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.util.DrawerImageLoader
import java.io.FileNotFoundException
class IconicsImageHolder(iicon: IIcon) : ImageHolder() {
var iicon: IIcon? = iicon
internal set
/**
* sets an existing image to the imageView
*
* @param imageView
* @param tag used to identify imageViews and define different placeholders
* @return true if an image was set
*/
override fun applyTo(imageView: ImageView, tag: String?): Boolean {
val ii = iicon
val uri = uri
if (uri != null) {
val consumed = DrawerImageLoader.instance.setImage(imageView, uri, tag)
if (!consumed) {
imageView.setImageURI(uri)
}
} else if (icon != null) {
imageView.setImageDrawable(icon)
} else if (bitmap != null) {
imageView.setImageBitmap(bitmap)
} else if (iconRes != -1) {
imageView.setImageResource(iconRes)
} else if (ii != null) {
imageView.setImageDrawable(IconicsDrawable(imageView.context, ii).actionBar())
} else {
imageView.setImageBitmap(null)
return false
}
return true
}
/**
* this only handles Drawables
*
* @param ctx
* @param iconColor
* @param tint
* @return
*/
override fun decideIcon(ctx: Context, iconColor: ColorStateList, tint: Boolean, paddingDp: Int): Drawable? {
var icon: Drawable? = icon
val ii = iicon
val uri = uri
when {
ii != null -> icon = IconicsDrawable(ctx, ii).apply {
colorList = iconColor
sizeDp = 24
this.paddingDp = paddingDp
}
iconRes != -1 -> icon = AppCompatResources.getDrawable(ctx, iconRes)
uri != null -> try {
val inputStream = ctx.contentResolver.openInputStream(uri)
icon = Drawable.createFromStream(inputStream, uri.toString())
} catch (e: FileNotFoundException) {
//no need to handle this
}
}
//if we got an icon AND we have auto tinting enabled AND it is no IIcon, tint it ;)
if (icon != null && tint && iicon == null) {
icon = icon.mutate()
icon.setColorFilter(iconColor.defaultColor, PorterDuff.Mode.SRC_IN)
}
return icon
}
}
================================================
FILE: materialdrawer-nav/.gitignore
================================================
/build
================================================
FILE: materialdrawer-nav/build.gradle.kts
================================================
plugins {
id("com.mikepenz.convention.android-library")
id("com.mikepenz.convention.kotlin")
id("com.mikepenz.convention.publishing")
id("androidx.navigation.safeargs.kotlin")
}
android {
namespace = "com.mikepenz.materialdrawer.nav"
}
dependencies {
implementation(project(":materialdrawer"))
implementation(libs.androidx.navigation.runtime)
implementation(libs.androidx.navigation.ui)
}
================================================
FILE: materialdrawer-nav/consumer-rules.pro
================================================
================================================
FILE: materialdrawer-nav/gradle.properties
================================================
POM_NAME=MaterialDrawer NavController-Extension
POM_DESCRIPTION=Extension for the MaterialDrawer library to add NavController support.
POM_ARTIFACT_ID=materialdrawer-nav
POM_PACKAGING=aar
================================================
FILE: materialdrawer-nav/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: materialdrawer-nav/proguard-rules.txt
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Entwicklung/android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: materialdrawer-nav/src/main/AndroidManifest.xml
================================================
================================================
FILE: materialdrawer-nav/src/main/java/com/mikepenz/materialdrawer/model/NavigationDrawerItem.kt
================================================
package com.mikepenz.materialdrawer.model
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.CallSuper
import androidx.annotation.IdRes
import androidx.navigation.NavOptions
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.R
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
class NavigationDrawerItem(
@IdRes val resId: Int,
val item: IDrawerItem,
val args: Bundle? = null,
val options: NavOptions? = defaultOptions
) : IDrawerItem by item {
init {
item.identifier = resId.toLong()
}
/**
* generates a view by the defined LayoutRes
*/
override fun generateView(ctx: Context): View {
val viewHolder = getViewHolder(LayoutInflater.from(ctx).inflate(layoutRes, null, false) as ViewGroup)
bindView(viewHolder, mutableListOf())
return viewHolder.itemView
}
/**
* generates a view by the defined LayoutRes and pass the LayoutParams from the parent
*/
override fun generateView(ctx: Context, parent: ViewGroup): View {
val viewHolder = getViewHolder(LayoutInflater.from(ctx).inflate(layoutRes, parent, false) as ViewGroup)
bindView(viewHolder, mutableListOf())
return viewHolder.itemView
}
@CallSuper
override fun bindView(holder: VH, payloads: List) {
item.bindView(holder, payloads)
holder.itemView.setTag(R.id.material_drawer_item, this)
}
companion object {
val defaultOptions = NavOptions.Builder()
.setLaunchSingleTop(true)
.setEnterAnim(androidx.navigation.ui.R.anim.nav_default_enter_anim)
.setExitAnim(androidx.navigation.ui.R.anim.nav_default_exit_anim)
.setPopEnterAnim(androidx.navigation.ui.R.anim.nav_default_pop_enter_anim)
.setPopExitAnim(androidx.navigation.ui.R.anim.nav_default_pop_exit_anim)
.build()
}
}
================================================
FILE: materialdrawer-nav/src/main/java/com/mikepenz/materialdrawer/util/DrawerNavigationUI.kt
================================================
package com.mikepenz.materialdrawer.util
import android.os.Bundle
import android.view.View
import androidx.annotation.IdRes
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import com.mikepenz.materialdrawer.model.NavigationDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
import java.lang.ref.WeakReference
/**
* Sets up a {@link Drawer} for use with a {@link NavController}.
* The selected item in the Drawer will automatically be updated when the destination
* changes.
*
* @param navController The NavController that hosts the destination
* @param fallBackListener the listener to handle no navigationDrawerItems
* @return
*/
@Deprecated("Added new successListener", ReplaceWith("setupWithNavController(navController, null, fallBackListener)"))
fun MaterialDrawerSliderView.setupWithNavController(
navController: NavController,
fallBackListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null,
) {
setupWithNavController(navController, null, fallBackListener)
}
/**
* Sets up a {@link Drawer} for use with a {@link NavController}.
* The selected item in the Drawer will automatically be updated when the destination
* changes.
*
* @param navController The NavController that hosts the destination
* @param successListener listener to retrieve a notification on successful navigation
* @param fallBackListener the listener to handle no navigationDrawerItems
* @return
*/
fun MaterialDrawerSliderView.setupWithNavController(
navController: NavController,
successListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null,
fallBackListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null,
) {
DrawerNavigationUI.setupWithNavController(this, navController, successListener, fallBackListener)
}
/**
* Created by petretiandrea on 19.07.19.
*/
object DrawerNavigationUI {
/**
* Sets up a {@link Drawer} for use with a {@link NavController}. It allow to navigate
* to a destination when an item {@link NavigationDrawerItem} of drawer is selected.
* The selected item in the Drawer will automatically be updated when the destination
* changes.
*
* It automatically close the Drawer.
*
* @param drawer The drawer that should be kept in sync with changes to the NavController.
* @param navController The NavController that allow to perform the navigation actions, relying on the item selected in the Drawer
* @param fallBackListener A listener called when perform navigation fails
* @return
*/
fun setupWithNavController(
drawer: MaterialDrawerSliderView,
navController: NavController,
successListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null,
fallBackListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null
) {
drawer.onDrawerItemClickListener = { v, item, position ->
val success = performNavigation(item, navController)
if (success) {
successListener?.invoke(v, item, position)
drawer.drawerLayout?.closeDrawer(drawer)
} else {
fallBackListener?.invoke(v, item, position)
}
success
}
val weakReference: WeakReference = WeakReference(drawer)
navController.addOnDestinationChangedListener(object : NavController.OnDestinationChangedListener {
override fun onDestinationChanged(controller: NavController, destination: NavDestination, arguments: Bundle?) {
val drawerWeak: MaterialDrawerSliderView? = weakReference.get()
if (drawerWeak == null) {
navController.removeOnDestinationChangedListener(this)
}
fun Iterable>.handleSelection(): Boolean {
var matched = false
for (element in this) {
// A ResId may refers to 3 intents.
val destinationId = controller.graph.getAction(element.resId)?.let { action ->
if (action.destinationId != 0) action.destinationId // an action navigate to a destination
else action.navOptions?.popUpToId // an action pop to a destination
} ?: element.resId // a destination
if (matchDestination(destination, destinationId)) {
drawerWeak?.selectExtension?.deselect()
drawerWeak?.setSelection(element.identifier, false)
drawerWeak?.getStickyFooterPosition(element)?.let {
drawerWeak.setStickyFooterSelection(it, false)
}
matched = true
}
}
return matched
}
if (drawerWeak?.itemAdapter?.adapterItems?.filterIsInstance>()?.handleSelection() != true) {
// if we did not match the normal items, also check the footer
if (drawerWeak?.footerAdapter?.adapterItems?.filterIsInstance>()?.handleSelection() != true) {
// if footer also did not match, go to sticky
drawerWeak?.stickyDrawerItems?.filterIsInstance>()?.handleSelection()
}
}
}
})
}
/***
* Try to perform a navigation using the NavController to destination associated to IDrawerItem.
*
* Importantly, it assumes that the item type's is {@link NavigationDrawerItem} and that
* the resId matches a valid {@link NavDestination#getAction(int) action id} or {@link NavDestination#getId() destination id}
* to be navigated to.
*
* @param item The selected drawer item
* @param navController The NavController that hosts the destination.
* @return True if the navigation is performed, False otherwise.
*/
private fun performNavigation(item: IDrawerItem<*>, navController: NavController): Boolean {
return when (item) {
is NavigationDrawerItem -> {
try {
navController.navigate(item.resId, item.args, item.options)
true
} catch (e: IllegalArgumentException) {
false
}
}
else -> false
}
}
private fun matchDestination(destination: NavDestination, @IdRes destId: Int): Boolean {
var currentDestination = destination
while (currentDestination.id != destId && currentDestination.parent != null) {
currentDestination = currentDestination.parent!!
}
return currentDestination.id == destId
}
}
================================================
FILE: settings.gradle.kts
================================================
rootProject.name = "MaterialDrawer"
// enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
pluginManagement {
repositories {
google()
gradlePluginPortal()
mavenCentral()
mavenLocal()
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
mavenLocal()
}
versionCatalogs {
create("baseLibs") {
from("com.mikepenz:version-catalog:0.1.3")
}
}
}
include(":app")
include(":materialdrawer")
include(":materialdrawer-iconics")
include(":materialdrawer-nav")