Skip to content

Commit

Permalink
Merge pull request #18 from Semper-Viventem/featere/AppBarLayout_support
Browse files Browse the repository at this point in the history
Featere/app bar layout support
  • Loading branch information
Semper-Viventem authored Feb 10, 2019
2 parents 3da95e6 + 1cced40 commit daba77e
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 73 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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.os.Bundle
import android.os.Parcelable
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
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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class MainActivity : AppCompatActivity() {
private const val MENU_TEXT = R.id.menuText
private const val MENU_LIST = R.id.menuList

private const val FRAGMENT_CONTAINER = R.id.foregroundContainer
private const val FRAGMENT_CONTAINER = R.id.frontLayout

private const val DEFAULT_ITEM = MENU_GALLERY
}
Expand All @@ -32,10 +32,9 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

backdropBehavior = foregroundContainer.findBehavior()
backdropBehavior = frontLayout.findBehavior()
with(backdropBehavior) {
attachBackContainer(R.id.backContainer)
attachToolbar(R.id.toolbar)
attachBackLayout(R.id.backLayout)
}
with(toolbar) {
setTitle(R.string.app_name)
Expand Down
Loading

0 comments on commit daba77e

Please sign in to comment.