您的位置:首页 > 移动开发 > Android开发

Android 系列 6.4通过将视图从模型中去耦来处理配置更改

2017-01-11 09:09 363 查看
6.4通过将视图从模型中去耦来处理配置更改

问题

当设备的配置更改(最常见的是因为方向更改)时,您的活动将被销毁并重新创建,从而使状态信息难以维护。



将您的用户界面从数据模型中分离,以便销毁活动不会影响您的状态数据。

讨论

这是一种情况,每个Android开发人员(除了那些谁阅读本书的这部分时间)与他们的第一个应用程序:“我的应用程序工作很好,但是当我改变我的手机的方向一切都重置!

根据设计,当设备的配置(读取:方向)更改时,Android UI框架会销毁当前活动并为新配置重新创建。

这使得设计者能够针对不同的屏幕取向和尺寸优化布局。然而,这对于希望维持活动的状态的开发者造成问题,因为它在取向改变破坏屏幕之前。

尝试解决这个问题可能导致许多复杂的解决方案,一些比其他更优雅。但如果我们退后一步,明智地设计我们的应用程序,我们可以编写更干净,更强大的代码,使生活更容易为每个人。

图形用户界面(GUI)正是其名称所描述的。它是底层数据模型的图形表示,允许用户与数据接口并操作数据。它不是数据模型本身。让我们通过一个例子来说明为什么这是一个重要的要点。

考虑一个井字游戏应用程序。一个简单的主要活动,这将最有可能包括在最低限度一个GridView(使用适当的适配器)显示板和一个TextView告诉用户的轮到它。当用户单击网格中的正方形时,在该网格单元中放置适当的X或O。作为新的Android开发人员,我们发现逻辑上还包括一个二维数组,其中包含了一个表示板的数据来存储数据,这样我们可以确定游戏是否结束,如果是的话,谁赢了(见例6-3 )。

实例6-3。 TicTacToe活动类的第一版本
public class TicTacToeActivity extends Activity {
private TicTacToeState[][] mBoardState;
private GridView mBoard;
private TextView mTurnText;
@Override
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.main);
mBoardState = new TicTacToeState[3][3];
mBoard = (GridView)findViewById(R.id.board);
mTurnText = (TextView)findViewById(R.id.turn_text);
// ... Set up Adapter, OnClickListeners, etc., for mBoard.
}
}
这很容易想象和实现,一切都很好。除了当你把你的手机侧身在一个强烈的一轮井字游戏的中间,你有一个新的板,盯着你的脸,你的不可避免的胜利被推迟。如前所述,UI框架刚刚销毁了您的Activity并重新创建它,调用onCreate()和重置板数据。

在阅读示例6-3中的代码时,您可能对自己说,“嘿,Bundle savedInstanceState看起来很有前途!”你说得对。对于这个痛苦,几乎刑事简单的例子,你可以把你的板数据到捆绑,并使用它来重新加载您的屏幕。甚至有一对方法,onRetainNonConfigurationInstance()和getLastNonConfigurationInstance(),让你传递任何你想要的对象从旧的,销毁的活动,到你新创建的。对于这个例子,你可以传递你的mBoardState数组到你的新Activity,你会被设置。但是我们每天都会写出大的,成功的,令人惊叹的应用程序,而且这些应用程序不能很好地适用于复杂的接口。我们可以做得更好!

这就是为什么将GUI从数据模型中分离得那么方便。您的GUI可以被销毁,重新创建和更改,但基础数据可以通过尽可能多的UI更改无懈可击,你可以抛出它。让我们将我们的游戏状态分成一个单独的数据类(见例6-4)。
实施例6-4。 TicTacToe类分

public class TicTacToeGame {
private TicTacToeState[][] mBoardState;
public TicTacToeGame() {
mBoardState = new TicTacToeState[3][3];
// ... Initialize
}
public TicTacToeState getCellState(int row, int col) {
return mBoardState[row][col];
}
public void setCellState(int row, int col, TicTacToeState state) {
mBoardState[row][col] = state;
}
// ... Other utility methods to determine whose turn it is, if the game is over, etc.
}


这不仅有助于我们保持我们的应用程序状态,但它通常只是良好的面向对象的设计。

现在我们的数据安全地在volatile Activity之外,我们如何访问它来构建我们的界面?有两种常见的方法:1)将TicTacToeGame中的所有变量声明为静态,并通过静态方法访问它们; 2)将TicTacToeGame设计为单例,允许访问一个全局实例以在我们的应用程序中使用。

我更喜欢第二个选项纯粹从设计的角度。我们可以将TicTacToeGame转换为单例,方法是将构造函数设为private,并在类的顶部添加以下几行:
private static TicTacToeGame instance = new TicTacToeGame();
public static TicTacToeGame getInstance() {
return instance;
};


现在我们要做的是获取游戏数据,并设置我们的UI元素以适当地显示数据。最有用的是将其封装在自己的function-refreshUI()中,以便在Activity对数据进行更改时使用。例如,当用户单击板的单元格时,监听器只需要两行代码:一次调用修改数据模型(通过我们的TicTacToeGame单例),一次调用刷新UI。

这可能是显而易见的,但值得一提的是,只要应用程序的进程正在运行,您的数据类就会继续存在。如果它被用户或系统杀死,自然数据丢失。这种情况需要通过文件系统或数据库进行更持久的存储,并且超出了本文的范围。

这种方法非常有效地将数据的视觉表示与数据本身分离,并使取向变化无关紧要。只需在onCreate(Bundle)方法中调用refreshUI()就足以确保当Activity被销毁和重新创建时,它可以访问数据模型并正确显示它。

作为一个额外的好处,你现在正在练习更好的面向对象的设计,并将看到你的代码基础变得更干净,更可扩展,更容易维护。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