-
Notifications
You must be signed in to change notification settings - Fork 76
6. Using custom views instead of fragments
In order to create a view-based setup, using the DefaultStateChanger
and Navigator
is the easiest way to start out with.
Here is a step-by-step guide to using views.
To create a custom viewgroup, you need to create a layout file as you generally do, for example layout/hello_world_view.xml
.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/hello_world_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"
android:layout_centerInParent="true" />
</RelativeLayout>
But you also create a corresponding class for it that extends your root viewgroup:
public class HelloWorldView extends RelativeLayout {
public HelloWorldView(@NonNull Context context) {
super(context);
init(context);
}
public HelloWorldView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public HelloWorldView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
@TargetApi(21)
public HelloWorldView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
private void init(Context context) {
if(!isInEditMode()) {
// ... get key, inject from dagger component, etc.
}
}
private HelloWorldViewBinding binding;
@Override
protected void onFinishInflate() {
super.onFinishInflate();
binding = HelloWorldViewBinding.bind(this);
}
}
And most importantly, once you've created this custom viewgroup, you want to replace the root in your layout XML file.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
to
<your.packages.HelloWorldView xmlns:android="http://schemas.android.com/apk/res/android"
...>
</your.packages.HelloWorldView>
This means when your layout XML is inflated, it'll create your custom viewgroup, where you can handle its views' events.
Keys are immutable value objects that represent your state in your application (where you are and where you've been).
Generally this should be Parcelable (or a KeyParceler
must be specified to make it be Parcelable).
If you use simple-stack
in Java, then your keys will typically look somewhat like this:
@AutoValue
public abstract class HelloWorldKey extends BaseKey {
public static HelloWorldKey create() {
return new AutoValue_HelloWorldKey();
}
@Override
public int layout() {
return R.layout.hello_world_view;
}
}
Where BaseKey
is
public abstract class BaseKey implements DefaultViewKey, Parcelable {
@Override
public ViewChangeHandler viewChangeHandler() {
return new SegueViewChangeHandler();
}
}
For AutoValue
to work, you need to add it as a compileOnly
and annotationProcessor
dependency. The samples use auto-parcel
to make them Parcelable, but with Kotlin, you can use @Parcelize data class
.
dependencies {
....
provided "com.google.auto.value:auto-value:1.4.1"
annotationProcessor "com.google.auto.value:auto-value:1.4.1"
}
Then it'll just work!
For additional parameters in the key, you just for example want to add a public abstract String param();
method, as you normally would with auto-value.
Considering you generally add additional parameters needed by your view to the key (think of it like a typed Intent), the DefaultStateChanger
inflates the view using stateChange.createContext()
(which creates a KeyContextWrapper
) as its context, which allows you to use Backstack.getKey(context)
to obtain the key.
For example,
public class HelloWorldView extends RelativeLayout {
// ...
HelloWorldKey helloWorldKey;
private void init(Context context) {
if(!isInEditMode()) {
helloWorldKey = Backstack.getKey(context);
}
}
In your Activity, you generally want to do the following, or something similar:
public class MainActivity
extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Navigator.install(this, findViewById(R.id.root), History.single(HelloWorldKey.create()));
}
}
If you're using Navigator, then you can easily access the backstack with Navigator.getBackstack(context)
.
binding.helloButton.setOnClickListener((view) -> {
Navigator.getBackstack(view.getContext()).goTo(OtherKey.create());
});