2022.11.24 - [프로젝트] - 채팅앱 만들기 서버 없이 DB 까지
채팅앱 만들기 서버 없이 DB 까지
이번에 만들어 볼 내용은 채팅앱의 기본 프레임을 짜보는 것이다. 간단하게 정리하면, 보내는 사람, 받는 사람, 이전의 대화 내용, 보낸 시간과 날짜가 나오도록 만들 것이다. 이 앱을 만드는 과
parkjunbackend.tistory.com
저번에 만든 front부분에 이어서 이번에는 back부분을 구현해보겠다.
먼저 구성요소는 MainActivity와 Message, MessageType, MessageAdapter, DBHelper이다.
가장 먼저 MainActivity 부분에서 해야 할 일은 텍스트뷰에 입력한 메세지 내용을 읽어와서 전송버튼을 누르면,
DB부분에 메세지 내용을 저장하고, 그 내용을 불러와서 화면에 띄워주는 부분을 구현한다.
아래의 코드가 내가 구현한 MainActivity다.
public class MainActivity extends AppCompatActivity {
private DBHelper dbHelper;
private List<Message> list;
private EditText editText;
private MessageAdapter messageAdapter;
private RecyclerView recyclerView;
private LinearLayoutManager layoutManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// DBHelper 객체를 생성하여 테이블 생성 및 데이터 가져오기
dbHelper = new DBHelper(this, 1);
Button sendButton = findViewById(R.id.sendButton);//전송 버튼
recyclerView = findViewById(R.id.recyclerView);
editText = findViewById(R.id.contentsEdit);
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
sendButton.setEnabled(false);
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
sendButton.setEnabled(false);
}
@Override
public void afterTextChanged(Editable editable) {
if(editable.length()>0){
sendButton.setEnabled(true);
}
}
});
// sendButton.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View view) {
// sendAction(view);
// editText.setText("");
// }
// });
list = dbHelper.selectAll();
layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL,false);
recyclerView.setLayoutManager(layoutManager);
messageAdapter = new MessageAdapter(list);
recyclerView.setAdapter(messageAdapter);
recyclerView.smoothScrollToPosition(list.size());
}
/*
전송 버튼을 누를 때 동작 되는 메소드
*/
public void sendAction(View view){
String ms = editText.getText().toString();
long id = dbHelper.insert(ms,MessageType.RIGHT_CONTENTS);
list.add(dbHelper.selectOne(id));
messageAdapter.notifyDataSetChanged();
recyclerView.smoothScrollToPosition(list.size());
editText.setText("");
}
}
먼저 필요한 필드요소들을 정의하고 이것들을 모두 레이아웃과 연결하고, 객체를 생성한다.
private DBHelper dbHelper;
private List<Message> list;
private EditText editText;
private MessageAdapter messageAdapter;
private RecyclerView recyclerView;
private LinearLayoutManager layoutManager;
그 다음에 뷰들을 인플레이션 해주기위해서 onCreate()메서드 안을 정의해주어야한다.
메세지 전송버튼의 뷰와 메세지 내용을 입력한 텍스트 뷰, 내용을 보여줄 recyclerView를 인플레이션 한다.
그리고 DB객체를 생성하고, DB객체에 전송 버튼을 누르면 데이터를 보내고, 화면에 띄우도록 구현한다.
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
sendButton.setEnabled(false);
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
sendButton.setEnabled(false);
}
@Override
public void afterTextChanged(Editable editable) {
if(editable.length()>0){
sendButton.setEnabled(true);
}
}
});
위 코드는 텍스트뷰에 입력 내용이 없으면 전송 버튼을 비활성화하고 내용이 입력되면 전송 버튼을 활성화 시키기 위한 코드이다.
TextWatcher에 대해서는 따로 더 알아보고 싶다면 찾아보도록...
그 다음은 이제 DB에 저장된 모든 대화내용을 불러오고, recyclerView의 Layout으로 관리해줄 LayoutManager를 구현하고, recyclerView에 내용을 띄워줄 Adapter를 구현해서 연결해주는 코드다.
list = dbHelper.selectAll();
layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL,false);
recyclerView.setLayoutManager(layoutManager);
messageAdapter = new MessageAdapter(list);
recyclerView.setAdapter(messageAdapter);
recyclerView.smoothScrollToPosition(list.size());//스크롤을 부드럽게 만드는 것
그 다음으로 메세지 클래스와 메세지 타입 클래스를 구현해보자.
// 메세지 데이터를 저장하는 객체(Value Object)
public class Message {
private int id;
private String message;
private String registerDate;
private MessageType messageType;
public Message(String message) {
this.message = message;
}
public Message(int id, String message, String registerDate, MessageType messageType) {
this.id = id;
this.message = message;
this.registerDate = registerDate;
this.messageType = messageType;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getRegisterDate() {
return registerDate;
}
public void setRegisterDate(String registerDate) {
this.registerDate = registerDate;
}
public MessageType getMessageType() {
return messageType;
}
public void setMessageType(MessageType messageType) {
this.messageType = messageType;
}
@Override
public String toString() {
return "Message{" +
"id=" + id +
", message='" + message + '\'' +
", registerDate='" + registerDate + '\'' +
", messageType=" + messageType +
'}';
}
}
DB에서 받아오고 보낼 데이터라고 생각하자.
public enum MessageType {
NONE(-1), LEFT_CONTENTS(0), CENTER_CONTENTS(1), RIGHT_CONTENTS(2);
private int code;
MessageType(int code) {
this.code = code;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public static MessageType of(int code) {
MessageType[] types = MessageType.values();
for (MessageType type : types) {
if (type.getCode() == code) {
return type;
}
}
return NONE;
}
}
그 다음 DB에서 받아온 데이터들을 메인 액티비티에서 보여주기 위해 Adapter를 구현해야한다.
public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private List<Message> list;
/*
위의 list 변수에는 null이 초기화되어 있으므로 list에 값을 넣어줄 수 있는 생성자나 메소드 정의해야 함
*/
public MessageAdapter(List<Message> list) {
this.list = list;
}
public void addList(Message ms){
list.add(ms);
}
public void setList(List<Message> list){
this.list=list;
}
public Message getMessage(int position){
return list.get(position);
}
public void setMs(int position, Message item){
list.set(position,item);
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (Objects.requireNonNull(MessageType.of(viewType))) {
case LEFT_CONTENTS:
return new LeftViewHolder(inflater.inflate(R.layout.message_left_item, parent, false));
case RIGHT_CONTENTS:
return new RightViewHolder(inflater.inflate(R.layout.message_right_item, parent, false));
default:
return new CenterViewHolder(inflater.inflate(R.layout.message_center_item, parent, false));
}
}//뷰 타입에 따라 생성 할 ViewHolder를 선택한다.
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if(holder instanceof RightViewHolder){
((RightViewHolder) holder).messageText.setText(list.get(position).getMessage());
((RightViewHolder)holder).datetimeText.setText(list.get(position).getRegisterDate());
}else if(holder instanceof LeftViewHolder){
((LeftViewHolder)holder).messageText.setText(list.get(position).getMessage());
((LeftViewHolder)holder).datetimeText.setText(list.get(position).getRegisterDate());
}else if(holder instanceof CenterViewHolder){
((CenterViewHolder)holder).datetimeText.setText(list.get(position).getRegisterDate());
}
}//뷰 타입에 따라 연결할 뷰를 선택하여 내용을 바꾸어 넣는다.
@Override
public int getItemCount() {
return list.size();
}
// 이 메소드를 재정의 하면 onCreateViewHolder 메소드의 두번째 파라미터 viewType 변수에 이 메소드의 리턴값이 들어간다.
@Override
public int getItemViewType(int position) {
// 해당 메시지의 message_type 을 리턴
return list.get(position).getMessageType().getCode();
}
private class RightViewHolder extends RecyclerView.ViewHolder {
private final TextView messageText;
private final TextView datetimeText;
public RightViewHolder(@NonNull View itemView) {
super(itemView);
messageText = itemView.findViewById(R.id.messageText);
datetimeText = itemView.findViewById(R.id.datetimeText);
}
}//보내는 사람(사용자 본인)의 메세지 내용을 보여줄 viewHolder class를 정의
private class LeftViewHolder extends RecyclerView.ViewHolder {
private final TextView messageText;
private final TextView datetimeText;
public LeftViewHolder(@NonNull View itemView) {
super(itemView);
messageText = itemView.findViewById(R.id.messageText);
datetimeText = itemView.findViewById(R.id.datetimeText);
}
}//받는 사람(상대방)의 메세지 내용을 보여줄 viewHolder class를 정의
private class CenterViewHolder extends RecyclerView.ViewHolder {
private final TextView datetimeText;
public CenterViewHolder(@NonNull View itemView) {
super(itemView);
datetimeText = itemView.findViewById(R.id.datetimeText);
}
}//대화 날짜를 보여주기 위한 viewHolder class를 정의
}
AdapterClass에서의 핵심적으로 구현해야할 메서드는 onCreateViewHolder와 onBindViewHolder이다.
recyclerView에서 사용할 뷰들을 초기에 생성하는 onCreateViewHolder와 기존에 생성되어 있는 뷰들을 재사용하기 위해 뷰 안의 내용을 바꾸는 onBindViewHolder를 정의하여 mainActivity의 recyclerView와 말풍선 레이아웃을 연결해준다.
그 다음은 이제 마지막으로 이 채팅앱의 대화내용 데이터 관리의 핵심인 DB관리를 위한 DBHelperClass를 정의하자.
여기서 사용할 DB는 SQLiteDB이다.
public class DBHelper extends SQLiteOpenHelper {
public DBHelper(@Nullable Context context, int version) {
super(context, "chatting.db", null, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS MESSAGE (ID INTEGER PRIMARY KEY AUTOINCREMENT, CONTENTS TEXT, REGISTER_DATE TEXT NOT NULL DEFAULT (datetime('now', 'localtime')), MESSAGE_TYPE INTEGER)");
// db.execSQL("CREATE TABLE IF NOT EXISTS MESSAGE (ID INTEGER PRIMARY KEY AUTOINCREMENT, CONTENTS TEXT, REGISTER_DATE TEXT, MESSAGE_TYPE INTEGER)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS MESSAGE");
onCreate(db);
}
public Message selectOne(long insertedId) {
Message message = null;
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query("message", new String[]{"id", "contents", "register_date", "message_type"}, "id = ?", new String[]{String.valueOf(insertedId)}, null, null, null);
// 하나의 메시지만 가져오기 때문에 while이 아니라 if문을 사용하여 가져올 데이터 있으면 가져와서 message를 리턴하고, 없으면 null을 리턴한다.
if (cursor.moveToNext()) {
int id = cursor.getInt(0);
String contents = cursor.getString(1);
String registerDate = cursor.getString(2);
int code = cursor.getInt(3);
message = new Message(id, contents, registerDate, MessageType.of(code));
}
return message;
}//DB에서 하나의 찾으려는 id의 메세지를 가져온다.
public List<Message> selectAll() {
List<Message> list = new ArrayList<>();
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query("message",new String[]{"id","contents","register_date","message_type"},null,null,null,null,"register_date");
Message message = null;
while (cursor.moveToNext()){
int id = cursor.getInt(0);
String contents = cursor.getString(1);
String registerDate = cursor.getString(2);
int code = cursor.getInt(3);
message = new Message(id,contents,registerDate,MessageType.of(code));
list.add(message);
}
return list;
}//모든 메세지를 가져온다.
// SimpleDateFormat mFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// public String getTime(){
// long mNow = System.currentTimeMillis();
// Date mDate = new Date(mNow);
// return mFormat.format(mDate);
// }
/**
* 메시지를 등록한다.
* @param contents 등록할 메시지
*/
public long insert(String contents, MessageType type) {
SQLiteDatabase db = getWritableDatabase();
// db.execSQL("INSERT INTO MESSAGE (CONTENTS) VALUES ('" + contents + "')");
ContentValues values = new ContentValues();
values.put("contents", contents);
// values.put("register_date",getTime());
values.put("message_type", type.getCode());
return db.insert("message", null, values);
}//DB에 데이터를 집어넣는 메서드
/**
* 메시지 아이디에 해당하는 데이터를 삭제한다.
* @param id 삭제할 아이디
*/
public void delete(int id) {
SQLiteDatabase db = getWritableDatabase();
// db.execSQL("DELETE FROM MESSAGE WHERE ID = " + id);
db.delete("message", "id = ?", new String[]{String.valueOf(id)});
}//DB의 id로 찾은 데이터를 지우는 메서드
public void deleteAll(){
SQLiteDatabase db = getWritableDatabase();
db.delete("message", null, null);
}//DB의 모든 내용을 지우는 메서드
}
결과 내용을 보면 아래와 같다.
'project' 카테고리의 다른 글
채팅앱 만들기 서버 없이 DB 까지 (0) | 2022.11.24 |
---|---|
음악재생 프로그램 만들기-1 (0) | 2022.11.08 |