⬆️ ⬇️

Create a development application using Android Studio

Introduction



Good afternoon, ladies and gentlemen! I am a student in grade 11 and I am engaged in development only because I don’t want to prepare for the EGE. The idea of ​​the application can be described in three words - I saw, remembered, repeated. Before the player appears a square box with a certain number of shaded elements. After some time, the field is cleared. It is necessary to choose which elements were painted over. As you progress through the levels of the game, the field becomes larger and you have to memorize more.



Theory



First we need to decide on the architecture of the software interface. I prefer MVC (Model - View - Controller).



image


With this approach, the controller intercepts the event from the outside and in accordance with the logic embedded in it, reacts to this event by changing the model by calling the appropriate method. After the change, the model uses the event that it has changed, and all the view events subscribed to it, upon receiving it, turn to the Model for updated data, after which they are displayed. So, decided. Next you need to select a programming language. I like java. Java is an object-oriented programming language, so we should follow the principles of OOP:



1) Encapsulation is a component that allows to combine the code and the data with which it handles.

2) Polymorphism is a property of the system that allows using objects with the same interface without information about the type and internal structure of the object.

3) Inheritance is a property of the system that allows you to describe a new class based on an existing one with partially or fully borrowed functionality. The class from which the inheritance is made is called the base or parent class. A new class is a descendant, heir, child, or derived class.

')

Now we restrict the domain to such classes as:



Game (Game), Player (Player), Field (Field). The game is responsible for the data generated directly in the game. The field stores information about which cells are filled and which are not. The game can create a field. The player can handle user data, i.e. save points and rating when necessary.



Main part



Enough theory. We need to perform the following sequence of actions:



1) Download development environment and JDK .

2) Install both sets on your computer.

3) Run the development environment.



Create an empty project and start creating. We define the package structure in the following way:





Next we do a little trick. Create markup files:





The root xml file is responsible for the root of our view. Inside it will dynamically change the markup of the fragments that we have already created. Accordingly, in the view package we will create the classes fragments:





Each fragment is responsible for one game mode. When you open the application, the onCreate method of the MainActivity class (which we also put in the view package) is executed. Change this method to this state:





Here we check that if our activity is created for the first time, then we need to create objects of the fragments and make the menu fragment visible. Of course, before creating objects, you need to give them links, but I think that you can handle it yourself.



Fragments are created, but they do nothing. It's time to breathe life into them. Create an interface:



