# OhosMvc **Repository Path**: chinasoft5_ohos/OhosMvc ## Basic Information - **Project Name**: OhosMvc - **Description**: Mvc Framework - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: https://gitee.com/chinasoft5_ohos/OhosMvc - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-08-12 - **Last Updated**: 2021-09-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # OhosMvc #### 项目介绍 - 项目名称:OhosMvc - 所属系列:openharmony的第三方组件适配移植 - 功能:Mvc Framework - 项目移植状态:主功能完成 - 调用差异:无 - 开发版本:sdk6,DevEco Studio2.2 Beta1 - 基线版本:Release3.3.0 #### 效果演示 ![MvcTest事例](https://images.gitee.com/uploads/images/2021/0819/105959_6ab115f7_962061.gif "mvctest.gif") #### 安装教程 1.在项目根目录下的build.gradle文件中, ``` allprojects { repositories { maven { url 'https://s01.oss.sonatype.org/content/repositories/releases/' } } } ``` 2.在entry模块的build.gradle文件中, ``` dependencies { implementation('com.gitee.chinasoft_ohos:ohos-mvc-poke:1.0.0') implementation('com.gitee.chinasoft_ohos:ohos-mvc-core:1.0.0') implementation('com.gitee.chinasoft_ohos:ohos-mvc:1.0.0') ...... } ``` 在sdk6,DevEco Studio2.2 Beta1下项目可直接运行 如无法运行,删除项目.gradle,.idea,build,gradle,build.gradle文件, 并依据自己的版本创建新项目,将新项目的对应文件复制到根目录下 #### 使用说明 ##### MVP架构事例 ###### base view ```java //Base view interface defined in OhosMvc framework public interface UiView { void update(); } ``` ###### base controller ```java //Base controller defined in OhosMvc framework public abstract class Controller extends Bean { protected VIEW view; } ``` ###### 事例一 ```java //A concrete controller extending Controller public class SomeController extends Controller { @Override public Class modelType() { return SomeController.class; } //The model of the controller that represents the state of the view public static class Model { String title; public String getTitle() { return title; } } public void updateTitle(String text) { //Model is updated getModel().title = text; //Notify the view. The implementation of method update() in //concrete view, the view reads the model of the controller //and reflect the model to UI view.update(); } } //View paired with the controller public class SomeView implements UiView { private Text title; @Inject private SomeController someController; @Override public void update() { //Read the controller's model and bind it to text title.setText(someController.getModel().getTitle()); } } ``` ###### 事例二 ```java //继承UiView,自定义跟新UI的方法 //entire model to the view public interface AsyncView extends UiView { void showLoadingStatus(); void hideLoadingStatus(); } //继承FragmentController,自定义控制器 public class LoginController extends FragmentController { @Override public Class modelType() { return null; } public void login(String username, String password) { runTask(new Task() { @Override public Void execute(Monitor monitor) throws Exception { //Task.execute methods runs on non-UI thread, so we need to //post the view update logic back to UI thread uiThreadRunner.post(new Runnable() { @Override public void run() { view.showLoadingStatus(); } }); //Send a http request to login //... //Request returns successfully //Task.execute methods runs on non-UI thread, so we need to //post the view update logic back to UI thread uiThreadRunner.post(new Runnable() { @Override public void run() { view.hideLoadingStatus(); } }); return null; } }, new Task.Callback() { @Override public void onException(Exception e) throws Exception { //Call back is guaranteed to run on UI thread by the framework //No need to use uiThreadRunner to post action view.hideLoadingStatus(); } }); } } //自定义LoginScreen 继承 MvcFragment public static class LoginScreen extends MvcFragment implements AsyncView{ private TextField username; private TextField password; private Button button; //Specify the class type of the paired controller @Override protected Class getControllerClass() { return LoginController.class; } @Override protected int getLayoutResId() { return ResourceTable.Layout_screen_login; } //Called when the view is ready. Similar to onViewCreated but this //callback guaranteed all injectable instances depended by this view are ready @Override public void onViewReady(Component view, PacMap savedInstanceState, Reason reason) { super.onViewReady(view, savedInstanceState, reason); //assign view by findComponentById //... button.setClickedListener(component-> controller.login(username.getText(),password.getText())); } @Override public void showLoadingStatus() { //Show progress dialog or progress bar } @Override public void hideLoadingStatus() { //Hide progress dialog or progress bar } @Override public void update() { //Bind model here } } ``` ##### MVVM架构事例 抽象 MvcFragment 和 AbstractController ```java // 抽象 MvcFragment public abstract class AbstractFragment extends MvcFragment implements UiView { @Override public void onViewReady(Component view, PacMap savedInstanceState, Reason reason) { super.onViewReady(view, savedInstanceState, reason); } } //抽象 AbstractController public abstract class AbstractController extends FragmentController { } ``` 自定义Manager ```java public class CounterManager extends Manager { /** * Namespace the events for this controller by nested interface so that all its events would * be referenced as CounterController.EventC2V.BlaBlaEvent. */ public interface Event2C { /** * Event2C to notify views counter has been updated */ class OnCounterUpdated { private final int count; public OnCounterUpdated(int count) { this.count = count; } public int getCount() { return count; } } } public static class Model { private int count; public int getCount() { return count; } public void setCount(int count) { this.count = count; } } /** * Just return the class type of the model managed by this controller */ @Override public Class modelType() { return Model.class; } public void setCount(Object sender, int count) { getModel().setCount(count); postEvent2C(new Event2C.OnCounterUpdated(count)); } public String convertNumberToEnglish(int number) { if (number < -3) { return "Less than negative three"; } else if (number == -3) { return "Negative three"; } else if (number == -2) { return "Negative two"; } else if (number == -1) { return "Negative one"; } else if (number == 0) { return "Zero"; } else if (number == 1) { return "One"; } else if (number == 2) { return "Two"; } else if (number == 3) { return "Three"; } else { return "Greater than three"; } } } ``` 自定义Controller ```java public class CounterMasterInsideController extends AbstractController { @Override public Class modelType() { return null; } @Inject private CounterManager counterManager; public String getCount() { return String.valueOf(counterManager.getModel().getCount()); } public String getCountInEnglish() { return counterManager.convertNumberToEnglish(counterManager.getModel().getCount()); } private void onEvent(CounterManager.Event2C.OnCounterUpdated event) { if (view != null) { view.update(); } } } ``` 自定义View ```java public class CounterMasterInsideView extends AbstractFragment { private Text txtCountInEnglish; @Override protected Class getControllerClass() { return CounterMasterInsideController.class; } @Override protected int getLayoutResId() { return ResourceTable.Layout_fragment_a_sub; } @Override public void onViewReady(Component view, PacMap savedInstanceState, Reason reason) { super.onViewReady(view, savedInstanceState, reason); txtCountInEnglish = (Text) view.findComponentById(ResourceTable.Id_fragment_a_sub_countInEnglish); } @Override public void update() { txtCountInEnglish.setText(controller.getCountInEnglish()); } } ``` ##### 页面路由 多页面路由的使用方式 继承MvcAbility ```java public class MainAbility extends MvcAbility { @Override protected Class mapFragmentRouting( Class controllerClass) { String controllerPackage = controllerClass.getPackage().getName(); String viewPkgName = controllerPackage.substring(0, controllerPackage.lastIndexOf(".")) + ".view"; String fragmentClassName = viewPkgName + "." + controllerClass.getSimpleName().replace("Controller", "Screen"); try { return (Class) Class.forName(fragmentClassName); } catch (ClassNotFoundException e) { String msg = String.format("Fragment class(%s) for controller(%s) can not be found", fragmentClassName, controllerClass.getName()); throw new RuntimeException(msg, e); } } @Override protected Class getDelegateFragmentClass() { return AppDelegateFragment.class; } } ``` 继承DelegateFragment ```java public class AppDelegateFragment extends MvcAbility.DelegateFragment implements AppDelegateController.View { private Text toolbarTitle; private Image toolbarBack; @Override protected int getLayoutResId() { return ResourceTable.Layout_fragment_app; } @Override protected int getContentLayoutResId() { return ResourceTable.Id_fragment_app_main; } @Override protected Class getControllerClass() { return AppDelegateController.class; } @Override public void update() { } @Override public void onViewReady(Component view, PacMap savedInstanceState, Reason reason) { super.onViewReady(view, savedInstanceState, reason); toolbarTitle = (Text) view.findComponentById(ResourceTable.Id_toolbar_title); toolbarBack = (Image) view.findComponentById(ResourceTable.Id_toolbar_image); toolbarBack.setClickedListener(component -> controller.navigateBack(component)); } /** * What to do when app starts for the first time */ @Override protected void onStartUp() { controller.startApp(this); } @Override public void updateTitle(String title) { toolbarTitle.setText(title); } @Override public void changeNavIcon(boolean showBackArrow) { if (showBackArrow) { toolbarBack.setVisibility(Component.VISIBLE); //toolbar.setNavigationIcon(R.drawable.ic_action_nav_back); } else { toolbarBack.setVisibility(Component.HIDE); } } } ``` 继承Manager ```java public class AppManager extends Manager { public interface Event { class OnTitleUpdated { private final String title; public OnTitleUpdated(String title) { this.title = title; } public String getTitle() { return title; } } } public static class Model { private String title; public String getTitle() { return title; } } @Override public Class modelType() { return Model.class; } public void setTitle(String title) { getModel().title = title; postEvent2C(new Event.OnTitleUpdated(title)); } } ``` 继承Controller ```java public class AppDelegateController extends AbstractController { public interface View extends UiView { void updateTitle(String title); void changeNavIcon(boolean showBackArrow); } @Inject private AppManager appManager; @Inject private NavigationManager navigationManager; @Override public Class modelType() { return null; } @Override public void onViewReady(Reason reason) { super.onViewReady(reason); view.updateTitle(appManager.getModel().getTitle()); doUpdateNavIcon(navigationManager.getModel().getCurrentLocation()); } public void startApp(Object sender) { navigationManager.navigate(sender).to(CounterMasterController.class); } public void navigateBack(Object sender) { navigationManager.navigate(sender).back(); } /** * Subscribe to event when app manager updates current page's title * @param event */ private void onEvent(AppManager.Event.OnTitleUpdated event) { view.updateTitle(event.getTitle()); } /** * Subscribe to forward navigation * @param event */ private void onEvent(NavigationManager.Event.OnLocationForward event) { updateToolbar(event.getCurrentValue()); } /** * Subscribe to backward navigation * @param event */ private void onEvent(NavigationManager.Event.OnLocationBack event) { updateToolbar(event.getCurrentValue()); } private void updateToolbar(final NavLocation location) { uiThreadRunner.post(new Runnable() { @Override public void run() { doUpdateNavIcon(location); } }); } private void doUpdateNavIcon(NavLocation location) { if (location != null) { view.changeNavIcon(location.getPreviousLocation() != null); } else { view.changeNavIcon(false); } } } ``` 跳转页面 ``` navigationManager.navigate(sender).to(CounterMasterController.class); ``` 页面返回 ``` navigationManager.navigate(sender).back(); ``` ##### Navigation 多页面导航的使用实例 继承MvcAbility ```java public class InjectionTestAbilityStateManagedObjects extends MvcAbility { @Override protected Class mapFragmentRouting( Class controllerClass) { if (controllerClass == ControllerA.class) { return FragmentA.class; } else if (controllerClass == ControllerB.class) { return FragmentB.class; } else if (controllerClass == ControllerC.class) { return FragmentC.class; } else if (controllerClass == ControllerD.class) { return FragmentD.class; } return null; } @Override protected Class getDelegateFragmentClass() { return HomeFragment.class; } public static class HomeFragment extends DelegateFragment { static class HomeController extends FragmentController { @Override public Class modelType() { return null; } } @Override protected Class getControllerClass() { return HomeController.class; } @Override public void update() { } @Inject private NavigationManager navigationManager; @Override protected void onStartUp() { navigationManager.navigate(this).to(ControllerD.class, new Forwarder().clearAll()); } } } ``` FragmentA,FragmentB,FragmentC,FragmentD 分别继承MvcFragment ControllerA,ControllerB,ControllerC,ControllerD 分别继承FragmentController 在子页面中的跳转事件中 ``` class loc = ControllerA.class;//ControllerB.class,ControllerC.class navigationManager.navigate(view).to(loc); ``` #### 测试信息 CodeCheck代码测试无异常 CloudTest代码测试无异常 病毒安全检测通过 当前版本demo功能与原组件基本无差异 #### 版本迭代 - 1.0.0 - 0.0.1-SNAPSHOT #### 版权和许可信息 - Apache2.0