4. Moxy
Требования к решениям задач
• Автоматическое сохранение отображения при пересоздании View
• Никакого boilerplate-кода
• Отсутствие ненужных флажков, id, switch-case, e.t.c.
15. Moxy
Задача #3
• Приложение запускается
• Проходит 2 секунды
• На экране отображается количество оставшихся секунд
• Отображается сообщение в виде AlertDialog
16. Moxy
public class MainActivity extends MvpAppCompatActivity implements HelloWorldView {
...
private AlertDialog mMessageDialog;
@Override
public void showMessage(int message) {
mMessageDialog = new AlertDialog.Builder(this)
.setTitle(R.string.app_name).setMessage(message)
.setPositiveButton(android.R.string.ok, null)
.setOnDismissListener(dialogInterface -> mHelloWorldPresenter.onDismissMessage())
.show();
}
@Override
public void hideMessage() {
if (mMessageDialog != null) {
mMessageDialog.dismiss();
}
}
}
17. Moxy
@InjectViewState
public class HelloWorldPresenter extends MvpPresenter<HelloWorldView> {
...
public void onDismissMessage() {
getViewState().hideMessage();
}
}
@StateStrategyType(AddToEndSingleStrategy.class)
public interface HelloWorldView extends MvpView {
void showTimer();
void hideTimer();
void setTimer(int seconds);
void showMessage(int message);
void hideMessage();
}
19. Moxy
Задача #4
• Приложение запускается
• Проходит 5 секунд
• На экране отображается количество оставшихся секунд
• Отображается сообщение в виде сторонней View
22. Moxy
Задача #5
• В Activity отображается 2 Fragment
• Каждый Fragment содержит счётчик нажатий на кнопку
• Изменения показаний счётчика одного фрагмента никак не
влияют на показания счётчика другого фрагмента
23. Moxy
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
if (savedInstanceState == null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction
.add(R.id.frame_1, getFragment(0xffFF80AB))
.add(R.id.frame_2, getFragment(0xffCCFF90))
.commit();
}
}
private Fragment getFragment(int color) {
CounterFragment fragment = new CounterFragment();
Bundle args = new Bundle();
args.putInt("argColor", color);
fragment.setArguments(args);
return fragment;
}
}
24. Moxy
@InjectViewState
public class CounterPresenter extends MvpPresenter<CounterView> {
private int mCount;
public CounterPresenter() {
getViewState().showCount(mCount);
}
public void onPlusClick() {
mCount++;
getViewState().showCount(mCount);
}
}
public interface CounterView extends MvpView {
@StateStrategyType(AddToEndSingleStrategy.class)
void showCount(int count);
}
30. Moxy
Задача #7
• В Activity отображается 2 счётчика
• Каждый счётчик является Custom View
• Изменения показаний одного счётчика никак не влияют на
показания другого счётчика
38. Moxy
Задача #9
• Сделать экран деталей конкретной новости
• Для каждой новости – свой глобальный Presenter
39. Moxy
public class DetailsActivity extends MvpAppCompatActivity implements DetailsView {
@InjectPresenter(type = PresenterType.GLOBAL)
DetailsPresenter mDetailsPresenter;
@ProvidePresenterTag(presenterClass = DetailsPresenter.class, type = PresenterType.GLOBAL)
String provideDetailsPresenterTag() {
return "details_" + getIntent().getLongExtra("extraDetailsId", 0);
}
@ProvidePresenter(type = PresenterType.GLOBAL)
DetailsPresenter provideDetailsPresenter() {
return new DetailsPresenter(getIntent().getLongExtra("extraDetailsId", 0));
}
...
equals equals
exclude tag
40. Moxy
Принцип действия. #1. @InjectPresenter
0. Annotation processor: Generate PresenterFields
1. MvpDelegate: onCreate(savedInstanceState)
2. MvpDelegate: Init delegate tag
3. MvpProcessor: Collect all PresenterField for MvpDelegate
4. MvpProcessor: Init each PresenterField
1. MvpProcessor: Generate presenter tag
2. PresenterStore: Get MvpPresenter by type and tag
3. MvpProcessor: MvpPresenter exists?
1. True:
1. MvpProcessor: Init presenter field of Delegated
2. False:
1. PresenterField: Provide presenter
2. PresenterStore: Save presenter
3. MvpProcessor: Init presenter field of Delegated
41. Moxy
Принцип действия. #2. @InjectViewState
0. Annotation processor: Generate ViewState
1. MvpPresenter: Construct
2. Binder: Bind presenter
3. Binder: Find ViewState for MvpPresenter
4. Binder: Create ViewState
5. Binder: Set ViewState to MvpPresenter
42. Moxy
Принцип действия. #3. ViewState
1. MvpPresenter: Send command
2. ViewState: Instantiation of ViewCommand
3. ViewState: Get StateStrategy of ViewCommand
4. StateStrategy: Called beforeApply(currentState, incomingCommand)
5. ViewState: Have a Views?
1. False: –
2. True:
1. ViewCommand: Apply to each Views
2. StateStrategy: Called afterApply(currentState, incomingState)
6. ViewState: Attached View
7. ViewState: Apply each ViewCommands
1. ViewCommand: Apply to attached View
2. StateStrategy: Called afterApply(currentState, incomingState)
43. Moxy
Итого
1. Нет проблем с жизненным циклом
2. Boilerplate-code генерируется в compile time
3. Можно использовать несколько Presenter в одном месте
4. Можно любой компонент превратить в MvpView
5. Можно использовать один экземпляр MvpPresenter в нескольких местах
Присоединяйтесь к проекту на github.com!
На CodeLab сделаем небольшой Github-client ;) + посмотрим результат кодогенерации
PS: https://github.com/senneco/MoxyCases