Android Study

Android Study(Making App #Quiz)

Box Maker 2023. 4. 7. 00:19

1. Quiz 앱 만들기

- 목표

 

  • 총 8개의 퀴즈를 만들어 화면에 퀴즈가 1번부터 8번까지 하나씩 보이게 합니다.
  • True, False 버튼을 만들어 사용자가 퀴즈의 답을 클릭하면 정답인지 아닌지 알려줍니다.
  • Prev, Next 버튼을 만들어 1번부터 8번까지 퀴즈를 앞뒤로 이동할수 있게 합니다.

 

완성화면)

 

 

1) TextView와 Button 만들기

 

  • 퀴즈가 들어가는 TextView와 각 Button을 디자인한 xml코드는 다음과 같다.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">


    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#2196F3"
        tools:context=".MainActivity">

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:contentDescription="@string/image_description"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:srcCompat="@drawable/ic_launcher_foreground" />

        <TextView
            android:id="@+id/question_text_view"
            style="@style/TextStyle"
            android:layout_width="0dp"
            android:layout_height="29dp"
            android:layout_marginTop="40dp"
            android:text="@string/placeholder"
            android:textColor="#212121"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/imageView" />

        <Button
            android:id="@+id/true_button"
            style="@style/ButtonStyle"
            android:text="@string/true_button_text"
            android:textColor="#000000"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/false_button"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/imageView" />

        <Button
            android:id="@+id/false_button"
            style="@style/ButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:layout_marginBottom="8dp"
            android:text="@string/false_button_text"
            android:textColor="#000000"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toEndOf="@+id/true_button"
            app:layout_constraintTop_toBottomOf="@+id/imageView" />

        <Button
            android:id="@+id/next_button"
            style="@style/ButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:text="@string/next_button_text"
            android:textColor="#000000"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toEndOf="@+id/prev_button"
            app:layout_constraintTop_toBottomOf="@+id/true_button" />

        <Button
            android:id="@+id/prev_button"
            style="@style/ButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:text="@string/prev_button_text"
            android:textColor="#000000"
            app:layout_constraintEnd_toStartOf="@+id/next_button"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintHorizontal_chainStyle="packed"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/false_button" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

style.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <style name="TextStyle">
        <item name="android:layout_marginStart">8dp</item>
        <item name="android:layout_marginEnd">8dp</item>
        <item name="android:fontFamily">@font/croissant_one</item>
        <item name="android:padding">@dimen/text_padding</item>
        <item name="android:textAlignment">textStart</item>
    </style>

    <style name="ButtonStyle" parent="TextStyle">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_marginTop">8dp</item>
        <item name="android:layout_marginBottom">8dp</item>
        <item name="android:backgroundTint">#D15858</item>
        <item name="android:textAlignment">center</item>
    </style>
</resources>

 

string.xml

<resources>
    <string name="app_name">True Citizen</string>
    <string name="false_button_text">false</string>
    <string name="true_button_text">True</string>
    <string name="prev_button_text">prev</string>
    <string name="next_button_text">next</string>
    <string name="placeholder">Question goes here</string>
    <string name="image_description">A droid icon image</string>

    <string name="question_declaration">The (U.S.) Declaration of Independence was Adopted in 1776.</string>
    <string name="correct_answer">That\'s correct</string>
    <string name="wrong_answer">That\'s incorrect</string>
    <string name="question_constitution">The Supreme law of the land is the Constitution.</string>
    <string name="question_amendments">The (U.S.) Constitution has 26 Amendments.</string>
    <string name="question_independence_rights">The two rights in the Declaration of Independence are:
        \n \t <b>life</b> \n  \t <b>pursuit of happiness</b>.</string>
    <string name="question_religion">Freedom of religion means:
        \n \t <b>You can practice any religion, or not practice a religion</b>.</string>
    <string name="question_government">Journalists is one branch or part of the government.</string>
    <string name="question_government_feds">Congress does not make federal laws.</string>
    <string name="question_government_senators">There are one hundred (100) U.S. Senators.</string>
</resources>

 

2) 기능 구현하기

목표에서 설명한 순서대로 다음과 같이 기능을 구현해보겠다.

 

2-1 화면에 퀴즈 출력

Question.java

public class Question {
    private int answerResId;
    private boolean answerTrue;

    public Question(int answerResId, boolean answerTrue) {
        this.answerResId = answerResId;
        this.answerTrue = answerTrue;
    }

