Skip to content

Commit

Permalink
Merge branch 'release/2.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcel Vojtkovszky committed Nov 5, 2020
2 parents 2d0e90d + 547e362 commit 58c4e00
Show file tree
Hide file tree
Showing 15 changed files with 247 additions and 130 deletions.
55 changes: 37 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,63 @@
# SingleActivityNavigation
A simple single activity application framework with straightforward navigation controlls, allowing
to reuse fragments and move between them as root views, dialogs, bottom sheets, modals etc. with minimal dependencies.
A single activity application framework with straightforward navigation controls, allowing free
movement between fragments through different containers using a single line of code.
<br/><br/>
<img src="example.gif" alt="Example Flow" width="320"/>

## How does it work?
Your single activity must extend from BaseSingleActivity and all your fragments extend from BaseSingleFragment.
Then you can simply move between your fragments by using public methods in your activity or fragment.
Your (single) activity must extend from BaseSingleActivity and all your fragments extend from BaseSingleFragment.
Then you can simply move between fragments from either activity or fragment.

``` kotlin
// take any fragment of your choosing
// take any fragment(s) of your choosing
val myFragment = MyFragment()

// simply navigate to it
navigateTo(myFragment)

// open your fragment in bottom sheet
// ... or open it in bottom sheet
openBottomSheet(myFragment)

// ... or in a dialog
openDialog(myFragment)

// you can simply navigate through the back stack using:
// multiple ways to navigate through back stack
navigateBack()
navigateBackToRoot()
navigateBackTo("fragmentName")
navigateBackTo("fragment name")
closeCurrentlyOpenBottomSheet()
closeCurrentlyOpenDialog()
```

