Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62a5a81107 | |||
| d234dd4c75 | |||
| a48b49cdbe | |||
| f0a488c711 | |||
| 511c229364 | |||
| b5d0e46740 | |||
| 8f1be7fe21 | |||
| 8a70c09fea | |||
| 343e8c92e5 | |||
| 332788ee01 | |||
| acf77101a7 | |||
| cba372966d | |||
| fff9f76e8a | |||
| bcc8f47856 | |||
| 61d68a9c6c | |||
| 5e7258973e | |||
| 194696e9cb | |||
| 5130909efc | |||
| 7874c4f72d | |||
| 8399868a63 | |||
| 3e97e7f8a8 | |||
| d5e54f33a0 | |||
| 58d1f37db0 | |||
| 7c175e4ce5 | |||
| 328b8a0873 | |||
| db446279bb | |||
| 80042ea71d |
@@ -1,4 +1,4 @@
|
||||
[](https://travis-ci.org/bluelinelabs/Conductor)
|
||||
[](https://travis-ci.org/bluelinelabs/Conductor) [](http://android-arsenal.com/details/1/3361)
|
||||
|
||||
# Conductor
|
||||
|
||||
@@ -14,20 +14,20 @@ A small, yet full-featured framework that allows building View-based Android app
|
||||
:floppy_disk: | State persistence
|
||||
:phone: | Callbacks for onActivityResult, onRequestPermissionsResult, etc
|
||||
:european_post_office: | MVP / MVVM / VIPER / MVC ready
|
||||
|
||||
|
||||
Conductor is architecture-agnostic and does not try to force any design decisions on the developer. We here at BlueLine Labs tend to use either MVP or MVVM, but it would work equally well with standard MVC or whatever else you want to throw at it.
|
||||
|
||||
## Installation
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:1.0.2'
|
||||
compile 'com.bluelinelabs:conductor:1.1.3'
|
||||
|
||||
// If you want the components that go along with
|
||||
// Android's support libraries (currently just a PagerAdapter):
|
||||
compile 'com.bluelinelabs:conductor-support:1.0.2'
|
||||
compile 'com.bluelinelabs:conductor-support:1.1.3'
|
||||
|
||||
// If you want RxJava/RxAndroid lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:1.0.2'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:1.1.3'
|
||||
```
|
||||
|
||||
## Components to Know
|
||||
@@ -43,7 +43,7 @@ __ControllerTransaction__ | Transactions are used to define data about adding Co
|
||||
|
||||
### Minimal Activity implementation
|
||||
|
||||
```
|
||||
```java
|
||||
public class MainActivity extends Activity {
|
||||
|
||||
private Router mRouter;
|
||||
@@ -53,9 +53,9 @@ public class MainActivity extends Activity {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
ViewGroup container = (ViewGroup)findViewById(R.id.controller_container)
|
||||
|
||||
|
||||
ViewGroup container = (ViewGroup)findViewById(R.id.controller_container)
|
||||
|
||||
mRouter = Conductor.attachRouter(this, container, savedInstanceState);
|
||||
if (!mRouter.hasRootController()) {
|
||||
mRouter.setRoot(new HomeController());
|
||||
@@ -74,19 +74,14 @@ public class MainActivity extends Activity {
|
||||
|
||||
### Minimal Controller implementation
|
||||
|
||||
```
|
||||
```java
|
||||
public class HomeController extends Controller {
|
||||
|
||||
@Override
|
||||
protected int layoutId() {
|
||||
return R.layout.controller_overlay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindView(@NonNull View view) {
|
||||
super.onBindView(view);
|
||||
|
||||
((TextView)view.findViewById(R.id.tv_title)).setText("Hello World");
|
||||
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
View view = inflater.inflate(R.layout.controller_home, container, false);
|
||||
((TextView)view.findViewById(R.id.tv_title)).setText("Hello World");
|
||||
return view;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.5.0'
|
||||
classpath 'com.android.tools.build:gradle:2.0.0'
|
||||
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
apply plugin: 'java'
|
||||
|
||||
configurations {
|
||||
lintChecks
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile rootProject.ext.lintapi
|
||||
compile rootProject.ext.lintchecks
|
||||
|
||||
lintChecks files(jar)
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes('Lint-Registry': 'com.bluelinelabs.conductor.lint.IssueRegistry')
|
||||
}
|
||||
}
|
||||
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package com.bluelinelabs.conductor.lint;
|
||||
|
||||
import com.android.annotations.NonNull;
|
||||
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
|
||||
import com.android.tools.lint.detector.api.Category;
|
||||
import com.android.tools.lint.detector.api.Detector;
|
||||
import com.android.tools.lint.detector.api.Implementation;
|
||||
import com.android.tools.lint.detector.api.Issue;
|
||||
import com.android.tools.lint.detector.api.JavaContext;
|
||||
import com.android.tools.lint.detector.api.Scope;
|
||||
import com.android.tools.lint.detector.api.Severity;
|
||||
import com.android.tools.lint.detector.api.Speed;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.ast.ClassDeclaration;
|
||||
import lombok.ast.ConstructorDeclaration;
|
||||
import lombok.ast.Node;
|
||||
import lombok.ast.NormalTypeBody;
|
||||
import lombok.ast.StrictListAccessor;
|
||||
import lombok.ast.TypeMember;
|
||||
import lombok.ast.VariableDefinition;
|
||||
|
||||
public final class ControllerChangeHandlerIssueDetector extends Detector implements Detector.JavaScanner, Detector.ClassScanner {
|
||||
|
||||
public static final Issue ISSUE =
|
||||
Issue.create("ValidControllerChangeHandler", "ControllerChangeHandler not instantiatable",
|
||||
"Non-abstract ControllerChangeHandler instances must have a default constructor for the"
|
||||
+ " system to re-create them in the case of the process being killed.",
|
||||
Category.CORRECTNESS, 6, Severity.FATAL,
|
||||
new Implementation(ControllerChangeHandlerIssueDetector.class, Scope.JAVA_FILE_SCOPE));
|
||||
|
||||
public ControllerChangeHandlerIssueDetector() { }
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Speed getSpeed() {
|
||||
return Speed.FAST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> applicableSuperClasses() {
|
||||
return Collections.singletonList("com.bluelinelabs.conductor.ControllerChangeHandler");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClass(@NonNull JavaContext context, ClassDeclaration node,
|
||||
@NonNull Node declarationOrAnonymous, @NonNull ResolvedClass cls) {
|
||||
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int flags = node.astModifiers().getEffectiveModifierFlags();
|
||||
if ((flags & Modifier.ABSTRACT) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((flags & Modifier.PUBLIC) == 0) {
|
||||
String message = String.format("This ControllerChangeHandler class should be public (%1$s)", cls.getName());
|
||||
context.report(ISSUE, node, context.getLocation(node.astName()), message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cls.getContainingClass() != null && (flags & Modifier.STATIC) == 0) {
|
||||
String message = String.format("This ControllerChangeHandler inner class should be static (%1$s)", cls.getName());
|
||||
context.report(ISSUE, node, context.getLocation(node.astName()), message);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasConstructor = false;
|
||||
boolean hasDefaultConstructor = false;
|
||||
NormalTypeBody body = node.astBody();
|
||||
if (body != null) {
|
||||
for (TypeMember member : body.astMembers()) {
|
||||
if (member instanceof ConstructorDeclaration) {
|
||||
hasConstructor = true;
|
||||
ConstructorDeclaration constructor = (ConstructorDeclaration)member;
|
||||
|
||||
if (constructor.astModifiers().isPublic()) {
|
||||
StrictListAccessor<VariableDefinition, ConstructorDeclaration> params = constructor.astParameters();
|
||||
if (params.isEmpty()) {
|
||||
hasDefaultConstructor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasConstructor && !hasDefaultConstructor) {
|
||||
String message = String.format(
|
||||
"This ControllerChangeHandler needs to have a public default constructor (`%1$s`)",
|
||||
cls.getName());
|
||||
context.report(ISSUE, node, context.getLocation(node.astName()), message);
|
||||
}
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package com.bluelinelabs.conductor.lint;
|
||||
|
||||
import com.android.SdkConstants;
|
||||
import com.android.annotations.NonNull;
|
||||
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
|
||||
import com.android.tools.lint.detector.api.Category;
|
||||
import com.android.tools.lint.detector.api.Detector;
|
||||
import com.android.tools.lint.detector.api.Implementation;
|
||||
import com.android.tools.lint.detector.api.Issue;
|
||||
import com.android.tools.lint.detector.api.JavaContext;
|
||||
import com.android.tools.lint.detector.api.Scope;
|
||||
import com.android.tools.lint.detector.api.Severity;
|
||||
import com.android.tools.lint.detector.api.Speed;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.ast.ClassDeclaration;
|
||||
import lombok.ast.ConstructorDeclaration;
|
||||
import lombok.ast.Node;
|
||||
import lombok.ast.NormalTypeBody;
|
||||
import lombok.ast.StrictListAccessor;
|
||||
import lombok.ast.TypeMember;
|
||||
import lombok.ast.VariableDefinition;
|
||||
|
||||
public final class ControllerIssueDetector extends Detector implements Detector.JavaScanner, Detector.ClassScanner {
|
||||
|
||||
public static final Issue ISSUE =
|
||||
Issue.create("ValidController", "Controller not instantiatable",
|
||||
"Non-abstract Controller instances must have a default or single-argument constructor"
|
||||
+ " that takes a Bundle in order for the system to re-create them in the"
|
||||
+ " case of the process being killed.", Category.CORRECTNESS, 6, Severity.FATAL,
|
||||
new Implementation(ControllerIssueDetector.class, Scope.JAVA_FILE_SCOPE));
|
||||
|
||||
public ControllerIssueDetector() { }
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Speed getSpeed() {
|
||||
return Speed.FAST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> applicableSuperClasses() {
|
||||
return Collections.singletonList("com.bluelinelabs.conductor.Controller");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClass(@NonNull JavaContext context, ClassDeclaration node,
|
||||
@NonNull Node declarationOrAnonymous, @NonNull ResolvedClass cls) {
|
||||
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int flags = node.astModifiers().getEffectiveModifierFlags();
|
||||
if ((flags & Modifier.ABSTRACT) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((flags & Modifier.PUBLIC) == 0) {
|
||||
String message = String.format("This Controller class should be public (%1$s)", cls.getName());
|
||||
context.report(ISSUE, node, context.getLocation(node.astName()), message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cls.getContainingClass() != null && (flags & Modifier.STATIC) == 0) {
|
||||
String message = String.format("This Controller inner class should be static (%1$s)", cls.getName());
|
||||
context.report(ISSUE, node, context.getLocation(node.astName()), message);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasConstructor = false;
|
||||
boolean hasDefaultConstructor = false;
|
||||
boolean hasBundleConstructor = false;
|
||||
NormalTypeBody body = node.astBody();
|
||||
if (body != null) {
|
||||
for (TypeMember member : body.astMembers()) {
|
||||
if (member instanceof ConstructorDeclaration) {
|
||||
hasConstructor = true;
|
||||
ConstructorDeclaration constructor = (ConstructorDeclaration)member;
|
||||
|
||||
if (constructor.astModifiers().isPublic()) {
|
||||
StrictListAccessor<VariableDefinition, ConstructorDeclaration> params = constructor.astParameters();
|
||||
if (params.isEmpty()) {
|
||||
hasDefaultConstructor = true;
|
||||
break;
|
||||
} else if (params.size() == 1 &&
|
||||
(params.first().astTypeReference().getTypeName().equals(SdkConstants.CLASS_BUNDLE)) ||
|
||||
params.first().astTypeReference().getTypeName().equals("Bundle")) {
|
||||
hasBundleConstructor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasConstructor && !hasDefaultConstructor && !hasBundleConstructor) {
|
||||
String message = String.format(
|
||||
"This Controller needs to have either a public default constructor or a" +
|
||||
" public single-argument constructor that takes a Bundle. (`%1$s`)",
|
||||
cls.getName());
|
||||
context.report(ISSUE, node, context.getLocation(node.astName()), message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.bluelinelabs.conductor.lint;
|
||||
|
||||
import com.android.tools.lint.detector.api.Issue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class IssueRegistry extends com.android.tools.lint.client.api.IssueRegistry {
|
||||
@Override public List<Issue> getIssues() {
|
||||
return Arrays.asList(
|
||||
ControllerIssueDetector.ISSUE,
|
||||
ControllerChangeHandlerIssueDetector.ISSUE);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -3,10 +3,10 @@ package com.bluelinelabs.conductor.rxlifecycle;
|
||||
public enum ControllerEvent {
|
||||
|
||||
CREATE,
|
||||
CREATE_VIEW,
|
||||
ATTACH,
|
||||
BIND_VIEW,
|
||||
DETACH,
|
||||
UNBIND_VIEW,
|
||||
DESTROY_VIEW,
|
||||
DESTROY
|
||||
|
||||
}
|
||||
|
||||
+6
-6
@@ -21,8 +21,8 @@ public class ControllerLifecycleSubjectHelper {
|
||||
|
||||
controller.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void preBindView(@NonNull Controller controller, @NonNull View view) {
|
||||
subject.onNext(ControllerEvent.BIND_VIEW);
|
||||
public void preCreateView(@NonNull Controller controller) {
|
||||
subject.onNext(ControllerEvent.CREATE_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -31,13 +31,13 @@ public class ControllerLifecycleSubjectHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preUnbindView(@NonNull Controller controller, @NonNull View view) {
|
||||
subject.onNext(ControllerEvent.UNBIND_VIEW);
|
||||
public void preDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
subject.onNext(ControllerEvent.DETACH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
subject.onNext(ControllerEvent.DETACH);
|
||||
public void preDestroyView(@NonNull Controller controller, @NonNull View view) {
|
||||
subject.onNext(ControllerEvent.DESTROY_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -33,8 +33,8 @@ public class RxControllerLifecycle {
|
||||
return ControllerEvent.DESTROY;
|
||||
case ATTACH:
|
||||
return ControllerEvent.DETACH;
|
||||
case BIND_VIEW:
|
||||
return ControllerEvent.UNBIND_VIEW;
|
||||
case CREATE_VIEW:
|
||||
return ControllerEvent.DESTROY_VIEW;
|
||||
case DETACH:
|
||||
return ControllerEvent.DESTROY;
|
||||
default:
|
||||
|
||||
@@ -31,11 +31,17 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
lintChecks
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompile rootProject.ext.junit
|
||||
testCompile rootProject.ext.roboelectric
|
||||
|
||||
compile rootProject.ext.supportAnnotations
|
||||
|
||||
lintChecks project(path: ':conductor-lint', configuration: 'lintChecks')
|
||||
}
|
||||
|
||||
unMock {
|
||||
@@ -46,6 +52,18 @@ unMock {
|
||||
keep "android.text.TextUtils"
|
||||
}
|
||||
|
||||
task copyLintJar(type: Copy) {
|
||||
from(configurations.lintChecks) {
|
||||
rename { 'lint.jar' }
|
||||
}
|
||||
into 'build/intermediates/lint/'
|
||||
}
|
||||
|
||||
project.afterEvaluate {
|
||||
def compileLintTask = project.tasks.find { it.name == 'compileLint' }
|
||||
compileLintTask.dependsOn(copyLintJar)
|
||||
}
|
||||
|
||||
ext.artifactId = 'conductor'
|
||||
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
|
||||
@@ -67,16 +67,18 @@ class Backstack implements Iterable<RouterTransaction> {
|
||||
mBackStack.push(transaction);
|
||||
}
|
||||
|
||||
public void popAll() {
|
||||
public List<RouterTransaction> popAll() {
|
||||
List<RouterTransaction> list = new ArrayList<>();
|
||||
while (!isEmpty()) {
|
||||
pop();
|
||||
list.add(pop());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public void saveInstanceState(Bundle outState) {
|
||||
public void detachAndSaveInstanceState(Bundle outState) {
|
||||
ArrayList<Bundle> entryBundles = new ArrayList<>(mBackStack.size());
|
||||
for (RouterTransaction entry : mBackStack) {
|
||||
entryBundles.add(entry.toBundle());
|
||||
entryBundles.add(entry.detachAndSaveInstanceState());
|
||||
}
|
||||
|
||||
outState.putParcelableArrayList(KEY_ENTRIES, entryBundles);
|
||||
|
||||
@@ -43,11 +43,12 @@ public class ChangeHandlerFrameLayout extends FrameLayout implements ControllerC
|
||||
|
||||
@Override
|
||||
public void onChangeStarted(Controller to, Controller from, boolean isPush, ViewGroup container, ControllerChangeHandler handler) {
|
||||
mInProgressTransactionCount++;
|
||||
mInProgressTransactionCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChangeCompleted(Controller to, Controller from, boolean isPush, ViewGroup container, ControllerChangeHandler handler) {
|
||||
mInProgressTransactionCount--;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ public class ChildControllerTransaction extends ControllerTransaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle toBundle() {
|
||||
Bundle bundle = super.toBundle();
|
||||
public Bundle detachAndSaveInstanceState() {
|
||||
Bundle bundle = super.detachAndSaveInstanceState();
|
||||
bundle.putInt(KEY_CONTAINER_ID, containerId);
|
||||
bundle.putBoolean(KEY_ADD_TO_LOCAL_BACKSTACK, addToLocalBackstack);
|
||||
return bundle;
|
||||
|
||||
@@ -10,8 +10,10 @@ import com.bluelinelabs.conductor.internal.LifecycleHandler;
|
||||
/**
|
||||
* Point of initial interaction with Conductor. Used to attach a {@link Router} to your Activity.
|
||||
*/
|
||||
public class Conductor {
|
||||
|
||||
public final class Conductor {
|
||||
|
||||
private Conductor() {}
|
||||
|
||||
/**
|
||||
* Conductor will create a {@link Router} that has been initialized for your Activity and containing ViewGroup.
|
||||
* If an existing {@link Router} is already associated with this Activity/ViewGroup pair, either in memory
|
||||
|
||||
@@ -11,6 +11,9 @@ import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.SparseArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
import android.view.ViewGroup;
|
||||
@@ -18,6 +21,7 @@ import android.view.ViewGroup;
|
||||
import com.bluelinelabs.conductor.ControllerTransaction.ControllerChangeType;
|
||||
import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler;
|
||||
import com.bluelinelabs.conductor.internal.ClassUtils;
|
||||
import com.bluelinelabs.conductor.internal.RouterRequiringFunc;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
@@ -53,6 +57,9 @@ public abstract class Controller {
|
||||
private boolean mIsBeingDestroyed;
|
||||
private boolean mDestroyed;
|
||||
private boolean mAttached;
|
||||
private boolean mHasOptionsMenu;
|
||||
private boolean mOptionsMenuHidden;
|
||||
private boolean mViewIsAttached;
|
||||
private Router mRouter;
|
||||
private View mView;
|
||||
private Controller mParentController;
|
||||
@@ -65,6 +72,7 @@ public abstract class Controller {
|
||||
private final List<ChildControllerTransaction> mChildControllers = new ArrayList<>();
|
||||
private final List<LifecycleListener> mLifecycleListeners = new ArrayList<>();
|
||||
private final ArrayList<String> mRequestedPermissions = new ArrayList<>();
|
||||
private final ArrayList<RouterRequiringFunc> mOnRouterSetListeners = new ArrayList<>();
|
||||
|
||||
static Controller newInstance(Bundle bundle) {
|
||||
final String className = bundle.getString(KEY_CLASS_NAME);
|
||||
@@ -108,15 +116,16 @@ public abstract class Controller {
|
||||
|
||||
/**
|
||||
* Called when the controller is ready to display its view. A valid view must be returned. The standard body
|
||||
* for this method will be {@code return inflater.inflate(R.layout.my_layout, container, false);}
|
||||
* for this method will be {@code return inflater.inflate(R.layout.my_layout, container, false);}, plus
|
||||
* any binding code.
|
||||
*
|
||||
* @param inflater The LayoutInflater that should be used to inflate views
|
||||
* @param inflater The LayoutInflater that should be used to inflate views
|
||||
* @param container The parent view that this Controller's view will eventually be attached to.
|
||||
* This Controller's view should NOT be added in this method. It is simply passed in
|
||||
* so that valid LayoutParams can be used during inflation.
|
||||
*/
|
||||
@NonNull
|
||||
protected abstract View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container);
|
||||
protected abstract View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container);
|
||||
|
||||
/**
|
||||
* Returns the {@link Router} object that can be used for pushing or popping other Controllers
|
||||
@@ -201,14 +210,16 @@ public abstract class Controller {
|
||||
* Returns the Resources from the host Activity
|
||||
*/
|
||||
public final Resources getResources() {
|
||||
return getActivity().getResources();
|
||||
Activity activity = getActivity();
|
||||
return activity != null ? activity.getResources() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Application Context derived from the host Activity
|
||||
*/
|
||||
public final Context getApplicationContext() {
|
||||
return getActivity().getApplicationContext();
|
||||
Activity activity = getActivity();
|
||||
return activity != null ? activity.getApplicationContext() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,22 +280,18 @@ public abstract class Controller {
|
||||
|
||||
/**
|
||||
* Optional target for this Controller. One reason this could be used is to send results back to the Controller
|
||||
* that started this one. Target Controllers are retained across instances.
|
||||
*
|
||||
* @param target The Controller that is the target of this one.
|
||||
*/
|
||||
public final void setTargetController(Controller target) {
|
||||
mTargetInstanceId = target != null ? target.getInstanceId() : null;
|
||||
onTargetControllerSet(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be called when {@link #setTargetController(Controller)} is called. It is recommended
|
||||
* that started this one. Target Controllers are retained across instances. It is recommended
|
||||
* that Controllers enforce that their target Controller conform to a specific Interface.
|
||||
*
|
||||
* @param target The Controller that is the target of this one.
|
||||
*/
|
||||
public void onTargetControllerSet(Controller target) { }
|
||||
public void setTargetController(Controller target) {
|
||||
if (mTargetInstanceId != null) {
|
||||
throw new RuntimeException("Target controller already set. A controller's target may only be set once.");
|
||||
}
|
||||
|
||||
mTargetInstanceId = target != null ? target.getInstanceId() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target Controller that was set with the {@link #setTargetController(Controller)} method
|
||||
@@ -295,27 +302,19 @@ public abstract class Controller {
|
||||
return mTargetInstanceId != null ? mRouter.getControllerWithInstanceId(mTargetInstanceId) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this Controller's View is inflated. This should overridden to bind the View
|
||||
* to variables, either using findViewById or something like Butterknife.
|
||||
*
|
||||
* @param view The View to which this Controller should be bound.
|
||||
*/
|
||||
protected void onBindView(@NonNull final View view) { }
|
||||
|
||||
/**
|
||||
* Called when this Controller's View is being destroyed. This should overridden to unbind the View
|
||||
* from any local variables.
|
||||
*
|
||||
* @param view The View to which this Controller should be bound.
|
||||
*/
|
||||
protected void onUnbindView(View view) { }
|
||||
protected void onDestroyView(View view) { }
|
||||
|
||||
/**
|
||||
* Called when this Controller begins the process of being swapped in or out of the host view.
|
||||
*
|
||||
* @param changeHandler The {@link ControllerChangeHandler} that's managing the swap
|
||||
* @param changeType The type of change that's occurring
|
||||
* @param changeType The type of change that's occurring
|
||||
*/
|
||||
protected void onChangeStarted(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) { }
|
||||
|
||||
@@ -323,7 +322,7 @@ public abstract class Controller {
|
||||
* Called when this Controller completes the process of being swapped in or out of the host view.
|
||||
*
|
||||
* @param changeHandler The {@link ControllerChangeHandler} that's managing the swap
|
||||
* @param changeType The type of change that occurred
|
||||
* @param changeType The type of change that occurred
|
||||
*/
|
||||
protected void onChangeEnded(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) { }
|
||||
|
||||
@@ -371,7 +370,7 @@ public abstract class Controller {
|
||||
* Controller lifecycle (ex: when another Controller has been pushed on top of it), care should be taken
|
||||
* to save anything needed to reconstruct the View.
|
||||
*
|
||||
* @param view This Controller's View, passed for convenience
|
||||
* @param view This Controller's View, passed for convenience
|
||||
* @param outState The Bundle into which the View state should be saved
|
||||
*/
|
||||
protected void onSaveViewState(@NonNull View view, @NonNull Bundle outState) { }
|
||||
@@ -380,7 +379,7 @@ public abstract class Controller {
|
||||
* Restores data that was saved in the {@link #onSaveViewState(View, Bundle)} method. This should be overridden
|
||||
* to restore the View's state to where it was before it was destroyed.
|
||||
*
|
||||
* @param view This Controller's View, passed for convenience
|
||||
* @param view This Controller's View, passed for convenience
|
||||
* @param savedViewState The bundle that has data to be restored
|
||||
*/
|
||||
protected void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) { }
|
||||
@@ -400,18 +399,43 @@ public abstract class Controller {
|
||||
*/
|
||||
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { }
|
||||
|
||||
/**
|
||||
* Calls startActivity(Intent) from this Controller's host Activity.
|
||||
*/
|
||||
public final void startActivity(final Intent intent) {
|
||||
executeWithRouter(new RouterRequiringFunc() {
|
||||
@Override public void execute() { mRouter.getLifecycleHandler().startActivity(intent); }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls startActivityForResult(Intent, int) from this Controller's host Activity.
|
||||
*/
|
||||
public final void startActivityForResult(Intent intent, int requestCode) {
|
||||
getRouter().getLifecycleHandler().startActivityForResult(mInstanceId, intent, requestCode);
|
||||
public final void startActivityForResult(final Intent intent, final int requestCode) {
|
||||
executeWithRouter(new RouterRequiringFunc() {
|
||||
@Override public void execute() { mRouter.getLifecycleHandler().startActivityForResult(mInstanceId, intent, requestCode); }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls startActivityForResult(Intent, int, Bundle) from this Controller's host Activity.
|
||||
*/
|
||||
public final void startActivityForResult(Intent intent, int requestCode, Bundle options) {
|
||||
getRouter().getLifecycleHandler().startActivityForResult(mInstanceId, intent, requestCode, options);
|
||||
public final void startActivityForResult(final Intent intent, final int requestCode, final Bundle options) {
|
||||
executeWithRouter(new RouterRequiringFunc() {
|
||||
@Override public void execute() { mRouter.getLifecycleHandler().startActivityForResult(mInstanceId, intent, requestCode, options); }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this Controller to handle onActivityResult responses. Calling this method is NOT
|
||||
* necessary when calling {@link #startActivityForResult(Intent, int)}
|
||||
*
|
||||
* @param requestCode The request code being registered for.
|
||||
*/
|
||||
public final void registerForActivityResult(final int requestCode) {
|
||||
executeWithRouter(new RouterRequiringFunc() {
|
||||
@Override public void execute() { mRouter.getLifecycleHandler().registerForActivityRequest(mInstanceId, requestCode); }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -419,8 +443,8 @@ public abstract class Controller {
|
||||
* the result.
|
||||
*
|
||||
* @param requestCode The requestCode passed to startActivityForResult
|
||||
* @param resultCode The resultCode that was returned to the host Activity's onActivityResult method
|
||||
* @param data The data Intent that was returned to the host Activity's onActivityResult method
|
||||
* @param resultCode The resultCode that was returned to the host Activity's onActivityResult method
|
||||
* @param data The data Intent that was returned to the host Activity's onActivityResult method
|
||||
*/
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) { }
|
||||
|
||||
@@ -430,9 +454,12 @@ public abstract class Controller {
|
||||
* {@link #onRequestPermissionsResult(int, String[], int[])} will be forwarded back to this Controller by the system.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
|
||||
public final void requestPermissions(@NonNull final String[] permissions, final int requestCode) {
|
||||
mRequestedPermissions.addAll(Arrays.asList(permissions));
|
||||
getRouter().getLifecycleHandler().requestPermissions(mInstanceId, permissions, requestCode);
|
||||
|
||||
executeWithRouter(new RouterRequiringFunc() {
|
||||
@Override public void execute() { mRouter.getLifecycleHandler().requestPermissions(mInstanceId, permissions, requestCode); }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -448,8 +475,8 @@ public abstract class Controller {
|
||||
/**
|
||||
* Should be overridden if this Controller has requested runtime permissions and needs to handle the user's response.
|
||||
*
|
||||
* @param requestCode The requestCode that was used to request the permissions
|
||||
* @param permissions The array of permissions requested
|
||||
* @param requestCode The requestCode that was used to request the permissions
|
||||
* @param permissions The array of permissions requested
|
||||
* @param grantResults The results for each permission requested
|
||||
*/
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { }
|
||||
@@ -541,6 +568,70 @@ public abstract class Controller {
|
||||
mOverriddenPopHandler = overriddenPopHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers/unregisters for participation in populating the options menu by receiving options-related
|
||||
* callbacks, such as {@link #onCreateOptionsMenu(Menu, MenuInflater)}
|
||||
*
|
||||
* @param hasOptionsMenu If true, this controller's options menu callbacks will be called.
|
||||
*/
|
||||
public final void setHasOptionsMenu(boolean hasOptionsMenu) {
|
||||
boolean invalidate = mAttached && !mOptionsMenuHidden && mHasOptionsMenu != hasOptionsMenu;
|
||||
|
||||
mHasOptionsMenu = hasOptionsMenu;
|
||||
|
||||
if (invalidate) {
|
||||
mRouter.invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not this controller's menu items should be visible. This is useful for hiding the
|
||||
* controller's options menu items when its UI is hidden, and not just when it is detached from the
|
||||
* window (the default).
|
||||
*
|
||||
* @param optionsMenuHidden Defaults to false. If true, this controller's menu items will not be shown.
|
||||
*/
|
||||
public final void setOptionsMenuHidden(boolean optionsMenuHidden) {
|
||||
boolean invalidate = mAttached && mHasOptionsMenu && mOptionsMenuHidden != optionsMenuHidden;
|
||||
|
||||
mOptionsMenuHidden = optionsMenuHidden;
|
||||
|
||||
if (invalidate) {
|
||||
mRouter.invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds option items to the host Activity's standard options menu. This will only be called if
|
||||
* {@link #setHasOptionsMenu(boolean)} has been called.
|
||||
*
|
||||
* @param menu The menu into which your options should be placed.
|
||||
* @param inflater The inflater that can be used to inflate your menu items.
|
||||
*/
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { }
|
||||
|
||||
/**
|
||||
* Prepare the screen's options menu to be displayed. This is called directly before showing the
|
||||
* menu and can be used modify its contents.
|
||||
*
|
||||
* @param menu The menu that will be displayed
|
||||
*/
|
||||
public void onPrepareOptionsMenu(Menu menu) { }
|
||||
|
||||
/**
|
||||
* Called when an option menu item has been selected by the user.
|
||||
*
|
||||
* @param item The selected item.
|
||||
* @return True if this event has been consumed, false if it has not.
|
||||
*/
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final void prepareForActivityPause() {
|
||||
mNeedsAttach = mNeedsAttach || mAttached;
|
||||
}
|
||||
|
||||
final boolean getNeedsAttach() {
|
||||
return mNeedsAttach;
|
||||
}
|
||||
@@ -557,11 +648,24 @@ public abstract class Controller {
|
||||
final void setRouter(@NonNull Router router) {
|
||||
mRouter = router;
|
||||
|
||||
for (RouterRequiringFunc listener : mOnRouterSetListeners) {
|
||||
listener.execute();
|
||||
}
|
||||
mOnRouterSetListeners.clear();
|
||||
|
||||
for (ChildControllerTransaction child : mChildControllers) {
|
||||
child.controller.setRouter(router);
|
||||
}
|
||||
}
|
||||
|
||||
final void executeWithRouter(@NonNull RouterRequiringFunc listener) {
|
||||
if (mRouter != null) {
|
||||
listener.execute();
|
||||
} else {
|
||||
mOnRouterSetListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private void addChildController(ChildControllerTransaction transaction, ControllerChangeHandler pushChangeHandler) {
|
||||
if (transaction.controller.mParentController == null) {
|
||||
transaction.controller.setRouter(mRouter);
|
||||
@@ -595,6 +699,10 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
final void activityResumed(Activity activity) {
|
||||
if (!mAttached && mView != null && mViewIsAttached) {
|
||||
attach(mView);
|
||||
}
|
||||
|
||||
onActivityResumed(activity);
|
||||
|
||||
for (ChildControllerTransaction child : mChildControllers) {
|
||||
@@ -622,7 +730,7 @@ public abstract class Controller {
|
||||
if (isChangingConfigurations) {
|
||||
removeViewReference();
|
||||
} else {
|
||||
destroy();
|
||||
destroy(true);
|
||||
}
|
||||
|
||||
for (ChildControllerTransaction child : mChildControllers) {
|
||||
@@ -637,7 +745,6 @@ public abstract class Controller {
|
||||
|
||||
mAttached = true;
|
||||
mNeedsAttach = false;
|
||||
mView = view;
|
||||
|
||||
for (ChildControllerTransaction child : mChildControllers) {
|
||||
attachChildController(child, new SimpleSwapChangeHandler());
|
||||
@@ -645,22 +752,30 @@ public abstract class Controller {
|
||||
|
||||
onAttach(view);
|
||||
|
||||
if (mHasOptionsMenu && !mOptionsMenuHidden) {
|
||||
mRouter.invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.postAttach(this, view);
|
||||
}
|
||||
}
|
||||
|
||||
private void detach(@NonNull View view) {
|
||||
private void detach(@NonNull View view, boolean allowViewRefRemoval) {
|
||||
final boolean removeViewRef = allowViewRefRemoval && (mRetainViewMode == RetainViewMode.RELEASE_DETACH || mIsBeingDestroyed);
|
||||
|
||||
if (mAttached) {
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.preDetach(this, view);
|
||||
}
|
||||
|
||||
saveViewState(view);
|
||||
|
||||
mAttached = false;
|
||||
onDetach(view);
|
||||
|
||||
if (mHasOptionsMenu && !mOptionsMenuHidden) {
|
||||
mRouter.invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
for (ChildControllerTransaction child : mChildControllers) {
|
||||
ViewGroup container = (ViewGroup)mView.findViewById(child.containerId);
|
||||
if (container != null) {
|
||||
@@ -668,28 +783,34 @@ public abstract class Controller {
|
||||
}
|
||||
}
|
||||
|
||||
if (mRetainViewMode == RetainViewMode.RELEASE_DETACH || mIsBeingDestroyed) {
|
||||
if (removeViewRef) {
|
||||
removeViewReference();
|
||||
}
|
||||
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.postDetach(this, view);
|
||||
}
|
||||
} else if (removeViewRef) {
|
||||
removeViewReference();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeViewReference() {
|
||||
if (mView != null) {
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.preUnbindView(this, mView);
|
||||
if (!mIsBeingDestroyed) {
|
||||
saveViewState(mView);
|
||||
}
|
||||
|
||||
onUnbindView(mView);
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.preDestroyView(this, mView);
|
||||
}
|
||||
|
||||
onDestroyView(mView);
|
||||
|
||||
mView = null;
|
||||
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.postUnbindView(this);
|
||||
lifecycleListener.postDestroyView(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,13 +821,36 @@ public abstract class Controller {
|
||||
|
||||
final View inflate(@NonNull ViewGroup parent) {
|
||||
if (mView == null) {
|
||||
View view = inflateView(LayoutInflater.from(parent.getContext()), parent);
|
||||
bindView(view);
|
||||
restoreViewState(view);
|
||||
return view;
|
||||
} else {
|
||||
return mView;
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.preCreateView(this);
|
||||
}
|
||||
|
||||
mView = onCreateView(LayoutInflater.from(parent.getContext()), parent);
|
||||
|
||||
restoreViewState(mView);
|
||||
|
||||
mView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) {
|
||||
if (v == mView) {
|
||||
mViewIsAttached = true;
|
||||
}
|
||||
attach(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) {
|
||||
mViewIsAttached = false;
|
||||
detach(v, true);
|
||||
}
|
||||
});
|
||||
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.postCreateView(this, mView);
|
||||
}
|
||||
}
|
||||
|
||||
return mView;
|
||||
}
|
||||
|
||||
final void performDestroy() {
|
||||
@@ -717,6 +861,10 @@ public abstract class Controller {
|
||||
|
||||
mDestroyed = true;
|
||||
|
||||
if (mRouter != null) {
|
||||
mRouter.getLifecycleHandler().unregisterForActivityRequests(mInstanceId);
|
||||
}
|
||||
|
||||
onDestroy();
|
||||
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
@@ -726,14 +874,20 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
final void destroy() {
|
||||
destroy(false);
|
||||
}
|
||||
|
||||
final void destroy(boolean removeViews) {
|
||||
mIsBeingDestroyed = true;
|
||||
|
||||
for (ChildControllerTransaction child : mChildControllers) {
|
||||
child.controller.destroy();
|
||||
child.controller.destroy(removeViews);
|
||||
}
|
||||
|
||||
if (!mAttached) {
|
||||
removeViewReference();
|
||||
} else if (removeViews) {
|
||||
detach(mView, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -768,9 +922,9 @@ public abstract class Controller {
|
||||
}
|
||||
}
|
||||
|
||||
final Bundle saveInstanceState() {
|
||||
final Bundle detachAndSaveInstanceState() {
|
||||
if (mAttached && mView != null) {
|
||||
saveViewState(mView);
|
||||
detach(mView, mIsBeingDestroyed);
|
||||
}
|
||||
|
||||
Bundle outState = new Bundle();
|
||||
@@ -791,7 +945,7 @@ public abstract class Controller {
|
||||
|
||||
ArrayList<Bundle> childBundles = new ArrayList<>();
|
||||
for (ChildControllerTransaction childController : mChildControllers) {
|
||||
childBundles.add(childController.toBundle());
|
||||
childBundles.add(childController.detachAndSaveInstanceState());
|
||||
}
|
||||
outState.putParcelableArrayList(KEY_CHILDREN, childBundles);
|
||||
|
||||
@@ -845,30 +999,25 @@ public abstract class Controller {
|
||||
}
|
||||
}
|
||||
|
||||
private void bindView(@NonNull final View view) {
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.preBindView(this, view);
|
||||
final void createOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
if (mAttached && mHasOptionsMenu && !mOptionsMenuHidden) {
|
||||
onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
}
|
||||
|
||||
onBindView(view);
|
||||
|
||||
view.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) {
|
||||
attach(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) {
|
||||
detach(view);
|
||||
}
|
||||
});
|
||||
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.postBindView(this, view);
|
||||
final void prepareOptionsMenu(Menu menu) {
|
||||
if (mAttached && mHasOptionsMenu && !mOptionsMenuHidden) {
|
||||
onPrepareOptionsMenu(menu);
|
||||
}
|
||||
}
|
||||
|
||||
final boolean optionsItemSelected(MenuItem item) {
|
||||
if (mAttached && mHasOptionsMenu && !mOptionsMenuHidden) {
|
||||
return onOptionsItemSelected(item);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ensureRequiredConstructor() {
|
||||
Constructor[] constructors = getClass().getConstructors();
|
||||
if (getBundleConstructor(constructors) == null && getDefaultConstructor(constructors) == null) {
|
||||
@@ -908,18 +1057,18 @@ public abstract class Controller {
|
||||
public void onChangeStart(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) { }
|
||||
public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) { }
|
||||
|
||||
public void preBindView(@NonNull Controller controller, @NonNull View view) { }
|
||||
public void postBindView(@NonNull Controller controller, @NonNull View view) { }
|
||||
public void preCreateView(@NonNull Controller controller) { }
|
||||
public void postCreateView(@NonNull Controller controller, @NonNull View view) { }
|
||||
|
||||
public void preAttach(@NonNull Controller controller, @NonNull View view) { }
|
||||
public void postAttach(@NonNull Controller controller, @NonNull View view) { }
|
||||
|
||||
public void preUnbindView(@NonNull Controller controller, @NonNull View view) { }
|
||||
public void postUnbindView(@NonNull Controller controller) { }
|
||||
|
||||
public void preDetach(@NonNull Controller controller, @NonNull View view) { }
|
||||
public void postDetach(@NonNull Controller controller, @NonNull View view) { }
|
||||
|
||||
public void preDestroyView(@NonNull Controller controller, @NonNull View view) { }
|
||||
public void postDestroyView(@NonNull Controller controller) { }
|
||||
|
||||
public void preDestroy(@NonNull Controller controller) { }
|
||||
public void postDestroy(@NonNull Controller controller) { }
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
@@ -71,7 +70,7 @@ public abstract class ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public static ControllerChangeHandler fromBundle(@Nullable Bundle bundle) {
|
||||
public static ControllerChangeHandler fromBundle(Bundle bundle) {
|
||||
if (bundle != null) {
|
||||
String className = bundle.getString(KEY_CLASS_NAME);
|
||||
ControllerChangeHandler changeHandler = ClassUtils.newInstance(className);
|
||||
|
||||
@@ -13,16 +13,24 @@ public class ControllerTransaction {
|
||||
*/
|
||||
public enum ControllerChangeType {
|
||||
/** The Controller is being pushed to the host container */
|
||||
PUSH_ENTER,
|
||||
PUSH_ENTER(true, true),
|
||||
|
||||
/** The Controller is being pushed to the backstack as another Controller is pushed to the host container */
|
||||
PUSH_EXIT,
|
||||
PUSH_EXIT(true, false),
|
||||
|
||||
/** The Controller is being popped from the backstack and placed in the host container as another Controller is popped */
|
||||
POP_ENTER,
|
||||
POP_ENTER(false, true),
|
||||
|
||||
/** The Controller is being popped from the host contianer */
|
||||
POP_EXIT
|
||||
/** The Controller is being popped from the host container */
|
||||
POP_EXIT(false, false);
|
||||
|
||||
public boolean isPush;
|
||||
public boolean isEnter;
|
||||
|
||||
ControllerChangeType(boolean isPush, boolean isEnter) {
|
||||
this.isPush = isPush;
|
||||
this.isEnter = isEnter;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String KEY_VIEW_CONTROLLER_BUNDLE = "ControllerTransaction.controller.bundle";
|
||||
@@ -77,10 +85,10 @@ public class ControllerTransaction {
|
||||
/**
|
||||
* Used to serialize this transaction into a Bundle
|
||||
*/
|
||||
public Bundle toBundle() {
|
||||
public Bundle detachAndSaveInstanceState() {
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
bundle.putBundle(KEY_VIEW_CONTROLLER_BUNDLE, controller.saveInstanceState());
|
||||
bundle.putBundle(KEY_VIEW_CONTROLLER_BUNDLE, controller.detachAndSaveInstanceState());
|
||||
|
||||
if (mPushControllerChangeHandler != null) {
|
||||
bundle.putBundle(KEY_PUSH_TRANSITION, mPushControllerChangeHandler.toBundle());
|
||||
|
||||
@@ -4,8 +4,13 @@ import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller.LifecycleListener;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeListener;
|
||||
import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler;
|
||||
import com.bluelinelabs.conductor.internal.LifecycleHandler;
|
||||
@@ -26,28 +31,25 @@ public class Router {
|
||||
private LifecycleHandler mLifecycleHandler;
|
||||
private ViewGroup mContainer;
|
||||
private final List<ControllerChangeListener> mChangeListeners = new ArrayList<>();
|
||||
private final List<Controller> mDestroyingControllers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Returns this Router's host Activity
|
||||
*/
|
||||
public Activity getActivity() {
|
||||
return mLifecycleHandler.getLifecycleActivity();
|
||||
return mLifecycleHandler != null ? mLifecycleHandler.getLifecycleActivity() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be called by the host Activity when its onActivityResult method is called. The call will be forwarded
|
||||
* to the {@link Controller} with the instanceId passed in.
|
||||
* This should be called by the host Activity when its onActivityResult method is called if the instanceId
|
||||
* of the controller that called startActivityForResult is not known.
|
||||
*
|
||||
* @param instanceId The instanceId of the Controller to which this result should be forwarded
|
||||
* @param requestCode The Activity's onActivityResult requestCode
|
||||
* @param resultCode The Activity's onActivityResult resultCode
|
||||
* @param data The Activity's onActivityResult data
|
||||
*/
|
||||
public void onActivityResult(String instanceId, int requestCode, int resultCode, Intent data) {
|
||||
Controller controller = getControllerWithInstanceId(instanceId);
|
||||
if (controller != null) {
|
||||
controller.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
getLifecycleHandler().onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,7 +104,7 @@ public class Router {
|
||||
boolean poppingTopController = topController.controller == controller;
|
||||
|
||||
if (poppingTopController) {
|
||||
mBackStack.pop();
|
||||
trackDestroyingController(mBackStack.pop());
|
||||
} else {
|
||||
for (RouterTransaction transaction : mBackStack) {
|
||||
if (transaction.controller == controller) {
|
||||
@@ -140,7 +142,7 @@ public class Router {
|
||||
public void replaceTopController(@NonNull RouterTransaction transaction) {
|
||||
RouterTransaction topTransaction = mBackStack.peek();
|
||||
if (!mBackStack.isEmpty()) {
|
||||
mBackStack.pop();
|
||||
trackDestroyingController(mBackStack.pop());
|
||||
}
|
||||
|
||||
pushToBackstack(transaction);
|
||||
@@ -199,12 +201,32 @@ public class Router {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the root {@link Controller}. If any {@link Controller} are currently in the backstack, they will be removed.
|
||||
* Sets the root {@link Controller}. If any {@link Controller}s are currently in the backstack, they will be removed.
|
||||
*
|
||||
* @param controller The new root {@link Controller}
|
||||
*/
|
||||
public void setRoot(@NonNull Controller controller) {
|
||||
setRoot(controller, null);
|
||||
setRoot(controller, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the root {@link Controller}. If any {@link Controller}s are currently in the backstack, they will be removed.
|
||||
*
|
||||
* @param controller The new root {@link Controller}
|
||||
* @param tag The tag to use for this {@link Controller}
|
||||
*/
|
||||
public void setRoot(@NonNull Controller controller, String tag) {
|
||||
setRoot(controller, tag, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the root {@link Controller}. If any {@link Controller}s are currently in the backstack, they will be removed.
|
||||
*
|
||||
* @param controller The new root {@link Controller}
|
||||
* @param changeHandler The {@link ControllerChangeHandler} to use for setting the root
|
||||
*/
|
||||
public void setRoot(@NonNull Controller controller, ControllerChangeHandler changeHandler) {
|
||||
setRoot(controller, null, changeHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,19 +234,33 @@ public class Router {
|
||||
*
|
||||
* @param controller The new root {@link Controller}
|
||||
* @param tag The tag to use for this {@link Controller}
|
||||
* @param changeHandler The {@link ControllerChangeHandler} to use for setting the root
|
||||
*/
|
||||
public void setRoot(@NonNull Controller controller, String tag) {
|
||||
mContainer.removeAllViews();
|
||||
mBackStack.popAll();
|
||||
public void setRoot(@NonNull Controller controller, String tag, ControllerChangeHandler changeHandler) {
|
||||
RouterTransaction currentTop = mBackStack.peek();
|
||||
|
||||
if (currentTop != null && currentTop.controller.getView() != null) {
|
||||
final View fromView = currentTop.controller.getView();
|
||||
|
||||
final int childCount = mContainer.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = mContainer.getChildAt(i);
|
||||
if (child != fromView) {
|
||||
mContainer.removeView(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trackDestroyingControllers(mBackStack.popAll());
|
||||
|
||||
RouterTransaction transaction = RouterTransaction.builder(controller)
|
||||
.tag(tag)
|
||||
.pushChangeHandler(new SimpleSwapChangeHandler())
|
||||
.pushChangeHandler(changeHandler != null ? changeHandler : new SimpleSwapChangeHandler())
|
||||
.popChangeHandler(new SimpleSwapChangeHandler())
|
||||
.build();
|
||||
|
||||
pushToBackstack(transaction);
|
||||
performControllerChange(transaction, null, true);
|
||||
performControllerChange(transaction, currentTop, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,6 +346,13 @@ public class Router {
|
||||
}
|
||||
}
|
||||
|
||||
public final void onActivityResult(String instanceId, int requestCode, int resultCode, Intent data) {
|
||||
Controller controller = getControllerWithInstanceId(instanceId);
|
||||
if (controller != null) {
|
||||
controller.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
public final void onActivityStarted(Activity activity) {
|
||||
for (RouterTransaction transaction : mBackStack) {
|
||||
transaction.controller.activityStarted(activity);
|
||||
@@ -335,27 +378,64 @@ public class Router {
|
||||
}
|
||||
|
||||
public final void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
||||
mBackStack.saveInstanceState(outState);
|
||||
for (RouterTransaction transaction : mBackStack) {
|
||||
transaction.controller.prepareForActivityPause();
|
||||
}
|
||||
|
||||
mBackStack.detachAndSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
public final void onActivityDestroyed(Activity activity) {
|
||||
mContainer.setOnHierarchyChangeListener(null);
|
||||
mLifecycleHandler = null;
|
||||
mContainer = null;
|
||||
mChangeListeners.clear();
|
||||
|
||||
for (RouterTransaction transaction : mBackStack) {
|
||||
transaction.controller.activityDestroyed(activity.isChangingConfigurations());
|
||||
}
|
||||
|
||||
for (Controller controller : mDestroyingControllers) {
|
||||
controller.activityDestroyed(activity.isChangingConfigurations());
|
||||
}
|
||||
|
||||
mLifecycleHandler = null;
|
||||
mContainer = null;
|
||||
}
|
||||
|
||||
public final void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
mBackStack.restoreInstanceState(savedInstanceState);
|
||||
}
|
||||
|
||||
public final void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
for (RouterTransaction transaction : mBackStack) {
|
||||
transaction.controller.createOptionsMenu(menu, inflater);
|
||||
}
|
||||
}
|
||||
|
||||
public final void onPrepareOptionsMenu(Menu menu) {
|
||||
for (RouterTransaction transaction : mBackStack) {
|
||||
transaction.controller.prepareOptionsMenu(menu);
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean onOptionsItemSelected(MenuItem item) {
|
||||
for (RouterTransaction transaction : mBackStack) {
|
||||
if (transaction.controller.optionsItemSelected(item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
final void invalidateOptionsMenu() {
|
||||
if (mLifecycleHandler != null) {
|
||||
mLifecycleHandler.getFragmentManager().invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
private void popToTransaction(@NonNull RouterTransaction transaction, ControllerChangeHandler changeHandler) {
|
||||
RouterTransaction topTransaction = mBackStack.peek();
|
||||
List<RouterTransaction> poppedTransactions = mBackStack.popTo(transaction);
|
||||
trackDestroyingControllers(poppedTransactions);
|
||||
|
||||
if (poppedTransactions.size() > 0) {
|
||||
if (changeHandler == null) {
|
||||
@@ -430,4 +510,23 @@ public class Router {
|
||||
mBackStack.push(entry);
|
||||
}
|
||||
|
||||
private void trackDestroyingController(RouterTransaction transaction) {
|
||||
if (!transaction.controller.isDestroyed()) {
|
||||
mDestroyingControllers.add(transaction.controller);
|
||||
|
||||
transaction.controller.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void postDestroy(@NonNull Controller controller) {
|
||||
mDestroyingControllers.remove(controller);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void trackDestroyingControllers(List<RouterTransaction> transactions) {
|
||||
for (RouterTransaction transaction : transactions) {
|
||||
trackDestroyingController(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
@@ -119,6 +119,11 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (from != null && (!isPush || mRemovesFromViewOnPush)) {
|
||||
|
||||
+2
-12
@@ -3,7 +3,6 @@ package com.bluelinelabs.conductor.changehandler;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
@@ -44,19 +43,10 @@ public class SimpleSwapChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
|
||||
if (to != null && to.getParent() == null) {
|
||||
to.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View view) {
|
||||
view.removeOnAttachStateChangeListener(this);
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) { }
|
||||
});
|
||||
|
||||
container.addView(to);
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,9 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Router;
|
||||
@@ -33,6 +36,7 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
public LifecycleHandler() {
|
||||
setRetainInstance(true);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
private static LifecycleHandler findInActivity(Activity activity) {
|
||||
@@ -155,13 +159,54 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
return super.shouldShowRequestPermissionRationale(permission);
|
||||
}
|
||||
|
||||
public void startActivityForResult(String instanceId, Intent intent, int requestCode) {
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
|
||||
for (Router router : mRouterMap.values()) {
|
||||
router.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
|
||||
for (Router router : mRouterMap.values()) {
|
||||
router.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
for (Router router : mRouterMap.values()) {
|
||||
if (router.onOptionsItemSelected(item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public void registerForActivityRequest(String instanceId, int requestCode) {
|
||||
mActivityRequestMap.put(requestCode, instanceId);
|
||||
}
|
||||
|
||||
public void unregisterForActivityRequests(String instanceId) {
|
||||
for (int i = mActivityRequestMap.size() - 1; i >= 0; i--) {
|
||||
if (instanceId.equals(mActivityRequestMap.get(mActivityRequestMap.keyAt(i)))) {
|
||||
mActivityRequestMap.removeAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startActivityForResult(String instanceId, Intent intent, int requestCode) {
|
||||
registerForActivityRequest(instanceId, requestCode);
|
||||
startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
public void startActivityForResult(String instanceId, Intent intent, int requestCode, Bundle options) {
|
||||
mActivityRequestMap.put(requestCode, instanceId);
|
||||
registerForActivityRequest(instanceId, requestCode);
|
||||
startActivityForResult(intent, requestCode, options);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.bluelinelabs.conductor.internal;
|
||||
|
||||
public interface RouterRequiringFunc {
|
||||
void execute();
|
||||
}
|
||||
+1
-1
@@ -17,7 +17,7 @@ public class ControllerChangeHandlerTests {
|
||||
.pushChangeHandler(horizontalChangeHandler)
|
||||
.popChangeHandler(fadeChangeHandler)
|
||||
.build();
|
||||
RouterTransaction restoredTransaction = new RouterTransaction(transaction.toBundle());
|
||||
RouterTransaction restoredTransaction = new RouterTransaction(transaction.detachAndSaveInstanceState());
|
||||
|
||||
ControllerChangeHandler restoredHorizontal = restoredTransaction.getPushControllerChangeHandler();
|
||||
ControllerChangeHandler restoredFade = restoredTransaction.getPopControllerChangeHandler();
|
||||
|
||||
@@ -29,9 +29,9 @@ public class ControllerTests {
|
||||
|
||||
private int mChangeStartCalls;
|
||||
private int mChangeEndCalls;
|
||||
private int mBindViewCalls;
|
||||
private int mCreateViewCalls;
|
||||
private int mAttachCalls;
|
||||
private int mUnbindViewCalls;
|
||||
private int mDestroyViewCalls;
|
||||
private int mDetachCalls;
|
||||
private int mDestroyCalls;
|
||||
|
||||
@@ -44,9 +44,9 @@ public class ControllerTests {
|
||||
|
||||
mChangeStartCalls = 0;
|
||||
mChangeEndCalls = 0;
|
||||
mBindViewCalls = 0;
|
||||
mCreateViewCalls = 0;
|
||||
mAttachCalls = 0;
|
||||
mUnbindViewCalls = 0;
|
||||
mDestroyViewCalls = 0;
|
||||
mDestroyCalls = 0;
|
||||
mDestroyCalls = 0;
|
||||
}
|
||||
@@ -94,7 +94,6 @@ public class ControllerTests {
|
||||
assertCalls(1, 1, 1, 1, 0, 0, 0);
|
||||
|
||||
mActivityController.destroy();
|
||||
ViewUtils.setAttached(controller.getView(), false);
|
||||
|
||||
assertCalls(1, 1, 1, 1, 1, 1, 1);
|
||||
}
|
||||
@@ -180,7 +179,7 @@ public class ControllerTests {
|
||||
controller.setRetainViewMode(RetainViewMode.RELEASE_DETACH);
|
||||
Assert.assertNull(controller.getView());
|
||||
View view = controller.inflate(new FrameLayout(mRouter.getActivity()));
|
||||
Assert.assertNull(controller.getView());
|
||||
Assert.assertNotNull(controller.getView());
|
||||
ViewUtils.setAttached(view, true);
|
||||
Assert.assertNotNull(controller.getView());
|
||||
ViewUtils.setAttached(view, false);
|
||||
@@ -189,7 +188,7 @@ public class ControllerTests {
|
||||
// Test View getting retained w/ RETAIN_DETACH
|
||||
controller.setRetainViewMode(RetainViewMode.RETAIN_DETACH);
|
||||
view = controller.inflate(new FrameLayout(mRouter.getActivity()));
|
||||
Assert.assertNull(controller.getView());
|
||||
Assert.assertNotNull(controller.getView());
|
||||
ViewUtils.setAttached(view, true);
|
||||
Assert.assertNotNull(controller.getView());
|
||||
ViewUtils.setAttached(view, false);
|
||||
@@ -229,9 +228,9 @@ public class ControllerTests {
|
||||
private void assertCalls(int changeStart, int changeEnd, int bindView, int attach, int unbindView, int detach, int destroy) {
|
||||
Assert.assertEquals(changeStart, mChangeStartCalls);
|
||||
Assert.assertEquals(changeEnd, mChangeEndCalls);
|
||||
Assert.assertEquals(bindView, mBindViewCalls);
|
||||
Assert.assertEquals(bindView, mCreateViewCalls);
|
||||
Assert.assertEquals(attach, mAttachCalls);
|
||||
Assert.assertEquals(unbindView, mUnbindViewCalls);
|
||||
Assert.assertEquals(unbindView, mDestroyViewCalls);
|
||||
Assert.assertEquals(detach, mDetachCalls);
|
||||
Assert.assertEquals(destroy, mDestroyCalls);
|
||||
}
|
||||
@@ -249,8 +248,8 @@ public class ControllerTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBindView(@NonNull Controller controller, @NonNull View view) {
|
||||
mBindViewCalls++;
|
||||
public void postCreateView(@NonNull Controller controller, @NonNull View view) {
|
||||
mCreateViewCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -259,8 +258,8 @@ public class ControllerTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postUnbindView(@NonNull Controller controller) {
|
||||
mUnbindViewCalls++;
|
||||
public void postDestroyView(@NonNull Controller controller) {
|
||||
mDestroyViewCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -279,7 +278,7 @@ public class ControllerTests {
|
||||
void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener);
|
||||
}
|
||||
|
||||
static class ChangeHandler extends ControllerChangeHandler {
|
||||
public static class ChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
private ChangeHandlerListener mListener;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ public class ControllerTransactionTests {
|
||||
.tag("Test Tag")
|
||||
.build();
|
||||
|
||||
Bundle bundle = transaction.toBundle();
|
||||
Bundle bundle = transaction.detachAndSaveInstanceState();
|
||||
|
||||
RouterTransaction restoredTransaction = new RouterTransaction(bundle);
|
||||
|
||||
@@ -39,7 +39,7 @@ public class ControllerTransactionTests {
|
||||
.tag("Test Tag")
|
||||
.build();
|
||||
|
||||
Bundle bundle = transaction.toBundle();
|
||||
Bundle bundle = transaction.detachAndSaveInstanceState();
|
||||
|
||||
ChildControllerTransaction restoredTransaction = new ChildControllerTransaction(bundle);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ public class TestController extends Controller {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
View view = new FrameLayout(inflater.getContext());
|
||||
view.setId(VIEW_ID);
|
||||
return view;
|
||||
|
||||
@@ -45,6 +45,9 @@ dependencies {
|
||||
compile 'com.bluelinelabs:conductor-support:' + rootProject.ext.publishedVersionName
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:' + rootProject.ext.publishedVersionName
|
||||
|
||||
// compile project(':conductor-support')
|
||||
// compile project(':conductor-rxlifecycle')
|
||||
|
||||
debugCompile rootProject.ext.leakCanary
|
||||
releaseCompile rootProject.ext.leakCanaryNoOp
|
||||
testCompile rootProject.ext.leakCanaryNoOp
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.bluelinelabs.conductor.demo"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
@@ -7,11 +8,13 @@
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
|
||||
<application
|
||||
android:name="com.bluelinelabs.conductor.demo.App"
|
||||
android:name="com.bluelinelabs.conductor.demo.DemoApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
android:fullBackupContent="true"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
|
||||
<activity
|
||||
android:name="com.bluelinelabs.conductor.demo.MainActivity"
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.bluelinelabs.conductor.demo;
|
||||
|
||||
import android.support.v7.app.ActionBar;
|
||||
|
||||
public interface ActionBarProvider {
|
||||
ActionBar getSupportActionBar();
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ import android.app.Application;
|
||||
import com.squareup.leakcanary.LeakCanary;
|
||||
import com.squareup.leakcanary.RefWatcher;
|
||||
|
||||
public class App extends Application {
|
||||
public class DemoApplication extends Application {
|
||||
|
||||
public static RefWatcher refWatcher;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package com.bluelinelabs.conductor.demo;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Conductor;
|
||||
@@ -11,8 +12,9 @@ import com.bluelinelabs.conductor.demo.controllers.HomeController;
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
public class MainActivity extends AppCompatActivity implements ActionBarProvider {
|
||||
|
||||
@Bind(R.id.toolbar) Toolbar mToolbar;
|
||||
@Bind(R.id.controller_container) ViewGroup mContainer;
|
||||
|
||||
private Router mRouter;
|
||||
@@ -24,6 +26,8 @@ public class MainActivity extends Activity {
|
||||
setContentView(R.layout.activity_main);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
setSupportActionBar(mToolbar);
|
||||
|
||||
mRouter = Conductor.attachRouter(this, mContainer, savedInstanceState);
|
||||
if (!mRouter.hasRootController()) {
|
||||
mRouter.setRoot(new HomeController());
|
||||
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
package com.bluelinelabs.conductor.changehandler;
|
||||
package com.bluelinelabs.conductor.demo.changehandler;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.annotation.TargetApi;
|
||||
@@ -9,6 +9,8 @@ import android.view.View;
|
||||
import android.view.ViewAnimationUtils;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.changehandler.AnimatorChangeHandler;
|
||||
|
||||
/**
|
||||
* An {@link AnimatorChangeHandler} that will perform a circular reveal
|
||||
*/
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.bluelinelabs.conductor.demo.changehandler;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
public class CircularRevealChangeHandlerCompat extends CircularRevealChangeHandler {
|
||||
|
||||
public CircularRevealChangeHandlerCompat() { }
|
||||
|
||||
public CircularRevealChangeHandlerCompat(@NonNull View fromView, @NonNull View containerView) {
|
||||
super(fromView, containerView);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Animator getAnimator(@NonNull ViewGroup container, View from, View to, boolean isPush, boolean toAddedToContainer) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return super.getAnimator(container, from, to, isPush, toAddedToContainer);
|
||||
} else {
|
||||
AnimatorSet animator = new AnimatorSet();
|
||||
if (to != null && toAddedToContainer) {
|
||||
animator.play(ObjectAnimator.ofFloat(to, View.ALPHA, 0, 1));
|
||||
}
|
||||
|
||||
if (from != null) {
|
||||
animator.play(ObjectAnimator.ofFloat(from, View.ALPHA, 0));
|
||||
}
|
||||
|
||||
return animator;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,13 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.demo.BundleBuilder;
|
||||
import com.bluelinelabs.conductor.demo.util.BundleBuilder;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.RefWatchingController;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
|
||||
import butterknife.Bind;
|
||||
|
||||
public class ChildController extends RefWatchingController {
|
||||
public class ChildController extends BaseController {
|
||||
|
||||
private static final String KEY_TITLE = "ChildController.title";
|
||||
private static final String KEY_BG_COLOR = "ChildController.bgColor";
|
||||
@@ -41,8 +41,8 @@ public class ChildController extends RefWatchingController {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindView(@NonNull View view) {
|
||||
super.onBindView(view);
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
||||
mTvTitle.setText(getArgs().getString(KEY_TITLE));
|
||||
|
||||
|
||||
+10
-7
@@ -10,14 +10,14 @@ import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.changehandler.ScaleFadeChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.RefWatchingController;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.widget.ElasticDragDismissFrameLayout;
|
||||
import com.bluelinelabs.conductor.demo.widget.ElasticDragDismissFrameLayout.ElasticDragDismissCallback;
|
||||
|
||||
import butterknife.Bind;
|
||||
|
||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||
public class DragDismissController extends RefWatchingController {
|
||||
public class DragDismissController extends BaseController {
|
||||
|
||||
@Bind(R.id.tv_lorem_ipsum) TextView mTvLoremIpsum;
|
||||
|
||||
@@ -36,9 +36,7 @@ public class DragDismissController extends RefWatchingController {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindView(@NonNull View view) {
|
||||
super.onBindView(view);
|
||||
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
((ElasticDragDismissFrameLayout)view).addListener(mDragDismissListener);
|
||||
|
||||
mTvLoremIpsum.setText("Lorem ipsum dolor sit amet, volutpat lacus egestas integer vitae, tempus potenti posuere dolore, elit cras ut vulputate pede eros. Pharetra curabitur, cum ultrices nisi nulla, non a est diamlorem in pede. Feugiat vivamus id, leo massa, pede ligula libero wisi, posuere nec interdum risus. Mauris eros. Scelerisque etiam dignissim, sem odio magna posuere libero in. Eget non posuere, rutrum nunc ut, ipsum ornare, vestibulum nisl turpis, urna interdum. Arcu mi velit. Sem dolor amet sed hymenaeos tempor. Cras felis.\n" +
|
||||
@@ -48,9 +46,14 @@ public class DragDismissController extends RefWatchingController {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUnbindView(View view) {
|
||||
super.onUnbindView(view);
|
||||
protected void onDestroyView(View view) {
|
||||
super.onDestroyView(view);
|
||||
|
||||
((ElasticDragDismissFrameLayout)view).removeListener(mDragDismissListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Drag to Dismiss";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,40 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.ColorRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.AbsoluteSizeSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.ChildControllerTransaction;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.ControllerTransaction.ControllerChangeType;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.RefWatchingController;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class HomeController extends RefWatchingController {
|
||||
public class HomeController extends BaseController {
|
||||
|
||||
public enum HomeDemoModel {
|
||||
NAVIGATION("Navigation Demos", R.color.red_300),
|
||||
@@ -45,6 +57,10 @@ public class HomeController extends RefWatchingController {
|
||||
|
||||
@Bind(R.id.recycler_view) RecyclerView mRecyclerView;
|
||||
|
||||
public HomeController() {
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
@@ -52,14 +68,66 @@ public class HomeController extends RefWatchingController {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindView(@NonNull View view) {
|
||||
super.onBindView(view);
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
||||
mRecyclerView.setHasFixedSize(true);
|
||||
mRecyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
|
||||
mRecyclerView.setAdapter(new HomeAdapter(LayoutInflater.from(view.getContext()), HomeDemoModel.values()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.home, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChangeStarted(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
setOptionsMenuHidden(!changeType.isEnter);
|
||||
|
||||
if (changeType.isEnter) {
|
||||
setTitle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.about) {
|
||||
SpannableString details = new SpannableString("A small, yet full-featured framework that allows building View-based Android applications");
|
||||
details.setSpan(new AbsoluteSizeSpan(16, true), 0, details.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
final String url = "https://github.com/bluelinelabs/Conductor";
|
||||
SpannableString link = new SpannableString(url);
|
||||
link.setSpan(new URLSpan(url) {
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
|
||||
}
|
||||
}, 0, link.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
SpannableStringBuilder content = new SpannableStringBuilder();
|
||||
content.append("Conductor");
|
||||
content.append("\n\n");
|
||||
content.append(details);
|
||||
content.append("\n\n");
|
||||
content.append(link);
|
||||
|
||||
addChildController(ChildControllerTransaction.builder(new OverlayController(content), R.id.home_root)
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler())
|
||||
.addToLocalBackstack(true)
|
||||
.build());
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Conductor Demos";
|
||||
}
|
||||
|
||||
void onModelRowClick(HomeDemoModel model) {
|
||||
switch (model) {
|
||||
case NAVIGATION:
|
||||
@@ -91,7 +159,7 @@ public class HomeController extends RefWatchingController {
|
||||
.build());
|
||||
break;
|
||||
case OVERLAY:
|
||||
addChildController(ChildControllerTransaction.builder(new OverlayController(), R.id.home_root)
|
||||
addChildController(ChildControllerTransaction.builder(new OverlayController("I'm an Overlay!"), R.id.home_root)
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler())
|
||||
.addToLocalBackstack(true)
|
||||
|
||||
+10
-5
@@ -9,15 +9,15 @@ import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.BundleBuilder;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.RefWatchingController;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.BundleBuilder;
|
||||
import com.bluelinelabs.conductor.demo.util.ColorUtil;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class NavigationDemoController extends RefWatchingController {
|
||||
public class NavigationDemoController extends BaseController {
|
||||
|
||||
public static final String TAG_UP_TRANSACTION = "NavigationDemoController.up";
|
||||
|
||||
@@ -45,13 +45,18 @@ public class NavigationDemoController extends RefWatchingController {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindView(@NonNull View view) {
|
||||
super.onBindView(view);
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
||||
view.setBackgroundColor(ColorUtil.getMaterialColor(getResources(), mIndex));
|
||||
mTvTitle.setText(getResources().getString(R.string.navigation_title, mIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Navigation Demos";
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_next) void onNextClicked() {
|
||||
getRouter().pushController(RouterTransaction.builder(new NavigationDemoController(mIndex + 1))
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
|
||||
+21
-5
@@ -1,20 +1,35 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.RefWatchingController;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.BundleBuilder;
|
||||
|
||||
import butterknife.Bind;
|
||||
|
||||
public class OverlayController extends RefWatchingController {
|
||||
public class OverlayController extends BaseController {
|
||||
|
||||
private static final String KEY_TEXT = "OverlayController.text";
|
||||
|
||||
@Bind(R.id.text_view) TextView mTextView;
|
||||
|
||||
public OverlayController(CharSequence text) {
|
||||
this(new BundleBuilder(new Bundle())
|
||||
.putCharSequence(KEY_TEXT, text)
|
||||
.build());
|
||||
}
|
||||
|
||||
public OverlayController(Bundle args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
@@ -22,9 +37,10 @@ public class OverlayController extends RefWatchingController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindView(@NonNull View view) {
|
||||
super.onBindView(view);
|
||||
mTextView.setText("I'm an Overlay");
|
||||
public void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
mTextView.setText(getArgs().getCharSequence(KEY_TEXT));
|
||||
mTextView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+39
-15
@@ -1,6 +1,10 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.design.widget.TabLayout;
|
||||
import android.support.design.widget.TabLayout.OnTabSelectedListener;
|
||||
import android.support.design.widget.TabLayout.Tab;
|
||||
import android.support.design.widget.TabLayout.TabLayoutOnPageChangeListener;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -8,43 +12,58 @@ import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.RefWatchingController;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.support.ControllerPagerAdapter;
|
||||
|
||||
import butterknife.Bind;
|
||||
|
||||
public class PagerController extends RefWatchingController {
|
||||
public class PagerController extends BaseController {
|
||||
|
||||
private int[] PAGE_COLORS = new int[]{R.color.green_300, R.color.cyan_300, R.color.deep_purple_300, R.color.lime_300, R.color.red_300};
|
||||
|
||||
@Bind(R.id.tab_layout) TabLayout mTabLayout;
|
||||
@Bind(R.id.view_pager) ViewPager mViewPager;
|
||||
|
||||
private ControllerPagerAdapter mPagerAdapter;
|
||||
|
||||
public PagerController() {
|
||||
mPagerAdapter = new ControllerPagerAdapter(this) {
|
||||
@Override
|
||||
public Controller getItem(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return new ChildController("Child #1 (Swipe to see more)", R.color.cyan_300, true);
|
||||
case 1:
|
||||
return new ChildController("Child #2 (Swipe to see more)", R.color.deep_purple_300, true);
|
||||
case 2:
|
||||
return new ChildController("Child #3 (Swipe to see more)", R.color.lime_300, true);
|
||||
default:
|
||||
throw new RuntimeException("Invalid item position: " + position);
|
||||
}
|
||||
return new ChildController(String.format("Child #%d (Swipe to see more)", position), PAGE_COLORS[position], true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 3;
|
||||
return PAGE_COLORS.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return "Page " + position;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindView(@NonNull View view) {
|
||||
super.onBindView(view);
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
mTabLayout.setTabsFromPagerAdapter(mPagerAdapter);
|
||||
mViewPager.setAdapter(mPagerAdapter);
|
||||
|
||||
mViewPager.addOnPageChangeListener(new TabLayoutOnPageChangeListener(mTabLayout));
|
||||
mTabLayout.setOnTabSelectedListener(new OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(Tab tab) {
|
||||
mViewPager.setCurrentItem(tab.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(Tab tab) { }
|
||||
|
||||
@Override
|
||||
public void onTabReselected(Tab tab) { }
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -53,4 +72,9 @@ public class PagerController extends RefWatchingController {
|
||||
return inflater.inflate(R.layout.controller_pager, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "ViewPager Demo";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+38
-24
@@ -11,12 +11,10 @@ import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.ControllerTransaction.ControllerChangeType;
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.RefWatchingController;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.ColorUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ParentController extends RefWatchingController {
|
||||
public class ParentController extends BaseController {
|
||||
|
||||
private static final int NUMBER_OF_CHILDREN = 5;
|
||||
private boolean mFinishing;
|
||||
@@ -37,15 +35,43 @@ public class ParentController extends RefWatchingController {
|
||||
}
|
||||
|
||||
private void addChild(final int index) {
|
||||
int frameId = getResources().getIdentifier("child_content_" + (index + 1), "id", getActivity().getPackageName());
|
||||
String tag = Integer.toString(index);
|
||||
|
||||
ChildController childController = new ChildController("Child Controller #" + index, ColorUtil.getMaterialColor(getResources(), index), false);
|
||||
addChildController(ChildControllerTransaction.builder(childController, frameId)
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler())
|
||||
.build());
|
||||
if (getChildController(tag) == null) {
|
||||
int frameId = getResources().getIdentifier("child_content_" + (index + 1), "id", getActivity().getPackageName());
|
||||
|
||||
childController.addLifecycleListener(new LifecycleListener() {
|
||||
ChildController childController = new ChildController("Child Controller #" + index, ColorUtil.getMaterialColor(getResources(), index), false);
|
||||
addChildController(ChildControllerTransaction.builder(childController, frameId)
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler())
|
||||
.tag(tag)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
private void removeChild(int index) {
|
||||
removeChildController(getChildControllers().get(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleBack() {
|
||||
if (getChildControllers().size() == NUMBER_OF_CHILDREN && !mFinishing) {
|
||||
mFinishing = true;
|
||||
removeChild(getChildControllers().size() - 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Parent/Child Demo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChildController(ChildControllerTransaction transaction) {
|
||||
final int index = Integer.parseInt(transaction.tag);
|
||||
|
||||
transaction.controller.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
if (changeType == ControllerChangeType.PUSH_ENTER && index < NUMBER_OF_CHILDREN - 1) {
|
||||
@@ -59,19 +85,7 @@ public class ParentController extends RefWatchingController {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeChild(int index) {
|
||||
List<Controller> childControllers = getChildControllers();
|
||||
removeChildController(childControllers.get(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleBack() {
|
||||
if (getChildControllers().size() == NUMBER_OF_CHILDREN && !mFinishing) {
|
||||
mFinishing = true;
|
||||
removeChild(getChildControllers().size() - 1);
|
||||
}
|
||||
return true;
|
||||
super.addChildController(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-14
@@ -10,7 +10,7 @@ import android.widget.TextView;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.RefWatchingController;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.rxlifecycle.ControllerEvent;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -23,7 +23,7 @@ import rx.functions.Action1;
|
||||
|
||||
// Shamelessly borrowed from the official RxLifecycle demo by Trello and adapted for Conductor Controllers
|
||||
// instead of Activities or Fragments.
|
||||
public class RxLifecycleController extends RefWatchingController {
|
||||
public class RxLifecycleController extends BaseController {
|
||||
|
||||
private static final String TAG = "RxLifecycleController";
|
||||
|
||||
@@ -48,10 +48,8 @@ public class RxLifecycleController extends RefWatchingController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindView(@NonNull View view) {
|
||||
super.onBindView(view);
|
||||
|
||||
Log.i(TAG, "onBindView() called");
|
||||
public void onViewBound(@NonNull View view) {
|
||||
Log.i(TAG, "onCreateView() called");
|
||||
|
||||
mTvTitle.setText(getResources().getString(R.string.rxlifecycle_title, TAG));
|
||||
|
||||
@@ -59,14 +57,14 @@ public class RxLifecycleController extends RefWatchingController {
|
||||
.doOnUnsubscribe(new Action0() {
|
||||
@Override
|
||||
public void call() {
|
||||
Log.i(TAG, "Unsubscribing from onBindView()");
|
||||
Log.i(TAG, "Unsubscribing from onCreateView)");
|
||||
}
|
||||
})
|
||||
.compose(this.<Long>bindUntilEvent(ControllerEvent.UNBIND_VIEW))
|
||||
.compose(this.<Long>bindUntilEvent(ControllerEvent.DESTROY_VIEW))
|
||||
.subscribe(new Action1<Long>() {
|
||||
@Override
|
||||
public void call(Long num) {
|
||||
Log.i(TAG, "Started in onBindView(), running until onUnbindView(): " + num);
|
||||
Log.i(TAG, "Started in onCreateView(), running until onDestroyView(): " + num);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -94,10 +92,10 @@ public class RxLifecycleController extends RefWatchingController {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUnbindView(View view) {
|
||||
super.onUnbindView(view);
|
||||
protected void onDestroyView(View view) {
|
||||
super.onDestroyView(view);
|
||||
|
||||
Log.i(TAG, "onUnbindView() called");
|
||||
Log.i(TAG, "onDestroyView() called");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -114,6 +112,11 @@ public class RxLifecycleController extends RefWatchingController {
|
||||
Log.i(TAG, "onDestroy() called");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "RxLifecycle Demo";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
@@ -123,7 +126,7 @@ public class RxLifecycleController extends RefWatchingController {
|
||||
@OnClick(R.id.btn_next_release_view) void onNextWithReleaseClicked() {
|
||||
setRetainViewMode(RetainViewMode.RELEASE_DETACH);
|
||||
|
||||
getRouter().pushController(RouterTransaction.builder(new TextController("Logcat should now report that the observables from onAttach() and onBindView() have been unsubscribed from, while the constructor observable is still running."))
|
||||
getRouter().pushController(RouterTransaction.builder(new TextController("Logcat should now report that the observables from onAttach() and onViewBound() have been unsubscribed from, while the constructor observable is still running."))
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new HorizontalChangeHandler())
|
||||
.build()
|
||||
@@ -133,7 +136,7 @@ public class RxLifecycleController extends RefWatchingController {
|
||||
@OnClick(R.id.btn_next_retain_view) void onNextWithRetainClicked() {
|
||||
setRetainViewMode(RetainViewMode.RETAIN_DETACH);
|
||||
|
||||
getRouter().pushController(RouterTransaction.builder(new TextController("Logcat should now report that the observables from onAttach() has been unsubscribed from, while the constructor and onBindView() observables are still running."))
|
||||
getRouter().pushController(RouterTransaction.builder(new TextController("Logcat should now report that the observables from onAttach() has been unsubscribed from, while the constructor and onViewBound() observables are still running."))
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new HorizontalChangeHandler())
|
||||
.build()
|
||||
|
||||
+9
-4
@@ -17,12 +17,12 @@ import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.TargetTitleEntryController.TargetTitleEntryControllerListener;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.RefWatchingController;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class TargetDisplayController extends RefWatchingController implements TargetTitleEntryControllerListener {
|
||||
public class TargetDisplayController extends BaseController implements TargetTitleEntryControllerListener {
|
||||
|
||||
private static final int REQUEST_SELECT_IMAGE = 126;
|
||||
|
||||
@@ -70,8 +70,8 @@ public class TargetDisplayController extends RefWatchingController implements Ta
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindView(@NonNull View view) {
|
||||
super.onBindView(view);
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
setTextView();
|
||||
setImageView();
|
||||
}
|
||||
@@ -94,6 +94,11 @@ public class TargetDisplayController extends RefWatchingController implements Ta
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Target Controller Demo";
|
||||
}
|
||||
|
||||
private void setImageView() {
|
||||
mImageView.setImageURI(mImageUri);
|
||||
}
|
||||
|
||||
+7
-11
@@ -8,12 +8,12 @@ import android.widget.EditText;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.RefWatchingController;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class TargetTitleEntryController extends RefWatchingController {
|
||||
public class TargetTitleEntryController extends BaseController {
|
||||
|
||||
public interface TargetTitleEntryControllerListener {
|
||||
void onTitlePicked(String option);
|
||||
@@ -21,12 +21,12 @@ public class TargetTitleEntryController extends RefWatchingController {
|
||||
|
||||
@Bind(R.id.edit_text) EditText mEditText;
|
||||
|
||||
public TargetTitleEntryController() { }
|
||||
|
||||
public TargetTitleEntryController(Controller targetController) {
|
||||
public <T extends Controller & TargetTitleEntryControllerListener> TargetTitleEntryController(T targetController) {
|
||||
setTargetController(targetController);
|
||||
}
|
||||
|
||||
public TargetTitleEntryController() { }
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
@@ -34,12 +34,8 @@ public class TargetTitleEntryController extends RefWatchingController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTargetControllerSet(Controller target) {
|
||||
super.onTargetControllerSet(target);
|
||||
|
||||
if (!(target instanceof TargetTitleEntryControllerListener)) {
|
||||
throw new RuntimeException(getClass().getSimpleName() + " target Controllers must implement the " + TargetTitleEntryControllerListener.class.getSimpleName() + " interface.");
|
||||
}
|
||||
protected String getTitle() {
|
||||
return "Target Controller Demo";
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_use_title) void optionPicked() {
|
||||
|
||||
@@ -7,13 +7,13 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.demo.BundleBuilder;
|
||||
import com.bluelinelabs.conductor.demo.util.BundleBuilder;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.RefWatchingController;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
|
||||
import butterknife.Bind;
|
||||
|
||||
public class TextController extends RefWatchingController {
|
||||
public class TextController extends BaseController {
|
||||
|
||||
private static final String KEY_TEXT = "TextController.text";
|
||||
|
||||
@@ -37,8 +37,8 @@ public class TextController extends RefWatchingController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindView(@NonNull View view) {
|
||||
super.onBindView(view);
|
||||
public void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
mTextView.setText(getArgs().getString(KEY_TEXT));
|
||||
}
|
||||
|
||||
|
||||
+18
-13
@@ -15,27 +15,27 @@ import android.widget.TextView;
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.CircularRevealChangeHandler;
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.BundleBuilder;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.changehandler.ArcFadeMoveChangeHandlerCompat;
|
||||
import com.bluelinelabs.conductor.demo.changehandler.CircularRevealChangeHandlerCompat;
|
||||
import com.bluelinelabs.conductor.demo.changehandler.FlipChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.RefWatchingController;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.BundleBuilder;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class TransitionDemoController extends RefWatchingController {
|
||||
public class TransitionDemoController extends BaseController {
|
||||
|
||||
private static final String KEY_INDEX = "TransitionDemoController.index";
|
||||
|
||||
public enum TransitionDemo {
|
||||
VERTICAL("Vertical Slide Animation", R.layout.controller_transition_demo, R.color.blue_grey_300),
|
||||
CIRCULAR("Circular Reveal Animation", R.layout.controller_transition_demo, R.color.red_300),
|
||||
CIRCULAR("Circular Reveal Animation (on Lollipop and above, else Fade)", R.layout.controller_transition_demo, R.color.red_300),
|
||||
FADE("Fade Animation", R.layout.controller_transition_demo, R.color.blue_300),
|
||||
FLIP("Flip Animation", R.layout.controller_transition_demo, R.color.deep_orange_300),
|
||||
HORIZONTAL("Horizontal Slide Animation", R.layout.controller_transition_demo, R.color.green_300),
|
||||
@@ -58,7 +58,7 @@ public class TransitionDemoController extends RefWatchingController {
|
||||
|
||||
@Bind(R.id.tv_title) TextView mTvTitle;
|
||||
@Bind(R.id.btn_next) FloatingActionButton mBtnNext;
|
||||
View mContainerView;
|
||||
@Bind(R.id.transition_root) View mContainerView;
|
||||
|
||||
private TransitionDemo mTransitionDemo;
|
||||
|
||||
@@ -73,9 +73,15 @@ public class TransitionDemoController extends RefWatchingController {
|
||||
mTransitionDemo = TransitionDemo.fromIndex(args.getInt(KEY_INDEX));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected void onBindView(@NonNull View view) {
|
||||
super.onBindView(view);
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(mTransitionDemo.layoutId, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
||||
View bgView = ButterKnife.findById(view, R.id.bg_view);
|
||||
if (mTransitionDemo.colorId != 0 && bgView != null) {
|
||||
@@ -92,14 +98,12 @@ public class TransitionDemoController extends RefWatchingController {
|
||||
}
|
||||
|
||||
mBtnNext.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(getActivity(), buttonColor)));
|
||||
mContainerView = view;
|
||||
mTvTitle.setText(mTransitionDemo.title);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(mTransitionDemo.layoutId, container, false);
|
||||
protected String getTitle() {
|
||||
return "Transition Demos";
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_next) void onNextClicked() {
|
||||
@@ -118,7 +122,7 @@ public class TransitionDemoController extends RefWatchingController {
|
||||
return new VerticalChangeHandler();
|
||||
case CIRCULAR:
|
||||
TransitionDemoController demoController = (TransitionDemoController)from;
|
||||
return new CircularRevealChangeHandler(demoController.mBtnNext, demoController.mContainerView);
|
||||
return new CircularRevealChangeHandlerCompat(demoController.mBtnNext, demoController.mContainerView);
|
||||
case FADE:
|
||||
return new FadeChangeHandler();
|
||||
case FLIP:
|
||||
@@ -143,4 +147,5 @@ public class TransitionDemoController extends RefWatchingController {
|
||||
.popChangeHandler(changeHandler)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.view.View;
|
||||
|
||||
import com.bluelinelabs.conductor.demo.ActionBarProvider;
|
||||
|
||||
public abstract class BaseController extends RefWatchingController {
|
||||
|
||||
protected BaseController() { }
|
||||
|
||||
protected BaseController(Bundle args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
// Note: This is just a quick demo of how an ActionBar *can* be accessed, not necessarily how it *should*
|
||||
// be accessed. In a production app, this would use Dagger instead.
|
||||
protected ActionBar getActionBar() {
|
||||
ActionBarProvider actionBarProvider = ((ActionBarProvider)getActivity());
|
||||
return actionBarProvider != null ? actionBarProvider.getSupportActionBar() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttach(@NonNull View view) {
|
||||
setTitle();
|
||||
super.onAttach(view);
|
||||
}
|
||||
|
||||
protected void setTitle() {
|
||||
String title = getTitle();
|
||||
ActionBar actionBar = getActionBar();
|
||||
if (title != null && actionBar != null) {
|
||||
actionBar.setTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
protected String getTitle() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+14
-5
@@ -2,7 +2,9 @@ package com.bluelinelabs.conductor.demo.controllers.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.rxlifecycle.RxController;
|
||||
|
||||
@@ -15,16 +17,23 @@ public abstract class ButterKnifeController extends RxController {
|
||||
super(args);
|
||||
}
|
||||
|
||||
protected abstract View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container);
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected void onBindView(@NonNull View view) {
|
||||
super.onBindView(view);
|
||||
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
View view = inflateView(inflater, container);
|
||||
ButterKnife.bind(this, view);
|
||||
onViewBound(view);
|
||||
return view;
|
||||
}
|
||||
|
||||
protected void onViewBound(@NonNull View view) { }
|
||||
|
||||
@Override
|
||||
protected void onUnbindView(View view) {
|
||||
super.onUnbindView(view);
|
||||
protected void onDestroyView(View view) {
|
||||
super.onDestroyView(view);
|
||||
ButterKnife.unbind(this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+3
-16
@@ -1,10 +1,8 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
|
||||
import com.bluelinelabs.conductor.demo.App;
|
||||
import com.bluelinelabs.conductor.demo.DemoApplication;
|
||||
|
||||
public abstract class RefWatchingController extends ButterKnifeController {
|
||||
|
||||
@@ -13,21 +11,10 @@ public abstract class RefWatchingController extends ButterKnifeController {
|
||||
super(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetach(@NonNull View view) {
|
||||
super.onDetach(view);
|
||||
|
||||
if (isDestroyed()) {
|
||||
App.refWatcher.watch(view);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (getView() != null) {
|
||||
App.refWatcher.watch(getView());
|
||||
}
|
||||
App.refWatcher.watch(this);
|
||||
super.onDestroy();
|
||||
DemoApplication.refWatcher.watch(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.bluelinelabs.conductor.demo;
|
||||
package com.bluelinelabs.conductor.demo.util;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
+51
-37
@@ -16,13 +16,12 @@
|
||||
|
||||
package com.bluelinelabs.conductor.demo.widget;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.support.v4.view.NestedScrollingParent;
|
||||
import android.support.v4.view.animation.FastOutSlowInInterpolator;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -33,8 +32,30 @@ import java.util.List;
|
||||
* Applies an elasticity factor to reduce movement as you approach the given dismiss distance.
|
||||
* Optionally also scales down content during drag.
|
||||
*/
|
||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||
public class ElasticDragDismissFrameLayout extends FrameLayout {
|
||||
public class ElasticDragDismissFrameLayout extends FrameLayout implements NestedScrollingParent {
|
||||
|
||||
public static abstract class ElasticDragDismissCallback {
|
||||
|
||||
/**
|
||||
* Called for each drag event.
|
||||
*
|
||||
* @param elasticOffset Indicating the drag offset with elasticity applied i.e. may
|
||||
* exceed 1.
|
||||
* @param elasticOffsetPixels The elastically scaled drag distance in pixels.
|
||||
* @param rawOffset Value from [0, 1] indicating the raw drag offset i.e.
|
||||
* without elasticity applied. A value of 1 indicates that the
|
||||
* dismiss distance has been reached.
|
||||
* @param rawOffsetPixels The raw distance the user has dragged
|
||||
*/
|
||||
public void onDrag(float elasticOffset, float elasticOffsetPixels,
|
||||
float rawOffset, float rawOffsetPixels) { }
|
||||
|
||||
/**
|
||||
* Called when dragging is released and has exceeded the threshold dismiss distance.
|
||||
*/
|
||||
public void onDragDismissed() { }
|
||||
|
||||
}
|
||||
|
||||
// configurable attribs
|
||||
private float dragDismissDistance = Float.MAX_VALUE;
|
||||
@@ -51,21 +72,16 @@ public class ElasticDragDismissFrameLayout extends FrameLayout {
|
||||
private List<ElasticDragDismissCallback> callbacks;
|
||||
|
||||
public ElasticDragDismissFrameLayout(Context context) {
|
||||
this(context, null, 0, 0);
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0, 0);
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
this(context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs,
|
||||
int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
dragDismissDistance = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics());
|
||||
dragDismissFraction = 0.7f;
|
||||
@@ -73,29 +89,6 @@ public class ElasticDragDismissFrameLayout extends FrameLayout {
|
||||
shouldScale = true;
|
||||
}
|
||||
|
||||
public static abstract class ElasticDragDismissCallback {
|
||||
|
||||
/**
|
||||
* Called for each drag event.
|
||||
*
|
||||
* @param elasticOffset Indicating the drag offset with elasticity applied i.e. may
|
||||
* exceed 1.
|
||||
* @param elasticOffsetPixels The elastically scaled drag distance in pixels.
|
||||
* @param rawOffset Value from [0, 1] indicating the raw drag offset i.e.
|
||||
* without elasticity applied. A value of 1 indicates that the
|
||||
* dismiss distance has been reached.
|
||||
* @param rawOffsetPixels The raw distance the user has dragged
|
||||
*/
|
||||
public void onDrag(float elasticOffset, float elasticOffsetPixels,
|
||||
float rawOffset, float rawOffsetPixels) { }
|
||||
|
||||
/**
|
||||
* Called when dragging is released and has exceeded the threshold dismiss distance.
|
||||
*/
|
||||
public void onDragDismissed() { }
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
|
||||
return (nestedScrollAxes & View.SCROLL_AXIS_VERTICAL) != 0;
|
||||
@@ -126,7 +119,7 @@ public class ElasticDragDismissFrameLayout extends FrameLayout {
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.setDuration(200L)
|
||||
.setInterpolator(AnimationUtils.loadInterpolator(getContext(), android.R.interpolator.fast_out_slow_in))
|
||||
.setInterpolator(new FastOutSlowInInterpolator())
|
||||
.setListener(null)
|
||||
.start();
|
||||
totalDrag = 0;
|
||||
@@ -135,6 +128,24 @@ public class ElasticDragDismissFrameLayout extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNestedScrollAxes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNestedScrollAccepted(View child, View target, int axes) { }
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
@@ -171,6 +182,9 @@ public class ElasticDragDismissFrameLayout extends FrameLayout {
|
||||
draggingUp = true;
|
||||
if (shouldScale) setPivotY(0f);
|
||||
}
|
||||
|
||||
setPivotX(getWidth() / 2);
|
||||
|
||||
// how far have we dragged relative to the distance to perform a dismiss
|
||||
// (0–1 where 1 = dismiss distance). Decreasing logarithmically as we approach the limit
|
||||
float dragFraction = (float) Math.log10(1 + (Math.abs(totalDrag) / dragDismissDistance));
|
||||
|
||||
@@ -1,9 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.bluelinelabs.conductor.ChangeHandlerFrameLayout
|
||||
android:id="@+id/controller_container"
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.bluelinelabs.conductor.demo.MainActivity"
|
||||
/>
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="com.bluelinelabs.conductor.demo.MainActivity">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
/>
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<com.bluelinelabs.conductor.ChangeHandlerFrameLayout
|
||||
android:id="@+id/controller_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
/>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
|
||||
@@ -14,17 +14,16 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:textSize="28sp"
|
||||
android:textSize="22sp"
|
||||
android:padding="16dp"
|
||||
android:text="@string/drag_to_dismiss"
|
||||
/>
|
||||
|
||||
<ScrollView
|
||||
<android.support.v4.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:fillViewport="true"
|
||||
android:nestedScrollingEnabled="true"
|
||||
android:overScrollFooter="@android:color/transparent"
|
||||
android:overScrollMode="never" >
|
||||
|
||||
@@ -36,7 +35,7 @@
|
||||
android:padding="16dp"
|
||||
/>
|
||||
|
||||
</ScrollView>
|
||||
</android.support.v4.widget.NestedScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
</com.bluelinelabs.conductor.demo.widget.ElasticDragDismissFrameLayout>
|
||||
@@ -4,20 +4,21 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#88000000"
|
||||
android:clickable="true" >
|
||||
android:clickable="true">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="32dp"
|
||||
android:background="@android:color/white" >
|
||||
android:background="@android:color/white">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:padding="16dp"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/green_300"
|
||||
android:orientation="vertical" >
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
<android.support.design.widget.TabLayout
|
||||
android:id="@+id/tab_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:textSize="28sp"
|
||||
android:padding="16dp"
|
||||
android:text="@string/view_pager"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:elevation="6dp"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
/>
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:ignore="AlwaysShowAction">
|
||||
|
||||
<item
|
||||
android:id="@+id/about"
|
||||
android:title="@string/about"
|
||||
app:showAsAction="always"
|
||||
/>
|
||||
|
||||
</menu>
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<color name="colorPrimary">#78909c</color>
|
||||
<color name="colorPrimaryDark">#37474f</color>
|
||||
<color name="colorPrimary">#263239</color>
|
||||
<color name="colorPrimaryDark">#21272c</color>
|
||||
<color name="colorAccent">#1976d2</color>
|
||||
|
||||
<color name="white">@android:color/white</color>
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
<resources>
|
||||
<string name="app_name">Conductor Demo</string>
|
||||
|
||||
<!-- Home Controller -->
|
||||
<string name="about">About</string>
|
||||
|
||||
<!-- Navigation Demo -->
|
||||
<string name="pop_to_root">Pop to Root</string>
|
||||
<string name="go_up">Go Up</string>
|
||||
@@ -13,9 +16,6 @@
|
||||
<string name="transition_tag_dot">transition.dot</string>
|
||||
<string name="transition_tag_title">transition.title</string>
|
||||
|
||||
<!-- View Pager Demo -->
|
||||
<string name="view_pager">View Pager</string>
|
||||
|
||||
<!-- Parent Controller Demo -->
|
||||
<string name="parent_controller">Parent Controller</string>
|
||||
<string name="go_to_next_parent">Go to next parent</string>
|
||||
@@ -27,7 +27,7 @@
|
||||
<string name="use_title">Use Title</string>
|
||||
|
||||
<!-- Drag Dismiss Demo -->
|
||||
<string name="drag_to_dismiss">Drag to Dismiss</string>
|
||||
<string name="drag_to_dismiss">Elastic scrolling view - scroll past bounds to dismiss.</string>
|
||||
|
||||
<!-- RxLifecycle Demo -->
|
||||
<string name="rxlifecycle_title">Watch tag %1$s on logcat for a demo.</string>
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="android:windowBackground">@color/white</item>
|
||||
<item name="android:textViewStyle">@style/TextView</item>
|
||||
<item name="android:textViewStyle">@style/AppTheme.TextView</item>
|
||||
</style>
|
||||
|
||||
<style name="TextView" parent="@android:style/Widget.TextView">
|
||||
<style name="AppTheme.TextView" parent="@android:style/Widget.TextView">
|
||||
<item name="android:textSize">20sp</item>
|
||||
</style>
|
||||
|
||||
|
||||
+8
-5
@@ -5,8 +5,8 @@ ext {
|
||||
buildToolsVersion = '23.0.2'
|
||||
|
||||
versionCode = 1
|
||||
versionName = '1.0.2'
|
||||
publishedVersionName = '1.0.2'
|
||||
versionName = '1.1.3'
|
||||
publishedVersionName = '1.1.3'
|
||||
|
||||
supportV4 = 'com.android.support:support-v4:23.1.1'
|
||||
supportDesign = 'com.android.support:design:23.1.1'
|
||||
@@ -15,8 +15,8 @@ ext {
|
||||
|
||||
butterknife = 'com.jakewharton:butterknife:7.0.1'
|
||||
|
||||
leakCanary = 'com.squareup.leakcanary:leakcanary-android:1.4-beta1'
|
||||
leakCanaryNoOp = 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
|
||||
leakCanary = 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
|
||||
leakCanaryNoOp = 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
|
||||
|
||||
rxJava = 'io.reactivex:rxjava:1.1.0'
|
||||
rxAndroid = 'io.reactivex:rxandroid:1.1.0'
|
||||
@@ -24,4 +24,7 @@ ext {
|
||||
|
||||
junit = 'junit:junit:4.11'
|
||||
roboelectric = 'org.robolectric:robolectric:3.0'
|
||||
}
|
||||
|
||||
lintapi = 'com.android.tools.lint:lint-api:24.5.0'
|
||||
lintchecks = 'com.android.tools.lint:lint-checks:24.5.0'
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 103 KiB |
+5
-1
@@ -1 +1,5 @@
|
||||
include ':conductor', ':conductor-support', ':conductor-rxlifecycle', ':demo'
|
||||
include ':conductor'
|
||||
include':conductor-support'
|
||||
include':conductor-rxlifecycle'
|
||||
include':conductor-lint'
|
||||
include':demo'
|
||||
Reference in New Issue
Block a user