[프로젝트] 자문자답 앱 - 기능 구현(1)

유의선·2023년 9월 9일
0

데이터베이스를 제외한 액티비티와 그 안의 뷰들, 그리고 그 기능들을 데이터베이스 없이 구현할 수 있는 부분까지 구현하였다.


MainActivity.java / activity_main.xml

package org.techtown.qself;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    public static final int REQUEST_CODE_MAKE = 101;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button makeButton = findViewById(R.id.makeButton);
        Button listButton = findViewById(R.id.listButton);
        Button solveButton = findViewById(R.id.solveButton);

        makeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), MakeActivity.class);
                startActivityForResult(intent, REQUEST_CODE_MAKE);
            }
        });

        listButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), ListActivity.class);
                startActivity(intent);
            }
        });

        solveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), SolveActivity.class);
                startActivity(intent);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if(requestCode == REQUEST_CODE_MAKE){
            if(resultCode == RESULT_OK){
                Toast.makeText(getApplicationContext(),"문제가 만들어졌습니다.", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

앱을 키면 가장 먼저 나오는 화면이다.

Intent를 사용한 startAcitivty() 메소드와 startActivityForResult() 메소드를 사용해 문제 만들기 / 문제 목록 / 문제 풀기 화면으로 이동하도록 만들었다.

문제 만들기 화면 전환의 경우 startActivityForResult() 메소드를 사용해 결과를 받을 수 있게 만들었으며,
onActivityResult() 메소드를 제정의하여 문제가 만들어졌다는 결과를 받으면 Toast로 메세지를 띄워주도록 하였다.


MakeActivity.java / activity_make.xml

package org.techtown.qself;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MakeActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_make);

        EditText editTextTitle = findViewById(R.id.editTextTitle);
        EditText editTextQuestion = findViewById(R.id.editTextQuestion);
        EditText editTextAnswer = findViewById(R.id.editTextAnswer);

        Button buttonMakeQuestion = findViewById(R.id.buttonMakeQuestion);

        buttonMakeQuestion.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String title = editTextTitle.getText().toString();
                String question = editTextQuestion.getText().toString();
                String answer = editTextAnswer.getText().toString();

                Intent intent = new Intent();
                setResult(RESULT_OK, intent);
                finish();
            }
        });
    }
}

MainActivity에서 버튼을 눌러 접근하는 문제 만들기 화면이다.

EditText 뷰들에 문제의 제목 / 문제내용 / 정답 을 입력받게 하였으며,
문제 만들기 버튼을 누르면 EditText뷰들의 내용을 참조해 데이터베이스에 저장하며,
MainActivity로 Intent를 통해 RESULT_OK 응답을 보내 문제가 만들어졌음을 알리도록 만들었다.

데이터베이스에 문제를 저장하는 기능은 데이터베이스를 구현한 후에 구현하려고 한다.


ListActivity.java / activity_list.xml

package org.techtown.qself;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class ListActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list);

        RecyclerView recyclerView = findViewById(R.id.RecyclerView);

        LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(layoutManager);
        QuestionAdapter adapter = new QuestionAdapter();

        adapter.addItem(new Question(1, "제목1제목1제목1", "내용1내용1내용1", "정답1정답1정답1"));
        adapter.addItem(new Question(1, "제목2제목2제목2", "내용2내용2내용2", "정답2정답2정답2"));

        adapter.setOnItemClickListener(new OnQuestionClickListener() {
            @Override
            public void onItemClick(QuestionAdapter.ViewHolder holder, View view, int position) {
                Question item = adapter.getItem(position);

                Intent intent = new Intent(getApplicationContext(), ListQActivity.class);
                intent.putExtra("data", item);
                startActivity(intent);
            }
        });

        recyclerView.setAdapter(adapter);
    }
}

MainActivity에서 버튼을 눌러 접근하는 문제 목록 화면이다.

화면은 리사이클러뷰 하나로 구성하였다.


리사이클러뷰에 들어갈 뷰인 question_box.xml은 다음과 같이 직접 정의하였다.

package org.techtown.qself;

import android.os.Parcel;
import android.os.Parcelable;

public class Question implements Parcelable {
    int num;
    String title;
    String question;
    String answer;

    public Question(int num, String title, String question, String answer) {
        this.num = num;
        this.title = title;
        this.question = question;
        this.answer = answer;
    }