Interface
public interface SendingActivity { // ,     void send(int a); // ,         void send(Field field, int level); // ,     ,    void nextLevel(GameModChallenge game); // ,     ,    void nextLevel(GameModSprint game); // ,     ,    void nextLevel(GameModTwisting game); // ,     ,    void nextLevel(GameModEvidence game); //            ,     void next(GameModChallenge game); // ,    void failSprint(GameModSprint game); // ,    void failTwisting(GameModTwisting game); // ,    void failEvidence(GameModEvidence game); // ,      void releaseToolbar(); } 




Through the methods of this interface, the objects will inform about their actions of activity, and she will already decide what to do.



Create a fragment of the main menu:



Main menu
 public final class MainMenuFragment extends Fragment implements View.OnClickListener { Button sprint, challenge, twisting,evidence, exit; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.menu, container, false); //region findViewById sprint = (Button) view.findViewById(R.id.sprint); challenge = (Button) view.findViewById(R.id.challenge); twisting = (Button) view.findViewById(R.id.twist); evidence = (Button) view.findViewById(R.id.evidence); exit = (Button) view.findViewById(R.id.exit); //endregion //region setOnClickListener sprint.setOnClickListener(this); challenge.setOnClickListener(this); twisting.setOnClickListener(this); evidence.setOnClickListener(this); exit.setOnClickListener(this); //endregion return view; } @Override public void onClick(View view) { int id = view.getId(); switch (id) { case R.id.sprint: { SendingActivity mess = (SendingActivity) getActivity(); mess.send(0); break; } case R.id.challenge: { SendingActivity mess = (SendingActivity) getActivity(); mess.send(1); break; } case R.id.twist: { SendingActivity mess = (SendingActivity) getActivity(); mess.send(2); break; } case R.id.evidence: { SendingActivity mess = (SendingActivity) getActivity(); mess.send(3); break; } case R.id.exit: { SendingActivity mess = (SendingActivity) getActivity(); mess.send(4); break; } } } } 




Then I created found the buttons and hung on them the event listener. In the listener, which button was selected. Depending on this, an appropriate message is sent to the interface object. But our activity still does not respond to messages sent by the fragment. We will fix this later, and now we will write the logic of our game. Our logic is contained in the model package. There we will create an auxiliary Field class:



Field
 public final class Field { private int rightAnswer; private int sizeField; private int paintedSquare; private int square[][]; private Random randX; private Random randY; public int[][] getSquare() { return square; } public Field createSquare(int sizeField, int paintedSquare) { this.sizeField = sizeField; this.paintedSquare = paintedSquare; this.square = new int[sizeField][sizeField]; randX = new Random(System.nanoTime()); randY = new Random(System.nanoTime()); for (int i = 0; i < sizeField; i++) { for (int j = 0; j < sizeField; j++) { if (paintedSquare > 0) { paintedSquare--; this.square[i][j] = 1; } else { this.square[i][j] = 0; } } } for (int i = 0; i < sizeField; i++) { for (int j = 0; j < sizeField; j++) { mixing(square); } } return this; } //region mixing private void mixing(int[][] arr) { //,   for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr.length; j++) { swap(arr, i, j, randX.nextInt(sizeField), randY.nextInt(sizeField)); } } } private void swap(int[][] arr, int i, int j, int newI, int newJ) { int tmp = arr[i][j]; arr[i][j] = arr[newI][newJ]; arr[newI][newJ] = tmp; } //endregion public int getSizeField() { return sizeField; } public int getRightAnswer() { return rightAnswer; } public void setRightAnswer(int rightAnswer) { this.rightAnswer = rightAnswer; } public int getPaintedSquare() { return paintedSquare; } } 




As you can see this class we need to work with an array of correct answers. If the array element contains 1, then the cell is filled. The field is able to create itself. After creation, it immediately mixes itself. Now create a base Game class for game modes.



Game
 public abstract class Game { private Field field; private int currentLevel; private Activity a; Game(Activity a) { this.a = a; } Activity getActivity() { return a; } public int getCurrentLevel() { return currentLevel; } public void setCurrentLevel(int currentLevel) { this.currentLevel = currentLevel; } public void setField(Field field) { this.field = field; } public Field getField() { return field; } public Field createFieldInLevel(int level) { switch (level) { case 1: { field = new Field().createSquare(3, 2); break; } case 2: { field = new Field().createSquare(3, 3); break; } case 3: { field = new Field().createSquare(4, 4); break; } case 4: { field = new Field().createSquare(4, 6); break; } case 5: { field = new Field().createSquare(4, 8); break; } case 6: { field = new Field().createSquare(5, 10); break; } case 7: { field = new Field().createSquare(5, 12); break; } case 8: { field = new Field().createSquare(6, 14); break; } case 9: { field = new Field().createSquare(6, 16); break; } case 10: { field = new Field().createSquare(7, 18); break; } case 11: { field = new Field().createSquare(7, 20); break; } case 12: { field = new Field().createSquare(7, 22); break; } case 13: { field = new Field().createSquare(7, 24); break; } case 14: { field = new Field().createSquare(8, 26); break; } case 15: { field = new Field().createSquare(8, 28); break; } case 16: { field = new Field().createSquare(8, 30); break; } case 17: { field = new Field().createSquare(8, 32); break; } case 18: { field = new Field().createSquare(9, 34); break; } case 19: { field = new Field().createSquare(9, 36); break; } case 20: { field = new Field().createSquare(9, 40); break; } case 21: { field = new Field().createSquare(10, 42); break; } case 22: { field = new Field().createSquare(10, 44); break; } case 23: { field = new Field().createSquare(10, 46); break; } case 24: { field = new Field().createSquare(10, 48); break; } case 25: { field = new Field().createSquare(10, 50); break; } } return field; } public abstract void createVictoryDialog(LayoutInflater inflater); public abstract void createLooseDialog(LayoutInflater inflater); public abstract void showDialog(); } 




This class defines the basic functionality of the game modes. He is able to create a field, set the current level of the game, create dialog boxes (for this purpose he needs a link to activity) and show them. We cannot create objects of this class, therefore we will create classes inheritors. For example, the GameModSprint class:



GameModSprint
 private AlertDialog.Builder builder; private AlertDialog alert; public GameModSprint(Activity a) { super(a); } @Override public void createVictoryDialog(LayoutInflater inflater) { builder = new AlertDialog.Builder(getActivity()); builder.setCancelable(false); View dialogV = inflater.inflate(R.layout.dialog_view, null, false); View dialogTitle = inflater.inflate(R.layout.dialog_title, null, false); TextView title = (TextView) dialogTitle.findViewById(R.id.dialog_title); TextView message = (TextView) dialogTitle.findViewById(R.id.message); message.setText(getActivity().getResources().getString(R.string.progress)+ getCurrentLevel()); title.setText(getActivity().getResources().getString(R.string.good)); title.setText(getActivity().getResources().getString(R.string.good)); ImageView imageView = (ImageView) dialogV.findViewById(R.id.image); imageView.setImageResource(R.drawable.victory); ImageButton next = (ImageButton) dialogV.findViewById(R.id.next); ImageButton restart = (ImageButton) dialogV.findViewById(R.id.restart); View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View view) { switch (view.getId()) { case R.id.next: { SendingActivity send = (SendingActivity) getActivity(); send.nextLevel(self()); alert.dismiss(); break; } case R.id.restart: { SendingActivity send = (SendingActivity) getActivity(); send.failSprint(self()); alert.dismiss(); break; } case R.id.in_menu: { alert.dismiss(); getActivity().onBackPressed(); break; } } } }; next.setOnClickListener(listener); restart.setOnClickListener(listener); ImageButton inMenu = (ImageButton) dialogV.findViewById(R.id.in_menu); inMenu.setOnClickListener(listener); builder.setView(dialogV); builder.setCustomTitle(dialogTitle); } @Override public void createLooseDialog(LayoutInflater inflater) { builder = new AlertDialog.Builder(getActivity()); builder.setCancelable(false); View dialogV = inflater.inflate(R.layout.dialog_view, null, false); View dialogTitle = inflater.inflate(R.layout.dialog_title, null, false); TextView title = (TextView) dialogTitle.findViewById(R.id.dialog_title); TextView message = (TextView) dialogTitle.findViewById(R.id.message); message.setText(getActivity().getResources().getString(R.string.not_progress)+ getCurrentLevel()); title.setText(getActivity().getResources().getString(R.string.bad)); ImageView imageView = (ImageView) dialogV.findViewById(R.id.image); imageView.setImageResource(R.drawable.loose); ImageButton next = (ImageButton) dialogV.findViewById(R.id.next); ImageButton restart = (ImageButton) dialogV.findViewById(R.id.restart); next.setBackgroundColor(getActivity().getResources().getColor(R.color.background_no_access)); View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View view) { switch (view.getId()) { case R.id.restart: { SendingActivity send = (SendingActivity) getActivity(); send.failSprint(self()); alert.dismiss(); break; } case R.id.in_menu: { alert.dismiss(); getActivity().onBackPressed(); break; } } } }; next.setOnClickListener(listener); restart.setOnClickListener(listener); ImageButton inMenu = (ImageButton) dialogV.findViewById(R.id.in_menu); inMenu.setOnClickListener(listener); builder.setView(dialogV); builder.setCustomTitle(dialogTitle); } @Override public void showDialog() { alert = builder.create(); alert.show(); } private GameModSprint self() { return this; } } 




