Merge pull request #65 from TouchInstinct/base_fragment

Base fragment
This commit is contained in:
PilotOfSparrow 2019-09-11 14:08:39 +03:00 committed by GitHub
commit 8532d190e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 168 additions and 6 deletions

2
.gitignore vendored
View File

@ -3,6 +3,6 @@
/local.properties
/.idea
.DS_Store
/build
/captures
.externalNativeBuild
**/build

View File

@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.3.11'
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()

View File

@ -1,5 +1,6 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion versions.compileSdk
@ -21,14 +22,19 @@ dependencies {
api 'androidx.multidex:multidex:2.0.1'
api 'net.danlew:android.joda:2.9.9.4'
api 'net.danlew:android.joda:2.10.2'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "androidx.appcompat:appcompat:$versions.appcompat"
implementation "androidx.fragment:fragment:$versions.fragment"
implementation "androidx.fragment:fragment-ktx:$versions.fragment"
implementation "com.jakewharton:butterknife:$versions.butterknife"
kapt "com.jakewharton:butterknife-compiler:$versions.butterknife"
implementation("com.crashlytics.sdk.android:crashlytics:$versions.crashlytics@aar") {
transitive = true
}
}

View File

@ -21,12 +21,16 @@ package ru.touchin.roboswag.components.navigation
import android.content.Context
import android.os.Bundle
import android.os.Parcelable
import android.view.MenuItem
import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import ru.touchin.roboswag.core.log.Lc
import ru.touchin.roboswag.components.navigation.fragments.BaseFragment
import ru.touchin.roboswag.components.navigation.viewcontrollers.EmptyState
import kotlin.reflect.KClass
/**
* Created by Gavriil Sitnikov on 07/03/2016.
@ -150,9 +154,27 @@ open class FragmentNavigation(
fragmentClass: Class<out Fragment>,
args: Bundle? = null,
addToStack: Boolean = true,
backStackName: String? = null,
transactionSetup: ((FragmentTransaction) -> Unit)? = null
) {
addToStack(fragmentClass, null, 0, addToStack, args, null, transactionSetup)
addToStack(fragmentClass, null, 0, addToStack, args, backStackName, transactionSetup)
}
/**
* Pushes [Fragment] on top of stack with specific target fragment, arguments and transaction setup.
*
* @param fragmentClass KClass of [Fragment] to instantiate;
* @param state State of instantiated [Fragment];
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
*/
fun <T: Parcelable> push(
fragmentClass: KClass<out BaseFragment<*, out T>>,
state: T? = null,
addToStack: Boolean = true,
backStackName: String? = null,
transactionSetup: ((FragmentTransaction) -> Unit)? = null
) {
push(fragmentClass.java, BaseFragment.args(state ?: EmptyState), addToStack, backStackName, transactionSetup)
}
/**
@ -181,6 +203,24 @@ open class FragmentNavigation(
)
}
/**
* Pushes [Fragment] on top of stack with specific target fragment, arguments and transaction setup.
*
* @param fragmentClass KClass of [Fragment] to instantiate;
* @param targetFragment Target fragment to be set as [Fragment.getTargetFragment] of instantiated [Fragment];
* @param state State of instantiated [Fragment];
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
*/
fun <T: Parcelable> pushForResult(
fragmentClass: KClass<out BaseFragment<*, out T>>,
targetFragment: Fragment,
targetRequestCode: Int,
state: T? = null,
transactionSetup: ((FragmentTransaction) -> Unit)? = null
) {
pushForResult(fragmentClass.java, targetFragment, targetRequestCode, BaseFragment.args(state ?: EmptyState), transactionSetup)
}
/**
* Pushes [Fragment] on top of stack with specific transaction setup, arguments
* and with [.TOP_FRAGMENT_TAG_MARK] tag used for simple up/back navigation.
@ -215,6 +255,22 @@ open class FragmentNavigation(
setAsTop(fragmentClass, args, false, transactionSetup)
}
/**
* Pops all [Fragment]s and places new initial [Fragment] on top of stack with specific transaction setup and arguments.
*
* @param fragmentClass Class of [Fragment] to instantiate;
* @param state State of instantiated [Fragment];
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
*/
fun <T: Parcelable> setInitial(
fragmentClass: KClass<out BaseFragment<*, out T>>,
state: T? = null,
transactionSetup: ((FragmentTransaction) -> Unit)? = null
) {
beforeSetInitialActions()
setAsTop(fragmentClass.java, BaseFragment.args(state ?: EmptyState), false, transactionSetup)
}
/**
* Method calls every time before initial [Fragment] will be placed.
*/