    public Question(Parcel src){
        num = src.readInt();
        title = src.readString();
        question = src.readString();
        answer = src.readString();
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getQuestion() {
        return question;
    }

    public void setQuestion(String question) {
        this.question = question;
    }

    public String getAnswer() {
        return answer;
    }

    public void setAnswer(String answer) {
        this.answer = answer;
    }

    public static final Parcelable.Creator CREATOR = new Parcelable.Creator(){
        public Question createFromParcel(Parcel in){
            return new Question(in);
        }
        public Question[] newArray(int size){
            return new Question[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(num);
        dest.writeString(title);
        dest.writeString(question);
        dest.writeString(answer);
    }
}

뷰는 문제의 제목이 보이도록 TextView하나를 넣었으며 cardView를 사용해 디자인했다.

뷰는 문제의 번호/제목/내용/정답 을 담도록 만들었다. 이 부분은 데이터베이스를 구현하며 조금 수정할지도 모른다.

리사이클러뷰 안에 넣은 이 뷰를 클릭했을 때 이 Question.java 객체를 Intent로 전달할 수 있게 만들기 위해 Parcel을 상속하여 구현하였다.


다음은 리사이클러뷰 안의 뷰들을 관리하는 어댑터 QuestionAdapter.java를 정의하였다.

package org.techtown.qself;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class QuestionAdapter extends RecyclerView.Adapter<QuestionAdapter.ViewHolder> implements OnQuestionClickListener{

    ArrayList<Question> items = new ArrayList<Question>();
    OnQuestionClickListener listener;

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from((parent.getContext()));
        View itemView = inflater.inflate(R.layout.question_box, parent, false);

        return new ViewHolder(itemView, this);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Question item = items.get(position);
        holder.setItem(item);
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    public void addItem(Question item){
        items.add(item);
    }

    public void setItems(ArrayList<Question> items){
        this.items = items;
    }

    public Question getItem(int position){
        return items.get(position);
    }

    public void setItem(int position, Question item){
        items.set(position, item);
    }

    public void setOnItemClickListener(OnQuestionClickListener listener){
        this.listener = listener;
    }

    @Override
    public void onItemClick(ViewHolder holder, View view, int position) {
        if(listener != null){
            listener.onItemClick(holder, view, position);
        }
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        TextView textView;

        public ViewHolder(View itemView, final OnQuestionClickListener listener){
            super(itemView);

            textView = itemView.findViewById(R.id.textView);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position= getAdapterPosition();

                    if(listener != null){
                        listener.onItemClick(ViewHolder.this, v, position);
                    }
                }
            });
        }

        public void setItem(Question item){
            textView.setText(item.getTitle());
        }
    }
}

어댑터에는 기본적인 기능에 더해 안의 뷰를 클릭했을 때 이벤트를 만들기 위해
OnQuestionClickListener 인터페이스를 만들고 이를 상속하였다.

package org.techtown.qself;

import android.view.View;

public interface OnQuestionClickListener {
    public void onItemClick(QuestionAdapter.ViewHolder holder, View view, int position);
}

이 어댑터는 문제풀이 화면에서도 동일하게 사용하면서, 클릭이벤트는 다르게 구현해야 했기 때문에 인터페이스를 사용하였다.


다시 ListActivity.java의 내용으로 돌아오겠다.

우선 두개의 뷰를 리사이클러뷰에 추가했는데, 이 내용은 추후 데이터베이스의 문제들을 넣도록 수정할예정이다.

adapter.setOnItemClickListener 로 구현한 클릭이벤트에서는
각 뷰를 클릭하면 그 뷰 객체를 intent를 통해 ListQActivity.java로 보내며 화면을 전환하도록 만들었다.


ListQActivity.java / activity_list_q.xml

package org.techtown.qself;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;

public class ListQActivity extends AppCompatActivity {
    TextView title;
    TextView question;
    TextView answer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_q);

        title = findViewById(R.id.textTitle);
        question = findViewById(R.id.textQuestion);
        answer = findViewById(R.id.textAnswer);

        Intent intent = getIntent();

        if(intent != null){
            Bundle bundle = intent.getExtras();
            Question data = bundle.getParcelable("data");

            title.setText(data.getTitle());
            question.setText(data.getQuestion());
            answer.setText(data.getAnswer());
        }
    }
}

ListActivity.java에서 문제 제목을 보여주는 뷰를 클릭해서 접근할 수 있는 문제 확인 화면이다.

ListActivity에서 뷰를 클릭하면 Question 객체를 Intent를 통해 전달한다.
전달받은 Question 객체에서 문제의 제목/내용/정답을 받아 화면의 TextView를 통해 보여주도록 하였다.