// define your root fragment(s). Those will be held at the bottom of stack, intended as the initial activity's fragment
override fun getNewRootFragmentInstance(positionIndex: Int): BaseSingleFragment? {
return MyRootFragment()
<br/>Make use of many convenience methods to help you control the state of your app
``` kotlin
getCurrentFragment()?.let {
when (it.fragmentType) {
FragmentType.ROOT -> TODO()
FragmentType.DEFAULT -> TODO()
FragmentType.MODAL -> TODO()
FragmentType.DIALOG -> TODO()
FragmentType.BOTTOM_SHEET -> TODO()
}
}

// you can fine-tune animation behaviour for fragment transitions by accessing customAnimationSettings
customAnimationSettings.setCustomAnimationsRoot(...)
customAnimationSettings.setCustomAnimationsModal(...)
customAnimationSettings.setCustomAnimationsDefault(...)
```

Refer to example application for detailed implementation overview.
<br/>Let the fragment define behavioural patterns of it's own by overriding open properties:
``` kotlin
open val isModal: Boolean
open val overridesBackPress: Boolean
open val animationEnter: Int
open val animationExit: Int
open val animationPopEnter: Int
open val animationPopExit: Int
```

<br/>And some more:
* Master/detail implementation support.
* Safe instance state recreation.
* Custom transition animations based on fragment type.
* Make sure to check out example app to help you get started.
<br/>

## Nice! How do I get started?
Make sure root build.gradle repositories include JitPack
Expand All @@ -51,7 +70,7 @@ allprojects {
}
```

And BillinSingleActivityNavigationHelper dependency is added to app build.gradle
And SingleActivityNavigation dependency is added to app build.gradle
``` gradle
dependencies {
implementation 'com.github.mvojtkovszky:SingleActivityNavigation:$latest_version'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.View
import com.vojtkovszky.singleactivitynavigation.BaseSingleActivity
import com.vojtkovszky.singleactivitynavigation.BaseSingleFragment
import com.vojtkovszky.singleactivitynavigation.FragmentType
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : BaseSingleActivity() {
Expand All @@ -13,8 +14,15 @@ class MainActivity : BaseSingleActivity() {
private const val ROOT_FRAGMENT_POS_HOME = 0
private const val ROOT_FRAGMENT_POS_DASHBOARD = 1
private const val ROOT_FRAGMENT_POS_NOTIFICATIONS = 2
// bundle keys for instance state
private const val ARG_SELECTED_TAB_INDEX = "MainActivity.ARG_SELECTED_TAB_INDEX"
}

// define containers where fragments will be held
override val defaultFragmentContainerId: Int = R.id.fragmentContainer

private var selectedTabIndex = ROOT_FRAGMENT_POS_HOME

// define main fragments based on position
override fun getNewRootFragmentInstance(positionIndex: Int): BaseSingleFragment? {
return when (positionIndex) {
Expand All @@ -25,42 +33,62 @@ class MainActivity : BaseSingleActivity() {
}
}

// define containers where fragments will be held
override val defaultFragmentContainerId: Int = R.id.fragmentContainer

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// we'll be switching main fragments with out bottom navigation
navigationView.setOnNavigationItemSelectedListener {
when (it.itemId) {
R.id.navigation_home -> selectRootFragment(ROOT_FRAGMENT_POS_HOME)
R.id.navigation_dashboard -> selectRootFragment(ROOT_FRAGMENT_POS_DASHBOARD)
R.id.navigation_notifications -> selectRootFragment(ROOT_FRAGMENT_POS_NOTIFICATIONS)
selectedTabIndex = when (it.itemId) {
R.id.navigation_home -> ROOT_FRAGMENT_POS_HOME
R.id.navigation_dashboard -> ROOT_FRAGMENT_POS_DASHBOARD
R.id.navigation_notifications -> ROOT_FRAGMENT_POS_NOTIFICATIONS
else -> return@setOnNavigationItemSelectedListener false
}
selectRootFragment(selectedTabIndex)
return@setOnNavigationItemSelectedListener true
}

// set animation behaviour
customAnimationSettings.setCustomAnimationsRoot(
R.anim.enter_fade_in, 0, 0, 0)
customAnimationSettings.setCustomAnimationsModal(
R.anim.enter_from_bottom, R.anim.exit_to_top_short,
R.anim.enter_from_top_short, R.anim.exit_to_bottom)
customAnimationSettings.setCustomAnimationsDefault(
R.anim.enter_from_right, R.anim.exit_to_left_short,
R.anim.enter_from_left_short, R.anim.exit_to_right)
// fresh start
if (savedInstanceState == null) {
// set animation behaviour
customAnimationSettings.setCustomAnimationsRoot(
0, 0, 0, 0)
customAnimationSettings.setCustomAnimationsModal(
R.anim.enter_from_bottom, R.anim.exit_to_top_short,
R.anim.enter_from_top_short, R.anim.exit_to_bottom)
customAnimationSettings.setCustomAnimationsDefault(
R.anim.enter_from_right, R.anim.exit_to_left_short,
R.anim.enter_from_left_short, R.anim.exit_to_right)

closeDialogsAndSheetsWhileNavigating = true

// use home fragment as default
selectRootFragment(ROOT_FRAGMENT_POS_HOME)
// select fragment
selectRootFragment(selectedTabIndex)
}
// just restore bottom bar visibility
else {
handleNavigationViewVisibility(supportFragmentManager.backStackEntryCount)
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(ARG_SELECTED_TAB_INDEX, selectedTabIndex)
}

override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
selectedTabIndex = savedInstanceState.getInt(ARG_SELECTED_TAB_INDEX, ROOT_FRAGMENT_POS_HOME)
}

override fun onBackStackChanged(backStackCount: Int) {
handleNavigationViewVisibility(backStackCount)
super.onBackStackChanged(backStackCount)
}

private fun handleNavigationViewVisibility(backStackCount: Int) {
// only make bottom bar visible if we're on root screen
navigationView.visibility = if (backStackCount == 0) View.VISIBLE else View.GONE

super.onBackStackChanged(backStackCount)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class MainFragment : BaseSingleFragment() {

// use it to set status text
textStatus.text = getString(R.string.this_is_type_fragment, title)
textStackSize.text = getString(R.string.stack_size_is,
activity?.supportFragmentManager?.backStackEntryCount ?: -1)
// and change title, but not needed in dialog
if (!fragmentType.isDialogOrBottomSheet()) {
baseSingleActivity?.supportActionBar?.title = title
Expand Down
10 changes: 0 additions & 10 deletions app/src/main/res/anim/enter_fade_in.xml

This file was deleted.

3 changes: 1 addition & 2 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize">
android:layout_height="match_parent">

<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/navigationView"
Expand Down
8 changes: 8 additions & 0 deletions app/src/main/res/layout/fragment_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@
android:id="@+id/textStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:textColor="@color/black"
android:layout_gravity="center_horizontal"/>
<TextView
android:id="@+id/textStackSize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:textColor="@color/black"
android:layout_gravity="center_horizontal"/>

<Button
Expand Down
16 changes: 0 additions & 16 deletions app/src/main/res/values-night/themes.xml

This file was deleted.

3 changes: 2 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
<string name="type_dialog">Dialog</string>
<string name="back_to_root">Back To Root</string>
<string name="open_type_fragment">Open %s Fragment</string>
<string name="this_is_type_fragment">This is %s Fragment</string>
<string name="this_is_type_fragment">This is a %s Fragment</string>
<string name="stack_size_is">Stack size: %d</string>
</resources>
3 changes: 0 additions & 3 deletions app/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,5 @@
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
2 changes: 1 addition & 1 deletion singleactivitynavigation/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ android {
minSdkVersion 17
targetSdkVersion 30
versionCode 1
versionName "2.0.0"
versionName "2.1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
Loading

0 comments on commit 58c4e00

Please sign in to comment.