View File

@ -0,0 +1,100 @@
package ru.touchin.roboswag.components.navigation.fragments
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable
import android.view.View
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import butterknife.ButterKnife
import butterknife.Unbinder
import ru.touchin.roboswag.components.navigation.BuildConfig
import ru.touchin.roboswag.components.navigation.viewcontrollers.LifecycleLoggingObserver
open class BaseFragment<TActivity : FragmentActivity, TState : Parcelable>(@LayoutRes layoutRes: Int) : Fragment(layoutRes) {
companion object {
private const val BASE_FRAGMENT_STATE_EXTRA = "BASE_FRAGMENT_STATE_EXTRA"
fun args(state: Parcelable?): Bundle = Bundle().also { it.putParcelable(BASE_FRAGMENT_STATE_EXTRA, state) }
// This method used to check unique state of each fragment.
// If two fragments share same class for state, you should not pass state instance of current fragment to the one you transition to
private fun <T : Parcelable> reserialize(parcelable: T): T {
var parcel = Parcel.obtain()
parcel.writeParcelable(parcelable, 0)
val serializableBytes = parcel.marshall()
parcel.recycle()
parcel = Parcel.obtain()
parcel.unmarshall(serializableBytes, 0, serializableBytes.size)
parcel.setDataPosition(0)
val result = parcel.readParcelable<T>(Thread.currentThread().contextClassLoader) ?: throw IllegalStateException("It must not be null")
parcel.recycle()
return result
}
}
protected val view: View
@JvmName("requireViewKtx") get() = requireView()
protected val activity: TActivity
@JvmName("requireActivityKtx") get() = requireActivity() as TActivity
protected val context: Context
@JvmName("requireContextKtx") get() = requireContext()
protected lateinit var state: TState
private set
private lateinit var butterKnifeUnbinder: Unbinder
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
state = savedInstanceState?.getParcelable<TState>(BASE_FRAGMENT_STATE_EXTRA)
?: arguments?.getParcelable(BASE_FRAGMENT_STATE_EXTRA)
?: throw IllegalStateException("Fragment state can't be null")
if (BuildConfig.DEBUG) {
state = reserialize(state)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycle.addObserver(LifecycleLoggingObserver())
butterKnifeUnbinder = ButterKnife.bind(this, view)
}
override fun onDestroyView() {
butterKnifeUnbinder.unbind()
super.onDestroyView()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(BASE_FRAGMENT_STATE_EXTRA, state)
}
fun <T : View> findViewById(@IdRes id: Int): T = view.findViewById(id)
@ColorInt
fun getColor(@ColorRes resId: Int): Int = ContextCompat.getColor(requireContext(), resId)
fun getColorStateList(@ColorRes resId: Int): ColorStateList? = ContextCompat.getColorStateList(context, resId)
fun getDrawable(@DrawableRes resId: Int): Drawable? = ContextCompat.getDrawable(context, resId)
}

View File

@ -171,7 +171,7 @@ open class ViewControllerFragment<TActivity : FragmentActivity, TState : Parcela
viewController?.onCreateOptionsMenu(menu, inflater)
}
override fun onPrepareOptionsMenu(menu: Menu?) {
override fun onPrepareOptionsMenu(menu: Menu) {
viewController?.onPrepareOptionsMenu(menu)
}