반응형

(210518 수정)

 

이번에는 실제로 채팅을 할 수 있게 채팅 기능을 만들어 보겠다. 

 

앱을 만들 때 상당히 많이 사용하는 RecylerView 를 이용하니 잘 봐두면 좋을 것이다.

 

------------------------------------------------------------------------------------------------------

 

우선 채팅창의 화면은 activity_chat.xml이고 이 xml파일을 Inflation 시켜주는 클래스 파일은

ChatActivity.class 이고 이 클래스 부터 알아보자면

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
public class ChatActivity extends AppCompatActivity {
 
    private static final String TAG = "ChatActivity";
 
    private RecyclerView recyclerView;
    MyAdapter  adapter;
    private RecyclerView.LayoutManager layoutManager; // 리사이클러뷰의 레이아웃을 정해줄 레이아웃 매니저
    FirebaseDatabase database;
    ArrayList<Chat> chatArrayList; // 모델(이메일과 텍스트)을 나열함
 
   EditText edt;
    Button btnSend;
    String stEmail;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);
 
        chatArrayList = new ArrayList<>(); // Chat 모델을 나열하는 ArrayList
 
        database = FirebaseDatabase.getInstance();
 
        stEmail = getIntent().getStringExtra("email"); // 인텐트로 MainActivity에서 입력된 이메일 값을 받아오는 문자열 값 email
 
       edt = (EditText)findViewById(R.id.edt);
 
        recyclerView = (RecyclerView)findViewById(R.id.recyclerview);
        recyclerView.setHasFixedSize(true); // 변경하지 않음 -> 항목의 높이가 바뀌지 않아야 비용이 적게 드므로 성능이 좋음
 
        layoutManager = new LinearLayoutManager(this); // 리사이클러뷰의 레이아웃을 정해줄 레이아웃 매니저
        recyclerView.setLayoutManager(layoutManager); // 리사이클러뷰에 리니어 레이아웃 매니저를 사용함
 
        String[] myDataset = {"테스트1""테스트2","테스트3""테스트4"}; // myDataset에 다음과 같은 문자열 삽입
        adapter = new MyAdapter(chatArrayList, stEmail); // chatArrayList를 어댑터로 연결, 회원의 이메일도 넘김
        recyclerView.setAdapter(adapter); // 리사이클뷰에 어댑터를 설정
 
        btnSend = (Button)findViewById(R.id.btnSend);
        btnSend.setOnClickListener(new View.OnClickListener() { // 메시지 전송 버튼(화살표)을 누르면
            @Override
            public void onClick(View v) {
                String stText = editText.getText().toString(); // 입력창에 입력한 문자가 stText에 담겨짐
                editText.setText(""); // 입력창은 공백이됨
 
                Calendar calendar = Calendar.getInstance(); // 캘린더 객체 인스턴스 calendar
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-dd-MMM HH:mm:ss"); // SimpleDataFormat 이라는 날짜와 시간을 출력하는 객체 생성, hh을 HH로 변경했더니 24시각제로 바뀜
                String datetime = dateFormat.format(calendar.getTime()); // 캘린더 날짜시간 값을 가져와서 문자열인 datatime 으로 변환함
                System.out.println(datetime); // 문자열 datetime 값 출력
 
                DatabaseReference myRef = database.getReference("message").child(datetime); // 데이터베이스에 message 안에 자식으로 날짜인 datatime이 들어감
 
                Hashtable<StringString> numbers // 해시테이블 객체 numbers 생성 후 안에 이메일과 입력한 문자가 들어감
                        = new Hashtable<StringString>();
                numbers.put("email", stEmail);
                numbers.put("text", stText);
 
                myRef.setValue(numbers); // DB 객체에 numbers 객체가 설정됨
            }
        });
 
        ChildEventListener childEventListener = new ChildEventListener() { // 현재 시간의 자식들인 이메일, 텍스트들을 담는 객체 생성
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) { // 현재 시간의 자식들을 추가함
                Log.d(TAG, "child를 추가합니다. :" + dataSnapshot.getKey());
 
                // 리스너는 이벤트 발생 시점에 DB에서 지정된 위치에 있던 데이터를 포함하는
                // DataSnapShot을 수신함. getValue()를 호출하면서 데이터의 자바 객체 표현이 반환됨
                Chat chat = dataSnapshot.getValue(Chat.class);
 
                // 모델인 Chat.class 에서 이메일이랑 텍스트를 가져와서 확인
                String stEmail = chat.getEmail();
                String stText = chat.getText();
                Log.d(TAG, "이메일 가져오기 : " + stEmail);
                Log.d(TAG, "텍스트 가져오기 : " + stText);
 
                chatArrayList.add(chat); // Chat 모델을 추가하고
                adapter.notifyDataSetChanged(); // 변경된다는 것을 어댑터에게 알려줌
            }
 
            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
                Log.d(TAG, "Chile가 변경되었습니다. :" + dataSnapshot.getKey());
                String commentKey = dataSnapshot.getKey();
            }
 
            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {
                Log.d(TAG, "CHild가 삭제되었습니다. :" + dataSnapshot.getKey());
                String commentKey = dataSnapshot.getKey();
            }
 
            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
                Log.d(TAG, "Child가 이동되었습니다. :" + dataSnapshot.getKey());
            }
 
            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.w(TAG, "취소되었습니다. ", databaseError.toException());
            }
        };
 
        DatabaseReference databaseReference = database.getReference("message"); // 데이터베이스에 message 라는 문자로 경로가 만들어짐
        databaseReference.addChildEventListener(childEventListener);
    }
}
cs@Override
 