This class defines how dialog boxes will appear. The fact is that not always I need to just move the player to the next level. For example, in other game modes, I want to add a rating to a player or any other manipulations with user data that need to be performed only in this game mode. The remaining classes are created in the same way.



Finally, we can implement our interface.



Interface implementation
<img src = " "alt =" image "/>

 @Override public void send(int a) { switch (a) { case 0: { sprintFragment = new SprintFragment(); game = new GameModSprint(this); game.createFieldInLevel(1); game.setCurrentLevel(1); sprintFragment.setGame(game); getFragmentManager().beginTransaction().replace(R.id.activity_main, sprintFragment).addToBackStack(null).commit(); break; } case 1: { getFragmentManager().beginTransaction().replace(R.id.activity_main, levelChallengeFragment).addToBackStack(null).commit(); break; } case 2: { twistingFragment = new TwistingFragment(); game = new GameModTwisting(this); game.createFieldInLevel(1); game.setCurrentLevel(1); twistingFragment.setGame(game); getFragmentManager().beginTransaction().replace(R.id.activity_main, twistingFragment).addToBackStack(null).commit(); break; } case 3: { evidenceFragment = new EvidenceFragment(); game = new GameModEvidence(this); game.createFieldInLevel(1); game.setCurrentLevel(1); evidenceFragment.setGame(game); getFragmentManager().beginTransaction().replace(R.id.activity_main, evidenceFragment).addToBackStack(null).commit(); break; } case 4: { finish(); break; } } } @Override public void send(Field field, int level) { challengeFragment = new ChallengeFragment(); game = new GameModChallenge(this); game.setField(field); game.setCurrentLevel(level); challengeFragment.setGame(game); getFragmentManager().beginTransaction().replace(R.id.activity_main, challengeFragment).commit(); } @Override public void nextLevel(GameModChallenge game) { challengeFragment = new ChallengeFragment(); game.setCurrentLevel(game.getCurrentLevel() + 1); game.createFieldInLevel(game.getCurrentLevel()); textPoint.setText(String.valueOf(point)); textRating.setText(String.valueOf(rating)); challengeFragment.setGame(game); getFragmentManager().beginTransaction().replace(R.id.activity_main, challengeFragment).commit(); } @Override public void nextLevel(GameModSprint game) { sprintFragment = new SprintFragment(); textPoint.setText(String.valueOf(point)); game.setCurrentLevel(game.getCurrentLevel() + 1); game.createFieldInLevel(game.getCurrentLevel()); sprintFragment.setGame(game); getFragmentManager().beginTransaction().replace(R.id.activity_main, sprintFragment).commit(); } @Override public void nextLevel(GameModTwisting game) { twistingFragment = new TwistingFragment(); textPoint.setText(String.valueOf(point)); game.setCurrentLevel(game.getCurrentLevel() + 1); game.createFieldInLevel(game.getCurrentLevel()); twistingFragment.setGame(game); getFragmentManager().beginTransaction().replace(R.id.activity_main, twistingFragment).commit(); } @Override public void nextLevel(GameModEvidence game) { evidenceFragment = new EvidenceFragment(); textPoint.setText(String.valueOf(point)); game.setCurrentLevel(game.getCurrentLevel() + 1); game.createFieldInLevel(game.getCurrentLevel()); evidenceFragment.setGame(game); getFragmentManager().beginTransaction().replace(R.id.activity_main, evidenceFragment).commit(); } @Override public void next(GameModChallenge game) { //,     textPoint.setText(String.valueOf(point)); challengeFragment = new ChallengeFragment(); game.createFieldInLevel(game.getCurrentLevel()); challengeFragment.setGame(game); getFragmentManager().beginTransaction().replace(R.id.activity_main, challengeFragment).commit(); } @Override public void failSprint(GameModSprint game) { sprintFragment = new SprintFragment(); game = new GameModSprint(this); game.createFieldInLevel(1); game.setCurrentLevel(1); sprintFragment.setGame(game); getFragmentManager().beginTransaction().replace(R.id.activity_main, sprintFragment).commit(); } @Override public void failTwisting(GameModTwisting game) { twistingFragment = new TwistingFragment(); game = new GameModTwisting(this); game.createFieldInLevel(1); game.setCurrentLevel(1); twistingFragment.setGame(game); getFragmentManager().beginTransaction().replace(R.id.activity_main, twistingFragment).commit(); } @Override public void failEvidence(GameModEvidence game) { evidenceFragment = new EvidenceFragment(); game = new GameModEvidence(this); game.createFieldInLevel(1); game.setCurrentLevel(1); evidenceFragment.setGame(game); getFragmentManager().beginTransaction().replace(R.id.activity_main, evidenceFragment).commit(); } 




Now a fragment of the game level has everything you need to create. He knows everything he needs about the current field. It remains only to link our data with the user interface.



Fragment code
 public final class SprintFragment extends Fragment implements View.OnClickListener { private Game game; private Field field; private int fieldSize; private Button sq[][]; private LayoutInflater inflater; private Runnable runner; private View root; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { root = inflater.inflate(R.layout.game, container, false); TextView title = (TextView) root.findViewById(R.id.game_challenge_title); title.setText(String.valueOf(getActivity().getResources().getString(R.string.current_level) + game.getCurrentLevel())); this.inflater = inflater; final View.OnClickListener listener = this; int doNotDuplicateId = 0; final TableLayout fieldView = (TableLayout) root.findViewById(R.id.field); ProgressBar progressBar = (ProgressBar) root.findViewById(R.id.progress_bar); progressBar.setVisibility(View.INVISIBLE); fieldSize = field.getSizeField(); sq = new Button[fieldSize][fieldSize]; fieldView.setWeightSum(fieldSize); TableRow tableRows[] = new TableRow[fieldSize]; TableRow.LayoutParams paramsRow = new TableRow.LayoutParams(); paramsRow.height = TableRow.LayoutParams.MATCH_PARENT; paramsRow.width = TableRow.LayoutParams.WRAP_CONTENT; paramsRow.weight = 1; for (int i = 0; i < fieldSize; i++) { tableRows[i] = new TableRow(getActivity().getApplicationContext()); tableRows[i].setLayoutParams(paramsRow); for (int j = 0; j < fieldSize; j++) { sq[i][j] = (Button) inflater.inflate(R.layout.one_field_button, null, false); sq[i][j].setId(doNotDuplicateId); sq[i][j].setWidth(getActivity().getWindow().getWindowManager().getDefaultDisplay().getWidth() / fieldSize - getActivity().getWindow().getWindowManager().getDefaultDisplay().getWidth() / (fieldSize * fieldSize)); sq[i][j].setHeight(getActivity().getWindow().getWindowManager().getDefaultDisplay().getWidth() / fieldSize - getActivity().getWindow().getWindowManager().getDefaultDisplay().getWidth() / (fieldSize * fieldSize)); doNotDuplicateId++; if (field.getSquare()[i][j] == 1) { sq[i][j].setBackgroundDrawable(getActivity().getApplicationContext().getResources().getDrawable(R.drawable.sq_painted)); } tableRows[i].addView(sq[i][j], j); tableRows[i].setWeightSum(fieldSize); } fieldView.addView(tableRows[i], i); } root.postDelayed(runner = new Runnable() { @Override public void run() { for (int i = 0; i < fieldSize; i++) { for (int j = 0; j < fieldSize; j++) { sq[i][j].setOnClickListener(listener); if (field.getSquare()[i][j] == 1) { sq[i][j].setBackgroundDrawable(getActivity().getApplicationContext().getResources().getDrawable(R.drawable.sq)); } } } } }, 2000L); return root; } public void setGame(Game game) { this.game = game; this.field = game.getField(); } @Override public void onClick(View view) { int id = view.getId(); for (int i = 0; i < fieldSize; i++) { for (int j = 0; j < fieldSize; j++) { if (id == sq[i][j].getId()) { System.out.println(sq[i][j].getId()); if (field.getSquare()[i][j] == 1) { field.setRightAnswer(field.getRightAnswer() + 1); view.setOnClickListener(null); view.setBackgroundDrawable(getActivity().getApplicationContext().getResources().getDrawable(R.drawable.sq_painted)); if (field.getRightAnswer() == field.getPaintedSquare()) { game.createVictoryDialog(inflater); game.showDialog(); } } else if (field.getSquare()[i][j] == 0) { view.setBackgroundDrawable(getActivity().getApplicationContext().getResources().getDrawable(R.drawable.sq_wrong)); game.createLooseDialog(inflater); game.showDialog(); } } } } } @Override public void onStop() { super.onStop(); root.removeCallbacks(runner); } } 




Pay attention to this method:



  public void setGame(Game game) { this.game = game; this.field = game.getField(); } 


Through it, the activity transmits a link to the game object to the fragment. Also, in order not to use long dot notation, create a separate link for the field.



The code is generally very simple. An array of buttons is created, populated. It turns out that the indices of a two-dimensional array of buttons coincide with the indices of a two-dimensional array from an object of class Field.



Thus, when determining the pressed button, it is necessary to check which digit lies in the array of the Fild class object. If 0, the user made an incorrect input and you need to end the game with a call.



 game.createLooseDialog(inflater); game.showDialog(); 


which will create and open the dialog box we need from the choices of actions.



If the player has selected all elements equal to 1, then a winning dialog box is created. Despite the fact that we have a reference of the Game type, the object was created by an activity of the GameModSprint type, this means that the corresponding overdefined version of this method will be called:



Victory
 if (field.getRightAnswer() == field.getPaintedSquare()) { game.createVictoryDialog(inflater); game.showDialog(); } 




Thus, we use the most powerful tool of the Java language - dynamic method dispatching.



Conclusion



So, we got acquainted with the development of mobile applications and this is what happened:

sprint game mode
Level selection:



And the game itself:





Link to download the game from Google Play

play.google.com/store/apps/details?id=com.thekingames.memorize

Good luck everyone!

Source: https://habr.com/ru/post/322008/



All Articles