原文:https://medium.com/@husayn.hakeem/android-by-example-mvvm-data-binding-model-part-2-82b5e3ee6ccb
This article is part 4/4 from a series or articles about implementing the MVVM design pattern and data binding in a sample Tic-Tac-Toe application. If you’re unfamiliar with the terms data binding or MVVM pattern, refer to part 1 for a quick refresher. In part 2 we implement the Model classes, while in part 3 we implement the View Model class, so make sure to read through them before continuing along this part.
Note: You’ll find all the code for this project in the following Github repo.
husaynhakeem/TicTacToe-MVVM
TicTacToe-MVVM - Sample android application used to learn the Model View View Model pattern and DataBinding in Android
github.com
V for View
Let’s start by the XML code. This following file is named activity_game.xml.
<data>
<variable
name="gameViewModel"
type="husaynhakeem.io.tictactoe_mvvm.viewmodel.GameViewModel" />
</data>
<android.support.v7.widget.GridLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:padding="32dp">
<TextView
android:id="@+id/cell_00"
style="@style/CellTextView"
android:background="@drawable/cell_00"
android:onClick="@{() -> gameViewModel.onClickedCellAt(0, 0)}"
android:text='@{gameViewModel.cells["00"]}'
app:layout_column="1"
app:layout_columnWeight="1"
app:layout_row="1"
app:layout_rowWeight="1" />
...
</android.support.v7.widget.GridLayout>
2 things to note about the code above
You’ll notice that the rest of the cells (TextViews) were ommitted, they all resemble the one present above, each calling the onClickedCell method from the ViewModel once clicked upon, and each displaying the value of the variable cells from the View Model class.
A variable of the name gameViewModel of type GameViewModel is used in the layout above, its value is set in the GameActivity class as you’ll see shortly.
This is it for the layout, let’s now see the GameActivity class.
As we said in part 3, the View will observe for the end of the game, which is indicated by aLiveData instance provided by the ViewModel.
Here is a list of things the View should be able to do:
Prompt the players to enter their names. A Tic-Tac-Toe game is played by 2 players, their names are required before begining a game.
Subscribe and bind to the View Model’s LiveData.
React to notifications from the View Model
The code below is the implementation of the main View class, it implements each of the requirements written above.
public class GameActivity extends AppCompatActivity {
private static final String GAME_BEGIN_DIALOG_TAG = "game_dialog_tag";
private static final String GAME_END_DIALOG_TAG = "game_end_dialog_tag";
private static final String NO_WINNER = "No one";
private GameViewModel gameViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
promptForPlayers();
}
public void promptForPlayers() {
GameBeginDialog dialog = GameBeginDialog.newInstance(this);
dialog.show(getSupportFragmentManager(), GAME_BEGIN_DIALOG_TAG);
}
public void onPlayersSet(String player1, String player2) {
initDataBinding(player1, player2);
}
private void initDataBinding(String player1, String player2) {
ActivityGameBinding activityGameBinding = DataBindingUtil.setContentView(this, R.layout.activity_game);
gameViewModel = ViewModelProviders.of(this).get(GameViewModel.class);
gameViewModel.init(player1, player2);
activityGameBinding.setGameViewModel(gameViewModel);
setUpOnGameEndListener();
}
private void setUpOnGameEndListener() {
gameViewModel.getWinner().observe(this, this::onGameWinnerChanged);
}
@VisibleForTesting
public void onGameWinnerChanged(Player winner) {
String winnerName = winner == null || isNullOrEmpty(winner.name) ? NO_WINNER : winner.name;
GameEndDialog dialog = GameEndDialog.newInstance(this, winnerName);
dialog.show(getSupportFragmentManager(), GAME_END_DIALOG_TAG);
}
}
Things to note about the code:
gameViewModel.getWinner().observe(…): This method observes the winner LiveData instance. When the end of the gale is reached, the winner’s value is changed which triggers the onGameWinnerChanged method.
ActivityGameBinding: This class is generated based on the XML layout file activity_game.xml. This class holds all the bindings from the layout properties (in our case, we only have the gameViewModel variable from the XML layout). This class also knows how to assign values for the binding expressions, which brings us to the next point.
setGameViewModel: Method that assigns a value to the gameViewModel variable from the XML layout file.
You’ll notice that in the code, 2 dialog classes are being used, in order to keep the article concise and only focus on the important parts of the implementation, their codes hasn’t been added to this article, but rest assured, their code has nothing new to them, you’ll find them in the code in the Github repo for this project.
Congrats if you’ve made it this far! Hopefully by now you’ll have a better understanding of the MVVM pattern, and how to use data binding.
Feel free to ask questions, your remarks are also welcome.
Thanks for reading this article. Be sure to like and recommend it if you found it helpful.
For more about Java and Android, follow me so that you’ll get notified when I write new posts, or connect with me here on Github and Linkedin.