Step 1. Add the JitPack repository to your build file
Add it in your root settings.gradle at the end of repositories:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
Add it in your settings.gradle.kts at the end of repositories:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
Add to pom.xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
Add it in your build.sbt at the end of resolvers:
resolvers += "jitpack" at "https://jitpack.io"
Add it in your project.clj at the end of repositories:
:repositories [["jitpack" "https://jitpack.io"]]
Step 2. Add the dependency
dependencies {
implementation 'com.github.material-motion:material-motion-runtime-android:6.0.1'
}
dependencies {
implementation("com.github.material-motion:material-motion-runtime-android:6.0.1")
}
<dependency>
<groupId>com.github.material-motion</groupId>
<artifactId>material-motion-runtime-android</artifactId>
<version>6.0.1</version>
</dependency>
libraryDependencies += "com.github.material-motion" % "material-motion-runtime-android" % "6.0.1"
:dependencies [[com.github.material-motion/material-motion-runtime-android "6.0.1"]]
The Material Motion Runtime is a tool for describing motion declaratively.
This library does not do much on its own. What it does do, however, is enable the expression of motion as discrete units of data that can be introspected, composed, and sent over a wire.
This library encourages you to describe motion as data, or what we call plans. Plans are committed to the runtime. The runtime coordinates the creation of performers, objects responsible for translating plans into concrete execution.
To use the runtime, simply instantiate a MotionRuntime
object and add a plan.
Plan plan;
View target;
MotionRuntime runtime = new MotionRuntime();
runtime.addPlan(plan, target);
Learn more about the APIs defined in the library by reading our technical documentation and our Starmap.
Add the Jitpack repository to your project's build.gradle
:
allprojects {
repositories {
maven { url "https://jitpack.io" }
}
}
Depend on the latest version of the library. Take care to occasionally check for updates.
dependencies {
compile 'com.github.material-motion:runtime-android:6.0.1'
}
For more information regarding versioning, see:
You can have a copy of this library with local changes and test it in tandem
with its client project. To add a local dependency on this library, add this
library's identifier to your project's local.dependencies
:
com.github.material-motion:runtime-android
Because
local.dependencies
is never to be checked into Version Control Systems, you must also ensure that any local dependencies are also defined inbuild.gradle
as explained in the previous section.
Important
For each local dependency listed, you must run gradle install
from its
project root every time you make a change to it. That command will publish your
latest changes to the local maven repository. If your local dependencies have
local dependencies of their own, you must gradle install
them as well.
You must gradle clean
your project every time you add or remove a local
dependency.
How to use the library in your project.
Open Android Studio,
choose File > New > Import
,
choose the root build.gradle
file.
To build the sample application, run the following commands:
git clone https://github.com/material-motion/runtime-android.git
cd runtime-android
gradle installDebug
To run all unit tests, run the following commands:
git clone https://github.com/material-motion/runtime-android.git
cd runtime-android
gradle test
The Material Motion Runtime consists of two groups of APIs: a runtime object and a constellation of protocols loosely consisting of plan and performing types.
The MotionRuntime object is a coordinating entity whose primary responsibility is to fulfill plans by creating performers. You can create many runtimes throughout the lifetime of your application. A good rule of thumb is to have one runtime per interaction or transition.
The Plan and Performer classes each define the minimal characteristics required for an object to be considered either a plan or a performer, respectively, by the Material Motion Runtime.
Plans and performers have a symbiotic relationship. A plan is executed by the performer it defines. Performer behavior is configured by the provided plan instances.
Learn more about the Material Motion Runtime by reading the Starmap.
The following steps provide copy-pastable snippets of code.
Questions to ask yourself when creating a new plan type:
As general rules:
public class MyPlan {
}
Performers are responsible for fulfilling plans. Fulfillment is possible in a variety of ways:
See the associated links for more details on each performing type.
Note: only one instance of a type of performer per target is ever created. This allows you to register multiple plans to the same target in order to configure a performer. See How to configure performers with plans for more details.
public class MyPerformer {
}
Conforming to Plan requires:
public class MyPlan extends Plan<View> {
@Override
public Class<? extends Performer<View>> getPerformerClass() {
return MyPerformer.class;
}
@Override
public Plan clone() {
// Only override this method if you need to deep clone reference-typed fields.
return super.clone();
}
}
Conforming to Performer requires:
addPlan()
.public class MyPerformer extends Performer<View> {
@Override
public void addPlan(Plan<View> plan) {
View target = getTarget();
}
}
public class MyActivity extends Activity {
private final MotionRuntime runtime = new MotionRuntime();
}
Plan<View> plan;
View target;
runtime.addPlan(plan, target);
public class MyActivity extends Activity {
private final MotionRuntime runtime = new MotionRuntime();
}
NamedPlan<View> plan;
String name;
View target;
runtime.addNamedPlan(plan, name, target);
The addPlan()
method will be invoked with plans that require use of this performer.
public class MyPerformer extends Performer<View> {
@Override
public void addPlan(Plan<View> plan) {
MyPlan myPlan = (MyPlan) plan;
// Do something with myPlan.
}
}
Handling multiple plan types
public class MyPerformer extends Performer<View> {
@Override
public void addPlan(Plan<View> plan) {
if (plan instanceof Plan1) {
addPlan1((Plan1) plan);
} else if (plan instanceof Plan2) {
addPlan2((Plan2) plan);
} else {
throw new IllegalArgumentException("Plan type not supported for " + plan);
}
}
}
public class MyPerformer extends NamedPerformer<View> {
@Override
public void addPlan(NamedPlan<View> plan, String name) {
MyPlan myPlan = (MyPlan) plan;
// Do something with myPlan.
}
@Override
public void removePlan(String name) {
// Remove any configuration associated with the given name.
}
}
A composition performer is able to emit new plans using a plan emitter. This feature enables the reuse of plans and the creation of higher-order abstractions.
public class MyPerformer extends Performer<View> implements ComposablePerforming<View> {
// Store the emitter in your class' definition.
private PlanEmitter<View> emitter;
@Override
public void setPlanEmitter(PlanEmitter<View> planEmitter) {
this.emitter = planEmitter;
}
}
Performers are only able to emit plans for their associated target.
PlanEmitter<View> emitter;
Plan<View> plan;
emitter.emit(plan);
Performers will often perform their actions over a period of time or while an interaction is active. These types of performers are called continuous performers.
A continuous performer is able to affect the active state of the runtime by generating is-active tokens. The runtime is considered active so long as an is-active token exists and has not been terminated. Continuous performers are expected to terminate a token when its corresponding work has completed.
For example, a performer that registers a platform animation might generate a token when the animation starts. When the animation completes the token would be terminated.
public class MyPerformer extends Performer<View> implements ContinuousPerforming {
// Store the emitter in your class' definition.
private IsActiveTokenGenerator tokenGenerator;
@Override
public void setIsActiveTokenGenerator(IsActiveTokenGenerator isActiveTokenGenerator) {
tokenGenerator = isActiveTokenGenerator;
}
}
You will likely need to store the token in order to be able to reference it at a later point.
Animator animator;
animator.addListener(new AnimatorListenerAdapter() {
private IsActiveToken token;
@Override
public void onAnimationStart(Animator animation) {
token = tokenGenerator.generate();
}
});
animator.start();
@Override
public void onAnimationEnd(Animator animation) {
token.terminate();
}
Tracing allows you to observe internal events occurring within a runtime. This information may be used for the following purposes:
Use for other purposes is unsupported.
public class CustomTracer implements Tracing {
}
The documentation for the Tracing interface enumerates the available methods.
public class CustomTracer implements Tracing {
@Override
public <T> void onAddPlan(Plan<T> plan, T target) {
}
}
runtime.addTracer(new LogcatTracer());
We welcome contributions!
Check out our upcoming milestones.
Learn more about our team, our community, and our contributor essentials.
Licensed under the Apache 2.0 license. See LICENSE for details.