Skip to content

Commit

Permalink
Merge pull request #20 from Semper-Viventem/release
Browse files Browse the repository at this point in the history
Release 0.1.6
  • Loading branch information
Semper-Viventem committed Feb 10, 2019
2 parents 5c36e50 + 1b474e5 commit 3841ed5
Show file tree
Hide file tree
Showing 23 changed files with 201 additions and 154 deletions.
58 changes: 30 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,12 @@ This library makes it easy to implement a [Backdrop](https://material.io/design/

## Download
**JCenter (Recommended):**

*For support library:*
```groovy
dependencies {
implementation 'ru.semper-viventem.backdrop:backdrop:0.1.5'
}
```
*For Android X:*
```groovy
dependencies {
implementation 'ru.semper-viventem.backdrop:backdrop:0.1.5_x'
implementation 'ru.semper-viventem.backdrop:backdrop:0.1.6'
}
```

**JitPack:**
```groovy
repositories {
Expand All @@ -36,34 +29,41 @@ repositories {
}
dependencies {
implementation 'com.github.Semper-Viventem:BackdropView:0.1.5'
implementation 'com.github.Semper-Viventem:BackdropView:0.1.6'
}
```

## How to use it?
You need to add a layout Toolbar, back container and foreground container
You need to add front layout and back layout (with toolbar) to CoordinatorLayout.

Add BackdropBehavior to the Foreground View Container:
Add BackdropBehavior to your front layout:

**XML**
```xml
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

<LinearLayout
android:id="@+id/backContainer"

<!-- BackLayout for BackDrop -->
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/backLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<!-- anything -->
</LinearLayout>

<!-- Must contain a Toolbar -->
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

<!-- For example, NavigationView. Or you can use anything -->
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/main_menu"/>
</com.google.android.material.appbar.AppBarLayout>

<!-- Add BackdropBehavior to this view -->
<android.support.design.card.MaterialCardView
Expand All @@ -72,7 +72,7 @@ Add BackdropBehavior to the Foreground View Container:
android:layout_width="match_parent"
android:layout_height="match_parent">

<!-- anything -->
<!-- Anything -->
</android.support.design.card.MaterialCardView>

</android.support.design.widget.CoordinatorLayout>
Expand All @@ -97,14 +97,16 @@ fun <T : CoordinatorLayout.Behavior<*>> View.findBehavior(): T = layoutParams.ru
val backdropBehavior: BackdropBehavior = foregroundContainer.findBehavior() // find behavior

with(backdropBehavior) {
attachBackContainer(R.id.backContainer) // set back container
attachToolbar(R.id.toolbar) // set toolbar

// Attach your back layout to behavior.
// BackDropBehavior will find the toolbar itself.
attachBackLayout(R.id.backLayout)

// set navigation icons for toolbar
// Set navigation icons for toolbar
setClosedIcon(R.drawable.ic_menu)
setOpenedIcon(R.drawable.ic_close)

// add listener
// Add listener
addOnDropListener(object : BackdropBehavior.OnDropListener {
override fun onDrop(dropState: BackdropBehavior.DropState, fromUser: Boolean) {
// TODO: handle listener
Expand Down
8 changes: 4 additions & 4 deletions backdrop/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ ext {
siteUrl = 'https://github.com/Semper-Viventem/BackdropView'
gitUrl = 'https://github.com/Semper-Viventem/BackdropView'

libraryVersion = '0.1.5'
libraryVersion = '0.1.6'

developerId = 'semper-viventem'
developerName = 'constantine'
Expand Down Expand Up @@ -57,14 +57,14 @@ android {
}

ext {
supportVersion = '28.0.0-rc01'
kotlin_version = '1.2.50'
kotlin_version = '1.3.20'
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation "com.android.support:appcompat-v7:$supportVersion"
// Android X
implementation "androidx.appcompat:appcompat:1.1.0-alpha02"

// Kotlin
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package ru.semper_viventem.backdrop
import android.content.Context
import android.os.Bundle
import android.os.Parcelable
import android.support.annotation.IdRes
import android.support.design.widget.CoordinatorLayout
import android.support.v7.widget.Toolbar
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import androidx.annotation.IdRes
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout


class BackdropBehavior : CoordinatorLayout.Behavior<View> {
Expand All @@ -30,18 +31,22 @@ class BackdropBehavior : CoordinatorLayout.Behavior<View> {
private const val ARG_DROP_STATE = "arg_drop_state"
}

private var utils = BackdropUtils()

private var toolbarId: Int? = null
private var backContainerId: Int? = null
private var backLayoutId: Int? = null

private var child: View? = null
private var toolbar: Toolbar? = null
private var backContainer: View? = null
private var backLayout: ViewGroup? = null
private var frontLayout: View? = null

private var closedIconId: Int = R.drawable.ic_menu
private var openedIconRes: Int = R.drawable.ic_close

private var dropState: DropState = DEFAULT_DROP_STATE

private var needToInitializing = true

private var dropListeners = mutableListOf<OnDropListener>()

constructor() : super()
Expand All @@ -61,25 +66,35 @@ class BackdropBehavior : CoordinatorLayout.Behavior<View> {
}

override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
if (toolbarId == null || backContainerId == null) return false
if (toolbarId == null && backLayoutId == null) return false

return when (dependency.id) {
toolbarId -> true
backContainerId -> true
backLayoutId -> true
else -> false
}
}

override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {

this.child = child
this.frontLayout = child as? ViewGroup ?: throw IllegalArgumentException("BackLayout must extend a ViewGroup")

when (dependency.id) {
toolbarId -> toolbar = dependency as Toolbar
backContainerId -> backContainer = dependency
toolbarId -> toolbar = dependency as? Toolbar ?: throw IllegalArgumentException("toolbarId doesn't match Toolbar")

backLayoutId -> {
backLayout = dependency as? ViewGroup ?: throw IllegalArgumentException("backLayoutId doesn't match back Layout")

// TODO (next release): remove this conditional
if (toolbarId == null) {
toolbar = utils.findToolbar(backLayout!!)
?: throw IllegalArgumentException("AppBarLayout mast contain a Toolbar!")
}
}
}

if (toolbar != null && backContainer != null) {
initViews(parent, child, toolbar!!, backContainer!!)
if (toolbar != null && frontLayout != null && backLayout != null && needToInitializing) {
initViews(parent, frontLayout!!, toolbar!!, backLayout!!)
}

return super.onDependentViewChanged(parent, child, dependency)
Expand All @@ -93,12 +108,28 @@ class BackdropBehavior : CoordinatorLayout.Behavior<View> {
this.closedIconId = iconRes
}

/**
* Attach back layout to Backdrop.
* BackDropLayout must contain a [Toolbar]
*/
fun attachBackLayout(@IdRes appBarLayoutId: Int) {
this.backLayoutId = appBarLayoutId
}

/**
* @deprecated — use [BackdropBehavior.attachBackLayout]. This method will be removed in version 0.1.7+
*/
@Deprecated("Use BackdropBehavior.attachBackLayout")
fun attachToolbar(@IdRes toolbarId: Int) {
this.toolbarId = toolbarId
}

/**
* @deprecated — use [BackdropBehavior.attachBackLayout]. This method will be removed in version 0.1.7+
*/
@Deprecated("Use BackdropBehavior.attachBackLayout")
fun attachBackContainer(@IdRes backContainerId: Int) {
this.backContainerId = backContainerId
this.backLayoutId = backContainerId
}

fun addOnDropListener(listener: OnDropListener) {
Expand All @@ -113,8 +144,8 @@ class BackdropBehavior : CoordinatorLayout.Behavior<View> {
false
} else {
dropState = DropState.OPEN
if (child != null && toolbar != null && backContainer != null) {
drawDropState(child!!, toolbar!!, backContainer!!, withAnimation)
if (backLayout != null && toolbar != null && frontLayout != null) {
drawDropState(frontLayout!!, toolbar!!, backLayout!!, withAnimation)
} else {
throw IllegalArgumentException("Toolbar and backContainer must be initialized")
}
Expand All @@ -126,57 +157,77 @@ class BackdropBehavior : CoordinatorLayout.Behavior<View> {
false
} else {
dropState = DropState.CLOSE
if (child != null && toolbar != null && backContainer != null) {
drawDropState(child!!, toolbar!!, backContainer!!, withAnimation)
if (backLayout != null && toolbar != null && frontLayout != null) {
drawDropState(frontLayout!!, toolbar!!, backLayout!!, withAnimation)
} else {
throw IllegalArgumentException("Toolbar and backContainer must be initialized")
}
notifyListeners(false)
true
}

private fun initViews(parent: CoordinatorLayout, child: View, toolbar: Toolbar, backContainer: View) {
backContainer.y = toolbar.y + toolbar.height
child.layoutParams.height = parent.height - toolbar.height
drawDropState(child, toolbar, backContainer, false)
private fun initViews(parent: CoordinatorLayout, frontLayout: View, toolbar: Toolbar, backLayout: View) {

// TODO (next release): remove this block
if (toolbarId != null) {
backLayout.y = toolbar.y + toolbar.height
}

frontLayout.layoutParams.height = parent.height - calculateTopPosition(backLayout, toolbar).toInt()
drawDropState(frontLayout, toolbar, backLayout, false)

with(toolbar) {
setNavigationOnClickListener {
dropState = when (dropState) {
DropState.CLOSE -> DropState.OPEN
DropState.OPEN -> DropState.CLOSE
}
drawDropState(child, toolbar, backContainer)
drawDropState(frontLayout, toolbar, backLayout)
notifyListeners(true)
}
}

needToInitializing = false
}

private fun drawDropState(child: View, toolbar: Toolbar, backContainer: View, withAnimation: Boolean = true) {
private fun drawDropState(frontLayout: View, toolbar: Toolbar, backContainer: View, withAnimation: Boolean = true) {
when (dropState) {
DropState.CLOSE -> {
drawClosedState(child, backContainer, withAnimation)
drawClosedState(frontLayout, backContainer, toolbar, withAnimation)
toolbar.setNavigationIcon(closedIconId)
}
DropState.OPEN -> {
drawOpenedState(child, backContainer, withAnimation)
drawOpenedState(frontLayout, backContainer, withAnimation)
toolbar.setNavigationIcon(openedIconRes)
}
}
}

private fun drawClosedState(child: View, backContainer: View, withAnimation: Boolean = true) {
val position = backContainer.y
private fun drawClosedState(frontLayout: View, backLayout: View, toolbar: Toolbar, withAnimation: Boolean = true) {
val position = calculateTopPosition(backLayout, toolbar)
val duration = if (withAnimation) DEFAULT_DURATION else WITHOUT_DURATION

child.animate().y(position).setDuration(duration).start()
frontLayout.animate().y(position).setDuration(duration).start()
}

private fun drawOpenedState(child: View, backContainer: View, withAnimation: Boolean = true) {
val position = backContainer.y + backContainer.height
private fun drawOpenedState(frontLayout: View, backLayout: View, withAnimation: Boolean = true) {
val position = calculateBottomPosition(backLayout)
val duration = if (withAnimation) DEFAULT_DURATION else WITHOUT_DURATION

child.animate().y(position).setDuration(duration).start()
frontLayout.animate().y(position).setDuration(duration).start()
}

private fun calculateTopPosition(backLayout: View, toolbar: Toolbar): Float {
// TODO (next release): remove this block
return if (toolbarId != null) {
backLayout.y
} else {
(backLayout.y + toolbar.y + toolbar.height)
}
}

private fun calculateBottomPosition(backLayout: View): Float {
return backLayout.y + backLayout.height
}

private fun notifyListeners(fromUser: Boolean) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ru.semper_viventem.backdrop

import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar

internal class BackdropUtils {

fun findToolbar(viewGroup: ViewGroup): Toolbar? {
for (chileId in 0..viewGroup.childCount) {
val childView = viewGroup.getChildAt(chileId)
if (childView is Toolbar) {
return childView
}
}

return null
}
}
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.2.50'
ext.kotlin_version = '1.3.20'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
classpath 'com.android.tools.build:gradle:3.3.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
Expand Down
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
android.useAndroidX = true
android.enableJetifier = true
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
Expand Down
Loading

0 comments on commit 3841ed5

Please sign in to comment.