我们之前在制作强化界面时,点击切换飞机按钮属性界面就会跟着改变。两个部分是在StrengthenView内进行联系的。

1
2
3
4
5
6
7
8
9
10
11
[BindPrefab(Path.STRENGTHEN_VIEW)]
public class StrengthenView : ViewBase
{
protected override void InitChild()
{
SwitchPlayer switchPlayer = UIUtil.Get("Switchplayer").Go.AddComponent<SwitchPlayer>();
PlaneProperty planeProperty = UIUtil.Get("Property").Go.AddComponent<PlaneProperty>();

switchPlayer.OnSwitchAction(planeProperty.UpdataId);
}
}

这明显不符合MVC分离的原则,一方面每个需要刷新的事件都要写一个Action,另一方面就是增加了耦合性。

IViewUpdate

使用接口可以很灵活,没有继承ViewBase的Mono类,只要继承了这个接口,也可以被ViewBase缓存并调用接口方法。

1
2
3
4
public interface IViewUpdate
{
void ViewUpdate();
}

IView继承IViewUpdate

ViewBase

修改ViewBase,让它缓存IViewUpdate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public abstract class ViewBase : MonoBehaviour, IView
{
//...
private List<IViewUpdate> _viewUpdates;
//...
public virtual void Init()
{
InitChild();
GetAllSubView();//将InitSubView改名
InitAllSubView();//将之前的逻辑包装成方法
InitUpdateObjects();//获取所有的IViewUpdate
AddUpdateAction();//让所有的Button都触发视图的更新
}
//...
private void InitUpdateObjects()
{
_viewUpdates = transform.GetComponentsInChildren<IViewUpdate>().ToList();

}
private void InitAllSubView()
{
foreach (ViewBase view in _views)
{
view.Init();
}
}
private void AddUpdateAction()
{
foreach (Button button in GetComponentsInChildren<Button>())
{
button.onClick.AddListener(UpdateAction);
}
}
//...
private void UpdateAction()
{
foreach (IViewUpdate viewUpdate in _viewUpdates)
{
viewUpdate.ViewUpdate();
}
}
public virtual void ViewUpdate()
{

}
//...
}

注意ViewBase实现IViewUpdate接口时实现的是虚方法并且是空的,这样就能避免递归调用的问题(GetComponentsInChildren<IViewUpdate>包含ViewBase自身)

重构强化界面逻辑

在强化界面中,属性更新的部分是需要动态刷新的。

PlaneProperty

删掉PlanePropertyUpdateId方法,并删除缓存item的列表

PropertyItem

PropertyItem继承IViewUpdate

1
2
3
4
5
6
7
8
public class PropertyItem : MonoBehaviour, IViewUpdate
{
//...
public void ViewUpdate()
{
UpdatePlaneId(GameStateModel.Instance.SelectedPlaneId);
}
}

StrengthenView

1
2
3
4
5
6
7
8
9
[BindPrefab(Path.STRENGTHEN_VIEW)]
public class StrengthenView : ViewBase
{
protected override void InitChild()
{
UIUtil.Get("Switchplayer").Go.AddComponent<SwitchPlayer>();
UIUtil.Get("Property").Go.AddComponent<PlaneProperty>();
}
}

SwitchPlayer

修改SwitchPlayer,删掉_onSwitch,并删掉SwitchOnSwitchAction方法中对_onSwitch引用的部分。

引入Model层

在之前我们声明的IDataMemoryDataMgr等都是从存档中交互,我们还需要在运行时进行交互的数据层。就是Model层。

GameStateModel

这里的Model是一个单例

1
2
3
4
public class GameStateModel : NormalSingleton<GameStateModel>
{
public int SelectedPlaneId { get; set; }
}

SwitchPlayer

1
2
3
4
5
6
private void Switch(ref int id,int direction)
{
UpdateId(ref id,direction);
UpdateSprite(id);
GameStateModel.Instance.SelectedPlaneId = id;//+++
}

IView接口分离

通过前面的修改,我们实现了强化界面下所有的Button都能刷新界面中实现了IViewUpdate的UI元素的功能。

但是还有一个问题,PropertyItem的初始化依然没有被统一调用。导致一开始打开界面时没有办法刷新PropertyItem,同时PropertyItem并没有继承ViewBase,也就没有Init生命周期。

我们把IView接口分离,让没有继承ViewBase的UI元素可以选择性地继承初始化接口,从而把自己的初始化逻辑交出去。

新建IViewInitIViewShowIViewHide

1
2
3
4
public interface IViewInit
{
void Init();
}
1
2
3
4
public interface IViewShow
{
void Show();
}
1
2
3
4
public interface IViewHide
{
void Hide();
}

修改IView

1
2
3
public interface IView : IViewUpdate, IViewInit, IViewShow, IViewHide
{
}

第二次重构ViewBase

既然IView被分离了,我们就必须再次重构ViewBase,现在ViewBase是UI的主层逻辑才继承的,一些小的UI元素只需要选择性地继承上面分离出来的接口就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
using System.Collections.Generic;
using UnityEngine;

public abstract class ViewBase : MonoBehaviour, IView
{
private UIUtil m_UIUtil;
private List<IViewUpdate> _viewUpdates;
private List<IViewInit> _viewInits;
private List<IViewShow> _viewShows;
private List<IViewHide> _viewHides;
protected UIUtil UIUtil
{
get
{
if (m_UIUtil == null)
{
m_UIUtil = gameObject.AddComponent<UIUtil>();
m_UIUtil.Init();
}
return m_UIUtil;
}
}
public virtual void Init()
{
InitChild();
GetAllSubView();
InitAllSubView();
}
protected abstract void InitChild();
private void GetAllSubView()
{
_viewInits = new List<IViewInit>();
_viewShows = new List<IViewShow>();
_viewHides = new List<IViewHide>();
_viewUpdates = new List<IViewUpdate>();
InitInterface();
}
private void InitInterface()
{
InitViewInterface(_viewInits);
InitViewInterface(_viewShows);
InitViewInterface(_viewHides);
InitViewInterface(_viewUpdates);
}
private void InitViewInterface<T>(List<T> views)
{
foreach (Transform trans in transform)
{
if (trans.TryGetComponent(out T view))
{
views.Add(view);
}
}
}

private void InitAllSubView()
{
foreach (var view in _viewInits)
{
view.Init();
}
}

public virtual void Hide()
{
foreach (var view in _viewHides)
{
view.Hide();
}
gameObject.SetActive(false);//父物体要最后隐藏,否则可能会出现问题
}


public virtual void Show()
{
gameObject.SetActive(true);
foreach (var view in _viewShows)
{
view.Show();
}
UpdateFun();
}

public virtual void UpdateFun()//这个方法由UIMgr传递给对应的Controller调用,但是只有根节点的View才能传递这个方法
{
foreach (IViewUpdate viewUpdate in _viewUpdates)
{
viewUpdate.UpdateFun();
}
}
public Transform GetTrans()
{
return transform;
}
}

删除掉HashSet<ViewBase> _views,添加_viewInits_viewShows_viewHides

修改GetAllSubViewInitAllSubViewShowHide方法,添加InitViewInterface方法

修改PropertyItem

此时让PropertyItem继承IViewShow,并实现Show方法

1
2
3
4
5
6
7
8
9
public class PropertyItem : MonoBehaviour, IViewUpdate, IViewShow
{
//...
public void Show()
{
int id = DataMgr.Instance.Get<int>(DataKeys.PLANE_ID);
UpdatePlaneId(id);
}
}