RecyclerView는 ListView와 비슷하지만

레이아웃을 레이아웃 매니저로 직접 선택할 수 있다는 

차이점이 있다.

 

그리고 데이터를 저장하고 관리하는 Chat Model을 작성한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Chat {
 
    String email;
    String text;
 
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
 
    public String getText() {
        return text;
    }
 
    public void setText(String text) {
        this.text = text;
    }
}
 
cs}

 

Model은 디자인 패턴 중 하나이다.

필자가 디자인 패턴에 대해 아직 많이 미숙하기도 하고, 이 글에서 다룰 내용은 아니지만

Model에 대해서는 간략하게 말할 수 있을 것 같다.

 

Model은 쉽게 말해 어플리케이션의 정보, 즉 데이터를 말한다.

이번 채팅 기능에서의 데이터는 무엇을 말할까?

바로 이메일과 텍스트(채팅 메시지)를 말한다.

 

바로 이러한 Chat Model 데이터로

액티비티와 어댑터, 뷰와 상호작용을 할 수 있게 된다.

 

다음으로는 RecyclerView를 액티비티와 연결하고 설정하는

어댑터에 대해서 알아보자면

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
 
    private ArrayList<Chat> mDataset;
    String stMyEmail = "";
 
    public static class ViewHolder extends RecyclerView.ViewHolder { // 아이템 뷰를 저장하는 뷰홀더
        private final TextView tvChat;
 
        public ViewHolder(View view) {
            super(view);
            tvChat = (TextView) view.findViewById(R.id.tvChat); //  채팅문자가 입력될 텍스트뷰 찾기
        }
 
        public TextView getTextView() { // 채팅문자가 입력된 텍스트뷰 가져오기
            return tvChat;
        }
    }
 
    @Override
    public int getItemViewType(int position) {
        if(mDataset.get(position).email.equals(stMyEmail)) { // email과 stMyEmail과 같으면
            return 1// 내 메시지
        } else { // stMyEail과 다르면
            return 2;  // 내 메시지가 아님
        }
    }
 
    // Chat형 어레이 리스트 myDataset을 처음에 전역변수로 선언한  mDataset에 대입
    public MyAdapter(ArrayList<Chat> myDataset, String stEmail) {
        mDataset = myDataset;
        this.stMyEmail = stEmail;
    }
 
    // 뷰홀더를 생성(레이아웃 생성)
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { // 아이템들을 어떻게 보여줄 것인가
        View view = LayoutInflater.from(viewGroup.getContext()) // 상대방 메시지이면
                .inflate(R.layout.left_text_view, viewGroup, false); // 말풍선이 왼쪽에서 나타나는 xml 파일 인플레이션
 
        if(viewType == 1) { // 내 메시지이면
            view = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.right_text_view, viewGroup, false); // 말풍선이 오른쪽에서 나타나는 xml 파일 인플레이션
        }
        return new ViewHolder(view);
    }
 
    // 뷰홀더가 재활용될 때 실행
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {
        viewHolder.getTextView().setText(mDataset.get(position).getText());
    }
 
    @Override
    public int getItemCount() {
        return mDataset.size();
    }
}
cs

이런식으로 할 수 있다.

 

이렇게 해서

이메일과 패스워드를 입력해서 회원가입을 하고

로그인을 해서 채팅창으로 넘어가면

채팅을 자유롭게 할 수 있다.

 

로그인과 회원가입 화면
채팅창 화면

 

채팅을 하게 되면 파이어 베이스에서 실시간으로 저장된다. 

 

피드백은 언제나 환영입니다.

반응형

+ Recent posts