Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e028ed42da | |||
| 43dc561ac2 | |||
| 217b55090a | |||
| 5c8b78e41d | |||
| 899dd70d50 | |||
| d945571d31 | |||
| b7a4386d22 | |||
| b117307340 | |||
| b8ccf3623f | |||
| 495145b72b | |||
| 99e25d65f2 | |||
| 2619d13c8d | |||
| 62a5a81107 | |||
| d234dd4c75 | |||
| a48b49cdbe |
@@ -20,14 +20,14 @@ Conductor is architecture-agnostic and does not try to force any design decision
|
||||
## Installation
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:1.1.2'
|
||||
compile 'com.bluelinelabs:conductor:1.1.4'
|
||||
|
||||
// If you want the components that go along with
|
||||
// Android's support libraries (currently just a PagerAdapter):
|
||||
compile 'com.bluelinelabs:conductor-support:1.1.2'
|
||||
compile 'com.bluelinelabs:conductor-support:1.1.4'
|
||||
|
||||
// If you want RxJava/RxAndroid lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:1.1.2'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:1.1.4'
|
||||
```
|
||||
|
||||
## Components to Know
|
||||
|
||||
+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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,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 +54,7 @@ public abstract class Controller {
|
||||
private final Bundle mArgs;
|
||||
|
||||
private Bundle mViewState;
|
||||
private Bundle mSavedInstanceState;
|
||||
private boolean mIsBeingDestroyed;
|
||||
private boolean mDestroyed;
|
||||
private boolean mAttached;
|
||||
@@ -65,12 +67,14 @@ public abstract class Controller {
|
||||
private String mInstanceId;
|
||||
private String mTargetInstanceId;
|
||||
private boolean mNeedsAttach;
|
||||
private boolean mHasSavedViewState;
|
||||
private ControllerChangeHandler mOverriddenPushHandler;
|
||||
private ControllerChangeHandler mOverriddenPopHandler;
|
||||
private RetainViewMode mRetainViewMode = RetainViewMode.RELEASE_DETACH;
|
||||
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);
|
||||
@@ -400,22 +404,40 @@ public abstract class Controller {
|
||||
/**
|
||||
* Calls startActivity(Intent) from this Controller's host Activity.
|
||||
*/
|
||||
public final void startActivity(Intent intent) {
|
||||
getRouter().getLifecycleHandler().startActivity(intent);
|
||||
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); }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -434,9 +456,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); }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -623,10 +648,29 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
final void setRouter(@NonNull Router router) {
|
||||
mRouter = router;
|
||||
if (mRouter != router) {
|
||||
mRouter = router;
|
||||
|
||||
for (ChildControllerTransaction child : mChildControllers) {
|
||||
child.controller.setRouter(router);
|
||||
performOnRestoreInstanceState();
|
||||
|
||||
for (RouterRequiringFunc listener : mOnRouterSetListeners) {
|
||||
listener.execute();
|
||||
}
|
||||
mOnRouterSetListeners.clear();
|
||||
|
||||
for (ChildControllerTransaction child : mChildControllers) {
|
||||
child.controller.setRouter(router);
|
||||
}
|
||||
} else {
|
||||
performOnRestoreInstanceState();
|
||||
}
|
||||
}
|
||||
|
||||
final void executeWithRouter(@NonNull RouterRequiringFunc listener) {
|
||||
if (mRouter != null) {
|
||||
listener.execute();
|
||||
} else {
|
||||
mOnRouterSetListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -703,6 +747,8 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
private void attach(@NonNull View view) {
|
||||
mHasSavedViewState = false;
|
||||
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.preAttach(this, view);
|
||||
}
|
||||
@@ -761,7 +807,7 @@ public abstract class Controller {
|
||||
|
||||
private void removeViewReference() {
|
||||
if (mView != null) {
|
||||
if (!mIsBeingDestroyed) {
|
||||
if (!mIsBeingDestroyed && !mHasSavedViewState) {
|
||||
saveViewState(mView);
|
||||
}
|
||||
|
||||
@@ -825,6 +871,10 @@ public abstract class Controller {
|
||||
|
||||
mDestroyed = true;
|
||||
|
||||
if (mRouter != null) {
|
||||
mRouter.getLifecycleHandler().unregisterForActivityRequests(mInstanceId);
|
||||
}
|
||||
|
||||
onDestroy();
|
||||
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
@@ -852,6 +902,8 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
final void saveViewState(@NonNull View view) {
|
||||
mHasSavedViewState = true;
|
||||
|
||||
mViewState = new Bundle();
|
||||
|
||||
SparseArray<Parcelable> hierarchyState = new SparseArray<>();
|
||||
@@ -867,6 +919,10 @@ public abstract class Controller {
|
||||
child.controller.saveViewState(child.controller.mView);
|
||||
}
|
||||
}
|
||||
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.onSaveViewState(this, mViewState);
|
||||
}
|
||||
}
|
||||
|
||||
final void restoreViewState(@NonNull View view) {
|
||||
@@ -879,6 +935,10 @@ public abstract class Controller {
|
||||
child.controller.restoreViewState(child.controller.mView);
|
||||
}
|
||||
}
|
||||
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.onRestoreViewState(this, mViewState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,8 +947,12 @@ public abstract class Controller {
|
||||
detach(mView, mIsBeingDestroyed);
|
||||
}
|
||||
|
||||
if (!mHasSavedViewState && mView != null) {
|
||||
saveViewState(mView);
|
||||
}
|
||||
|
||||
Bundle outState = new Bundle();
|
||||
outState.putString(KEY_CLASS_NAME, getClass().getCanonicalName());
|
||||
outState.putString(KEY_CLASS_NAME, getClass().getName());
|
||||
outState.putBundle(KEY_VIEW_STATE, mViewState);
|
||||
outState.putBundle(KEY_ARGS, mArgs);
|
||||
outState.putString(KEY_INSTANCE_ID, mInstanceId);
|
||||
@@ -935,11 +999,19 @@ public abstract class Controller {
|
||||
addChildController(new ChildControllerTransaction(childBundle));
|
||||
}
|
||||
|
||||
Bundle savedState = savedInstanceState.getBundle(KEY_SAVED_STATE);
|
||||
onRestoreInstanceState(savedState);
|
||||
mSavedInstanceState = savedInstanceState.getBundle(KEY_SAVED_STATE);
|
||||
performOnRestoreInstanceState();
|
||||
}
|
||||
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.onRestoreInstanceState(this, savedState);
|
||||
private void performOnRestoreInstanceState() {
|
||||
if (mSavedInstanceState != null && mRouter != null) {
|
||||
onRestoreInstanceState(mSavedInstanceState);
|
||||
|
||||
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
|
||||
lifecycleListener.onRestoreInstanceState(this, mSavedInstanceState);
|
||||
}
|
||||
|
||||
mSavedInstanceState = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1034,6 +1106,9 @@ public abstract class Controller {
|
||||
|
||||
public void onSaveInstanceState(@NonNull Controller controller, @NonNull Bundle outState) { }
|
||||
public void onRestoreInstanceState(@NonNull Controller controller, @NonNull Bundle savedInstanceState) { }
|
||||
|
||||
public void onSaveViewState(@NonNull Controller controller, @NonNull Bundle outState) { }
|
||||
public void onRestoreViewState(@NonNull Controller controller, @NonNull Bundle savedViewState) { }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public abstract class ControllerChangeHandler {
|
||||
|
||||
final Bundle toBundle() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(KEY_CLASS_NAME, getClass().getCanonicalName());
|
||||
bundle.putString(KEY_CLASS_NAME, getClass().getName());
|
||||
|
||||
Bundle savedState = new Bundle();
|
||||
saveToBundle(savedState);
|
||||
|
||||
@@ -41,19 +41,15 @@ public class Router {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -350,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);
|
||||
@@ -400,6 +403,11 @@ public class Router {
|
||||
|
||||
public final void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
mBackStack.restoreInstanceState(savedInstanceState);
|
||||
|
||||
Iterator<RouterTransaction> backstackIterator = mBackStack.reverseIterator();
|
||||
while (backstackIterator.hasNext()) {
|
||||
backstackIterator.next().controller.setRouter(this);
|
||||
}
|
||||
}
|
||||
|
||||
public final void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
|
||||
+2
-2
@@ -50,8 +50,8 @@ public class TransitionChangeHandlerCompat extends ControllerChangeHandler {
|
||||
public void saveToBundle(@NonNull Bundle bundle) {
|
||||
super.saveToBundle(bundle);
|
||||
|
||||
bundle.putString(KEY_TRANSITION_HANDLER_CLASS, mTransitionChangeHandler.getClass().getCanonicalName());
|
||||
bundle.putString(KEY_FALLBACK_HANDLER_CLASS, mFallbackChangeHandler.getClass().getCanonicalName());
|
||||
bundle.putString(KEY_TRANSITION_HANDLER_CLASS, mTransitionChangeHandler.getClass().getName());
|
||||
bundle.putString(KEY_FALLBACK_HANDLER_CLASS, mFallbackChangeHandler.getClass().getName());
|
||||
|
||||
Bundle transitionBundle = new Bundle();
|
||||
mTransitionChangeHandler.saveToBundle(transitionBundle);
|
||||
|
||||
@@ -61,13 +61,16 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
Router router = mRouterMap.get(getRouterHashKey(container));
|
||||
if (router == null) {
|
||||
router = new Router();
|
||||
router.setHost(this, container);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
router.onRestoreInstanceState(savedInstanceState);
|
||||
}
|
||||
mRouterMap.put(getRouterHashKey(container), router);
|
||||
} else {
|
||||
router.setHost(this, container);
|
||||
}
|
||||
|
||||
router.setHost(this, container);
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -188,13 +191,25 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public void startActivityForResult(String instanceId, Intent intent, int requestCode) {
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class CallState implements Parcelable {
|
||||
|
||||
public int changeStartCalls;
|
||||
public int changeEndCalls;
|
||||
public int createViewCalls;
|
||||
public int attachCalls;
|
||||
public int destroyViewCalls;
|
||||
public int detachCalls;
|
||||
public int destroyCalls;
|
||||
public int saveInstanceStateCalls;
|
||||
public int restoreInstanceStateCalls;
|
||||
public int saveViewStateCalls;
|
||||
public int restoreViewStateCalls;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CallState callState = (CallState)o;
|
||||
|
||||
if (changeStartCalls != callState.changeStartCalls) {
|
||||
return false;
|
||||
}
|
||||
if (changeEndCalls != callState.changeEndCalls) {
|
||||
return false;
|
||||
}
|
||||
if (createViewCalls != callState.createViewCalls) {
|
||||
return false;
|
||||
}
|
||||
if (attachCalls != callState.attachCalls) {
|
||||
return false;
|
||||
}
|
||||
if (destroyViewCalls != callState.destroyViewCalls) {
|
||||
return false;
|
||||
}
|
||||
if (detachCalls != callState.detachCalls) {
|
||||
return false;
|
||||
}
|
||||
if (destroyCalls != callState.destroyCalls) {
|
||||
return false;
|
||||
}
|
||||
if (saveInstanceStateCalls != callState.saveInstanceStateCalls) {
|
||||
return false;
|
||||
}
|
||||
if (saveViewStateCalls != callState.saveViewStateCalls) {
|
||||
return false;
|
||||
}
|
||||
if (restoreViewStateCalls != callState.restoreViewStateCalls) {
|
||||
return false;
|
||||
}
|
||||
return restoreInstanceStateCalls == callState.restoreInstanceStateCalls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = changeStartCalls;
|
||||
result = 31 * result + changeEndCalls;
|
||||
result = 31 * result + createViewCalls;
|
||||
result = 31 * result + attachCalls;
|
||||
result = 31 * result + destroyViewCalls;
|
||||
result = 31 * result + detachCalls;
|
||||
result = 31 * result + destroyCalls;
|
||||
result = 31 * result + saveInstanceStateCalls;
|
||||
result = 31 * result + restoreInstanceStateCalls;
|
||||
result = 31 * result + saveViewStateCalls;
|
||||
result = 31 * result + restoreViewStateCalls;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "\nCallState{" +
|
||||
"\n changeStartCalls=" + changeStartCalls +
|
||||
"\n changeEndCalls=" + changeEndCalls +
|
||||
"\n createViewCalls=" + createViewCalls +
|
||||
"\n attachCalls=" + attachCalls +
|
||||
"\n destroyViewCalls=" + destroyViewCalls +
|
||||
"\n detachCalls=" + detachCalls +
|
||||
"\n destroyCalls=" + destroyCalls +
|
||||
"\n saveInstanceStateCalls=" + saveInstanceStateCalls +
|
||||
"\n restoreInstanceStateCalls=" + restoreInstanceStateCalls +
|
||||
"\n saveViewStateCalls=" + saveViewStateCalls +
|
||||
"\n restoreViewStateCalls=" + restoreViewStateCalls +
|
||||
"}\n";
|
||||
}
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeInt(changeStartCalls);
|
||||
out.writeInt(changeEndCalls);
|
||||
out.writeInt(createViewCalls);
|
||||
out.writeInt(attachCalls);
|
||||
out.writeInt(destroyViewCalls);
|
||||
out.writeInt(detachCalls);
|
||||
out.writeInt(destroyCalls);
|
||||
out.writeInt(saveInstanceStateCalls);
|
||||
out.writeInt(restoreInstanceStateCalls);
|
||||
out.writeInt(saveViewStateCalls);
|
||||
out.writeInt(restoreViewStateCalls);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<CallState> CREATOR = new Parcelable.Creator<CallState>() {
|
||||
public CallState createFromParcel(Parcel in) {
|
||||
CallState state = new CallState();
|
||||
|
||||
state.changeStartCalls = in.readInt();
|
||||
state.changeEndCalls = in.readInt();
|
||||
state.createViewCalls = in.readInt();
|
||||
state.attachCalls = in.readInt();
|
||||
state.destroyViewCalls = in.readInt();
|
||||
state.detachCalls = in.readInt();
|
||||
state.destroyCalls = in.readInt();
|
||||
state.saveInstanceStateCalls = in.readInt();
|
||||
state.restoreInstanceStateCalls = in.readInt();
|
||||
state.saveViewStateCalls = in.readInt();
|
||||
state.restoreViewStateCalls = in.readInt();
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public CallState[] newArray(int size) {
|
||||
return new CallState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -24,78 +26,174 @@ import org.robolectric.util.ActivityController;
|
||||
@Config(manifest = Config.NONE)
|
||||
public class ControllerTests {
|
||||
|
||||
private Application mApplication;
|
||||
private ActivityController<TestActivity> mActivityController;
|
||||
private Router mRouter;
|
||||
|
||||
private int mChangeStartCalls;
|
||||
private int mChangeEndCalls;
|
||||
private int mCreateViewCalls;
|
||||
private int mAttachCalls;
|
||||
private int mDestroyViewCalls;
|
||||
private int mDetachCalls;
|
||||
private int mDestroyCalls;
|
||||
private CallState mCurrentCallState;
|
||||
|
||||
public void createActivityController(Bundle savedInstanceState) {
|
||||
mActivityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).withApplication(mApplication).start();
|
||||
|
||||
@IdRes int containerId = 4;
|
||||
FrameLayout routerContainer = new FrameLayout(mActivityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
|
||||
mRouter = Conductor.attachRouter(mActivityController.get(), routerContainer, savedInstanceState);
|
||||
if (!mRouter.hasRootController()) {
|
||||
mRouter.setRoot(new TestController());
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mActivityController = Robolectric.buildActivity(TestActivity.class).create();
|
||||
Activity activity = mActivityController.get();
|
||||
mRouter = Conductor.attachRouter(activity, new FrameLayout(activity), null);
|
||||
mRouter.setRoot(new TestController());
|
||||
mApplication = new Application();
|
||||
|
||||
mChangeStartCalls = 0;
|
||||
mChangeEndCalls = 0;
|
||||
mCreateViewCalls = 0;
|
||||
mAttachCalls = 0;
|
||||
mDestroyViewCalls = 0;
|
||||
mDestroyCalls = 0;
|
||||
mDestroyCalls = 0;
|
||||
createActivityController(null);
|
||||
|
||||
mCurrentCallState = new CallState();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalLifecycle() {
|
||||
Controller controller = new TestController();
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
assertCalls(0, 0, 0, 0, 0, 0, 0);
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
mRouter.pushController(RouterTransaction.builder(controller)
|
||||
.pushChangeHandler(getPushHandler(0, 0, 0, 0, 0, 0, 0))
|
||||
.popChangeHandler(getPopHandler(1, 1, 1, 1, 0, 0, 0))
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller))
|
||||
.popChangeHandler(getPopHandler(expectedCallState, controller))
|
||||
.build()
|
||||
);
|
||||
|
||||
assertCalls(1, 1, 1, 1, 0, 0, 0);
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mRouter.popCurrentController();
|
||||
|
||||
Assert.assertNull(controller.getView());
|
||||
|
||||
assertCalls(2, 2, 1, 1, 1, 1, 1);
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleWithActivityDestroy() {
|
||||
Controller controller = new TestController();
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
assertCalls(0, 0, 0, 0, 0, 0, 0);
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
mRouter.pushController(RouterTransaction.builder(controller)
|
||||
.pushChangeHandler(getPushHandler(0, 0, 0, 0, 0, 0, 0))
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller))
|
||||
.build()
|
||||
);
|
||||
|
||||
assertCalls(1, 1, 1, 1, 0, 0, 0);
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.pause();
|
||||
|
||||
assertCalls(1, 1, 1, 1, 0, 0, 0);
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.stop();
|
||||
|
||||
assertCalls(1, 1, 1, 1, 0, 0, 0);
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.destroy();
|
||||
|
||||
assertCalls(1, 1, 1, 1, 1, 1, 1);
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyViewCalls++;
|
||||
expectedCallState.destroyCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleWithActivityConfigurationChange() {
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
mRouter.pushController(RouterTransaction.builder(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller))
|
||||
.tag("root")
|
||||
.build()
|
||||
);
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.get().isChangingConfigurations = true;
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
mActivityController.saveInstanceState(bundle);
|
||||
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
expectedCallState.saveInstanceStateCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.pause();
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.stop();
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.destroy();
|
||||
expectedCallState.destroyViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
createActivityController(bundle);
|
||||
controller = (TestController)mRouter.getControllerWithTag("root");
|
||||
|
||||
expectedCallState.restoreInstanceStateCalls++;
|
||||
expectedCallState.restoreViewStateCalls++;
|
||||
expectedCallState.changeStartCalls++;
|
||||
expectedCallState.changeEndCalls++;
|
||||
expectedCallState.createViewCalls++;
|
||||
|
||||
// Lifecycle listener isn't attached during restore, grab the current views from the controller for this stuff...
|
||||
mCurrentCallState.restoreInstanceStateCalls = controller.currentCallState.restoreInstanceStateCalls;
|
||||
mCurrentCallState.restoreViewStateCalls = controller.currentCallState.restoreViewStateCalls;
|
||||
mCurrentCallState.changeStartCalls = controller.currentCallState.changeStartCalls;
|
||||
mCurrentCallState.changeEndCalls = controller.currentCallState.changeEndCalls;
|
||||
mCurrentCallState.createViewCalls = controller.currentCallState.createViewCalls;
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.resume();
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleWithActivityBackground() {
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
mRouter.pushController(RouterTransaction.builder(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller))
|
||||
.build()
|
||||
);
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.pause();
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
mActivityController.saveInstanceState(bundle);
|
||||
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.saveInstanceStateCalls++;
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.resume();
|
||||
expectedCallState.createViewCalls++;
|
||||
expectedCallState.restoreViewStateCalls++;
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -112,22 +210,24 @@ public class ControllerTests {
|
||||
}))
|
||||
.build());
|
||||
|
||||
Controller child = new TestController();
|
||||
TestController child = new TestController();
|
||||
attachLifecycleListener(child);
|
||||
|
||||
assertCalls(0, 0, 0, 0, 0, 0, 0);
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID)
|
||||
.pushChangeHandler(getPushHandler(0, 0, 0, 0, 0, 0, 0))
|
||||
.popChangeHandler(getPopHandler(1, 1, 1, 1, 0, 0, 0))
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, child))
|
||||
.popChangeHandler(getPopHandler(expectedCallState, child))
|
||||
.build()
|
||||
);
|
||||
|
||||
assertCalls(1, 1, 1, 1, 0, 0, 0);
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
parent.removeChildController(child);
|
||||
|
||||
assertCalls(2, 2, 1, 1, 1, 1, 1);
|
||||
assertCalls(expectedCallState, child);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -152,23 +252,28 @@ public class ControllerTests {
|
||||
}))
|
||||
.build());
|
||||
|
||||
Controller child = new TestController();
|
||||
TestController child = new TestController();
|
||||
attachLifecycleListener(child);
|
||||
|
||||
assertCalls(0, 0, 0, 0, 0, 0, 0);
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID)
|
||||
.pushChangeHandler(getPushHandler(0, 0, 0, 0, 0, 0, 0))
|
||||
.popChangeHandler(getPopHandler(1, 1, 1, 1, 0, 0, 0))
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, child))
|
||||
.popChangeHandler(getPopHandler(expectedCallState, child))
|
||||
.build()
|
||||
);
|
||||
|
||||
assertCalls(1, 1, 1, 1, 0, 0, 0);
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
mRouter.popCurrentController();
|
||||
ViewUtils.setAttached(child.getView(), false);
|
||||
|
||||
assertCalls(1, 1, 1, 1, 1, 1, 1);
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyViewCalls++;
|
||||
expectedCallState.destroyCalls++;
|
||||
assertCalls(expectedCallState, child);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -199,77 +304,110 @@ public class ControllerTests {
|
||||
Assert.assertNull(controller.getView());
|
||||
}
|
||||
|
||||
private ChangeHandler getPushHandler(final int changeStart, final int changeEnd, final int bindView, final int attach, final int unbindView, final int detach, final int destroy) {
|
||||
private ChangeHandler getPushHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
assertCalls(changeStart + 1, changeEnd, bindView + 1, attach, unbindView, detach, destroy);
|
||||
expectedCallState.changeStartCalls++;
|
||||
expectedCallState.createViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
assertCalls(changeStart + 1, changeEnd, bindView + 1, attach + 1, unbindView, detach, destroy);
|
||||
|
||||
expectedCallState.attachCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
|
||||
expectedCallState.changeEndCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ChangeHandler getPopHandler(final int changeStart, final int changeEnd, final int bindView, final int attach, final int unbindView, final int detach, final int destroy) {
|
||||
private ChangeHandler getPopHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
assertCalls(changeStart + 1, changeEnd, bindView, attach, unbindView, detach, destroy);
|
||||
expectedCallState.changeStartCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
container.removeView(from);
|
||||
ViewUtils.setAttached(from, false);
|
||||
assertCalls(changeStart + 1, changeEnd, bindView, attach, unbindView + 1, detach + 1, destroy + 1);
|
||||
|
||||
expectedCallState.destroyViewCalls++;
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
|
||||
expectedCallState.changeEndCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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, mCreateViewCalls);
|
||||
Assert.assertEquals(attach, mAttachCalls);
|
||||
Assert.assertEquals(unbindView, mDestroyViewCalls);
|
||||
Assert.assertEquals(detach, mDetachCalls);
|
||||
Assert.assertEquals(destroy, mDestroyCalls);
|
||||
private void assertCalls(CallState callState, TestController controller) {
|
||||
Assert.assertEquals("Expected call counts and controller call counts do not match.", callState, controller.currentCallState);
|
||||
Assert.assertEquals("Expected call counts and lifecycle call counts do not match.", callState, mCurrentCallState);
|
||||
}
|
||||
|
||||
private void attachLifecycleListener(Controller controller) {
|
||||
controller.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void onChangeStart(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
mChangeStartCalls++;
|
||||
mCurrentCallState.changeStartCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
mChangeEndCalls++;
|
||||
mCurrentCallState.changeEndCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postCreateView(@NonNull Controller controller, @NonNull View view) {
|
||||
mCreateViewCalls++;
|
||||
mCurrentCallState.createViewCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
mAttachCalls++;
|
||||
mCurrentCallState.attachCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroyView(@NonNull Controller controller) {
|
||||
mDestroyViewCalls++;
|
||||
mCurrentCallState.destroyViewCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
mDetachCalls++;
|
||||
mCurrentCallState.detachCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroy(@NonNull Controller controller) {
|
||||
mDestroyCalls++;
|
||||
mCurrentCallState.destroyCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Controller controller, @NonNull Bundle outState) {
|
||||
mCurrentCallState.saveInstanceStateCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(@NonNull Controller controller, @NonNull Bundle savedInstanceState) {
|
||||
mCurrentCallState.restoreInstanceStateCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveViewState(@NonNull Controller controller, @NonNull Bundle outState) {
|
||||
mCurrentCallState.saveViewStateCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreViewState(@NonNull Controller controller, @NonNull Bundle savedViewState) {
|
||||
mCurrentCallState.restoreViewStateCalls++;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -293,4 +431,5 @@ public class ControllerTests {
|
||||
mListener.performChange(container, from, to, isPush, changeListener);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,4 +3,12 @@ package com.bluelinelabs.conductor;
|
||||
import android.app.Activity;
|
||||
|
||||
public class TestActivity extends Activity {
|
||||
|
||||
public boolean isChangingConfigurations = false;
|
||||
|
||||
@Override
|
||||
public boolean isChangingConfigurations() {
|
||||
return isChangingConfigurations;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -7,18 +8,92 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerTransaction.ControllerChangeType;
|
||||
|
||||
public class TestController extends Controller {
|
||||
|
||||
@IdRes public static final int VIEW_ID = 2342;
|
||||
|
||||
public TestController() { }
|
||||
private static final String KEY_CALL_STATE = "TestController.currentCallState";
|
||||
|
||||
public CallState currentCallState;
|
||||
|
||||
public TestController() {
|
||||
currentCallState = new CallState();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
currentCallState.createViewCalls++;
|
||||
View view = new FrameLayout(inflater.getContext());
|
||||
view.setId(VIEW_ID);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChangeStarted(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
super.onChangeStarted(changeHandler, changeType);
|
||||
currentCallState.changeStartCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChangeEnded(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
super.onChangeEnded(changeHandler, changeType);
|
||||
currentCallState.changeEndCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttach(@NonNull View view) {
|
||||
super.onAttach(view);
|
||||
currentCallState.attachCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetach(@NonNull View view) {
|
||||
super.onDetach(view);
|
||||
currentCallState.detachCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroyView(View view) {
|
||||
super.onDestroyView(view);
|
||||
currentCallState.destroyViewCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
currentCallState.destroyCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveViewState(@NonNull View view, @NonNull Bundle outState) {
|
||||
super.onSaveViewState(view, outState);
|
||||
currentCallState.saveViewStateCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) {
|
||||
super.onRestoreViewState(view, savedViewState);
|
||||
currentCallState.restoreViewStateCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
currentCallState.saveInstanceStateCalls++;
|
||||
|
||||
outState.putParcelable(KEY_CALL_STATE, currentCallState);
|
||||
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
|
||||
currentCallState = savedInstanceState.getParcelable(KEY_CALL_STATE);
|
||||
|
||||
currentCallState.restoreInstanceStateCalls++;
|
||||
}
|
||||
}
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -15,12 +15,12 @@ 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.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.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.BundleBuilder;
|
||||
@@ -35,7 +35,7 @@ public class TransitionDemoController extends BaseController {
|
||||
|
||||
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),
|
||||
@@ -122,7 +122,7 @@ public class TransitionDemoController extends BaseController {
|
||||
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:
|
||||
|
||||
+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));
|
||||
|
||||
@@ -19,12 +19,11 @@
|
||||
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>
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_view"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="200dp"
|
||||
android:layout_width="@dimen/display_target_image_size"
|
||||
android:layout_height="@dimen/display_target_image_size"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_margin="24dp"
|
||||
android:layout_margin="@dimen/display_target_image_margin"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">24dp</dimen>
|
||||
<dimen name="activity_vertical_margin">24dp</dimen>
|
||||
|
||||
<dimen name="display_target_image_size">120dp</dimen>
|
||||
<dimen name="display_target_image_margin">16dp</dimen>
|
||||
</resources>
|
||||
@@ -2,4 +2,7 @@
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
|
||||
<dimen name="display_target_image_size">200dp</dimen>
|
||||
<dimen name="display_target_image_margin">24dp</dimen>
|
||||
</resources>
|
||||
|
||||
+4
-4
@@ -5,8 +5,8 @@ ext {
|
||||
buildToolsVersion = '23.0.2'
|
||||
|
||||
versionCode = 1
|
||||
versionName = '1.1.2'
|
||||
publishedVersionName = '1.1.2'
|
||||
versionName = '1.1.4'
|
||||
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'
|
||||
|
||||
Reference in New Issue
Block a user