    public int getAnswerResId() {
        return answerResId;
    }

    public void setAnswerResId(int answerResId) {
        this.answerResId = answerResId;
    }

    public boolean isAnswerTrue() {
        return answerTrue;
    }

    public void setAnswerTrue(boolean answerTrue) {
        this.answerTrue = answerTrue;
    }
}
  • Question 클래스를 새로 생성한다.
  • 생성자를 생성하여 각 퀴즈의 id값(answerResId), 퀴즈의 정답(answerTrue)변수들을 초기화한다.
  • 다른 클래스에서 변수를 수정할 수 있도록 getter/setter를 생성한다.

 

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;

    private Question[] questionBank = new Question[] {
            //create/instantiate/question object
            new Question(R.string.question_amendments, false),
            new Question(R.string.question_constitution, true),
            new Question(R.string.question_declaration, true),
            new Question(R.string.question_independence_rights, true),
            new Question(R.string.question_religion, true),
            new Question(R.string.question_government, false),
            new Question(R.string.question_government_feds, false),
            new Question(R.string.question_government_senators, true),
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
}
  • 뷰의 id값을 해당 파일로 가져오기 위해서 findViewById()가 아닌 binding을 사용한다.
  • 퀴즈들을 배열로 선언하기 위해서 배열형태의 questionBank를 생성한다.
  • string.xml에 있는 문제들의 id이름을 정답과 같이 하나씩 배열의 인스턴스로 생성한다.
  • setContentView로 화면을 디자인한 리소스 id를 해당 파일에서 사용할 수 있게 한다.
  • 그 밑줄은 binding 방식으로 리소스 id를 해당파일에서 사용할 수 있게 해준다.

 

2-2 Prev, Next 버튼 기능 구현

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
    private int currentQuestionIndex = 0;

binding.nextButton.setOnClickListener(view -> {
            currentQuestionIndex = (currentQuestionIndex + 1) % questionBank.length;
            updateQuestion();

        });

        binding.prevButton.setOnClickListener(view -> {
            if (currentQuestionIndex > 0) {
                currentQuestionIndex = (currentQuestionIndex - 1) % questionBank.length;
                updateQuestion();
            }

        });
    }
    
    private void updateQuestion() {
        binding.questionTextView.setText(questionBank[currentQuestionIndex].getAnswerResId());

    }
}
  • next버튼 클릭 시 다음퀴즈를 보여주고 prev버튼 클릭 시 이전 퀴즈를 보여준다.
  • 먼저 updateQuestion 메소드를 생성한다.
  • 해당 메소드는 questionBank 배열을 이용해 퀴즈id값을 가져온다.
  • 처음 페이지에서 next 버튼을 클릭하면 (0+1)%8이 되어 나머지값인 1이 currentQuestionIndex 변수에 삽입된다. 따라서 updateQuestion 메소드로 넘어가 questionBank[1]이 되어 2번째 퀴즈를 보여주게 된다.
  • 2번째 페이지에서 prev버튼을 클릭하면 (1-1)%8이 되어 나머지값인 0이 currentQuestionIndex 변수에 삽입된다. 따라서 updateQuestion 메소드로 넘어가 questionBank[0]이 되어 1번째 퀴즈를 보여주게 된다.

 

2-3 True, False 버튼 기능 구현

MainActivity.java

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.trueButton.setOnClickListener(view -> checkAnswer(true));
        binding.falseButton.setOnClickListener(view -> checkAnswer(false));
	}

	private void checkAnswer(boolean userChoseCorrect) {
        boolean answerIsCorrect = questionBank[currentQuestionIndex].isAnswerTrue();
        int messageId;

        if (answerIsCorrect == userChoseCorrect) {
            messageId = R.string.correct_answer;
        }else {
            messageId = R.string.wrong_answer;
        }
        Snackbar.make(binding.imageView, messageId, Snackbar.LENGTH_SHORT)
                .show();

    }
  • checkAnswer 메소드를 생성해 boolean 리턴타입을 가진 userChosenCorrect변수를 선언한다.
  • answerIsCorrect 변수에 각 퀴즈에 실제 정답을 가져와 삽입한다.
  • 정답과 오답시에 출력문들의 리소스값을 messageId 변수에 삽입한다.
  • Snackbar를 이용해 정답인지 오답인지 출력한다.

 

2. 앱 실행