지금은 Question 객체에 문제에 모든 내용을 담아 전달하도록 만들었지만, 데이터베이스를 구현한 후에는 데이터베이스에서 내용을 받아오도록 수정할지도 모르겠다.


SolveActivity.java / activity_solve.xml

package org.techtown.qself;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class SolveActivity extends AppCompatActivity {
    public static final int REQUEST_CODE_SOLVE = 102;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_solve);

        RecyclerView recyclerView = findViewById(R.id.RecyclerView);

        LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(layoutManager);
        QuestionAdapter adapter = new QuestionAdapter();

        adapter.addItem(new Question(1, "제목1제목1제목1", "내용1내용1내용1", "정답1정답1정답1"));
        adapter.addItem(new Question(1, "제목2제목2제목2", "내용2내용2내용2", "정답2정답2정답2"));

        adapter.setOnItemClickListener(new OnQuestionClickListener() {
            @Override
            public void onItemClick(QuestionAdapter.ViewHolder holder, View view, int position) {
                Question item = adapter.getItem(position);

                Intent intent = new Intent(getApplicationContext(), SolveQActivity.class);
                intent.putExtra("data", item);
                startActivity(intent);
            }
        });

        recyclerView.setAdapter(adapter);
    }
}

MainActivity에서 문제 풀기 버튼을 통해 접근할 수 있는 문제 풀이 목록 화면이다.

이전 글에선 차례대로 풀기 버튼이 있었지만 이를 제거하였고, 각 문제에서 바로 앞뒤로 이동할 수 있게 수정할 생각이다.

기본적인 내용은 ListActivity와 동일하다.
다른 점은 adapter.setOnItemClickListener를 통해 이동하는 화면이 SolveQActivity라는 점이다.


SolveQActivity.java / activity_solve_q.xml

package org.techtown.qself;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class SolveQActivity extends AppCompatActivity {
    TextView title;
    TextView question;
    String answer;
    int num;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_solve_q);

        title = findViewById(R.id.textTitle);
        question = findViewById(R.id.textQuestion);
        Button buttonAnswer = findViewById(R.id.buttonShowAnswer);
        Button buttonBefore = findViewById(R.id.buttonBefore);
        Button buttonAfter = findViewById(R.id.buttonNext);

        Intent intent = getIntent();

        if(intent != null){
            Bundle bundle = intent.getExtras();
            Question data = bundle.getParcelable("data");

            title.setText(data.getTitle());
            question.setText(data.getQuestion());
            answer = data.getAnswer();
            num = data.getNum();
        }

        buttonAnswer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), AnswerDialog.class);
                intent.putExtra("answer", answer);
                startActivity(intent);
            }
        });
    }
}

SolveActivity에서 문제 뷰를 클릭해 접근할 수 있는 문제 풀기 화면이다.

SolveActivity에서 클릭한 뷰의 Question 객체를 받아와 문제의 제목과 내용을 TextView에 표시한다.

정답확인 버튼을 누르면 Intent로 Question 객체에서 받은 정답을 보내 정의한 AnswerDialog 대화상자를 띄워서 정답을 확인할 수 있도록 만들었다.

이전 문제와 다음 문제로 넘어갈 수 있도록 버튼을 두개 추가하였다. 이 기능은 데이터베이스의 구현 후에 구현할 예정이다.


AnswerDialog.java / activity_answer_dialog.xml


package org.techtown.qself;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class AnswerDialog extends AppCompatActivity {
    TextView textAnswer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_answer_dialog);

        textAnswer = findViewById(R.id.textAnswer);
        Button buttonBack = findViewById(R.id.buttonBack);

        Intent intent = getIntent();
        if(intent != null){
            Bundle bundle = intent.getExtras();
            String answer = bundle.getString("answer");
            textAnswer.setText(answer);
        }

        buttonBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                finish();
            }
        });
    }
}

SolveQActivity에서 정답 확인 버튼을 통해 접근하는 정답 확인 화면이다.

SolveQActivity에서 받은 Intent에 들어있는 정답을 받아 텍스트뷰에 띄우도록 하였다.

돌아가기 버튼을 누르면 다시 SolveQActivity로 돌아가도록 하였다.


기능 구현에 있어 남은 일은 데이터베이스를 만들고 데이터베이스의 기능을 사용해 문제를 저장, 불러오기 등의 기능을 구현하는 것이다.
지금은 만들어두지 않았지만 데이터베이스를 구현한다면 문제의 수정 삭제 기능도 추가할 생각이다.

그러기위해선 다음 과제는 데이터베이스의 구현이 될 것 같다.

0개의 댓글