Android Studio(기능) Firebase - Database+Storage [채팅창 만들기2] 에서 첫 화면에 접속하고 Fire Database, Storage에 저장하였다. 그리고 앱을 종료하고 다시 실행시키면 기존에 썼던 데이터가 자동으로 입력된다.
이제 두 번째 화면(채팅화면) ChatActivity을 구현하겠다.
<최종 실행 화면>
기존에 했던 대화들도 보이고 상대와 나의 말이 구분된다.
이앱을 다른 기기에서 실행하면 같이 대화할 수 있다!!! (신기하다.. :-)
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
|
<?xml version="1.0" encoding="utf-8"?>
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ChatActivity">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:divider="#ffffff"
android:dividerHeight="0dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="4dp"
android:background="@color/colorPrimary"
android:gravity="center_vertical">
<EditText
android:id="@+id/et"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="Input message"
android:inputType="textMultiLine"
android:padding="10dp"
android:maxLines="3"
android:background="@drawable/back_et_name"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="send"
android:backgroundTint="#ff8800"
android:layout_marginLeft="8dp"
android:onClick="clickSend"/>
</LinearLayout>
</LinearLayout>
|
채팅창에 글 마다에 사진, 이름, 시간, 메세지 내용이 있다.
이것을 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
|
package com.lcw.ex85firebasechatting;
public class MessageItem {
String name;
String message;
String time;
String pofileUrl;
public MessageItem(String name, String message, String time, String pofileUrl) {
this.name = name;
this.message = message;
this.time = time;
this.pofileUrl = pofileUrl;
}
//firebase DB에 객체로 값을 읽어올 때..
//파라미터가 비어있는 생성자가 핑요함.
public MessageItem() {
}
//Getter & Setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getPofileUrl() {
return pofileUrl;
}
public void setPofileUrl(String pofileUrl) {
this.pofileUrl = pofileUrl;
}
}
|
아이템 하나의 모양을 만들자. (두개의 모양 필요 : 내 메세지 박스, 상대방 메세지 박스 )
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
|
<?xml version="1.0" encoding="utf-8"?>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/iv"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/ic_launcher"
android:layout_alignParentRight="true"/>
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="name"
android:textColor="#333333"
android:layout_toLeftOf="@+id/iv"
android:layout_marginRight="16dp"/>
<TextView
android:id="@+id/tv_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello android"
android:layout_below="@+id/tv_name"
android:layout_toLeftOf="@id/iv"
android:layout_marginRight="16dp"
android:padding="12dp"
android:maxWidth="250dp"
android:background="@drawable/back_et_mymsgbox"
android:textColor="#ffffff"/>
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="13:35"
android:textSize="12sp"
android:layout_toLeftOf="@+id/tv_msg"
android:layout_marginRight="8dp"
android:layout_alignBottom="@id/tv_msg"/>
</RelativeLayout>
|
내 메세지 박스 모양을 만들자.
1
2
3
4
5
6
7
8
9
|
<?xml version="1.0" encoding="utf-8"?>
android:shape="rectangle">
<solid android:color="@color/colorPrimary"/>
<stroke android:color="@color/colorPrimaryDark" android:width="1dp"/>
<corners android:radius="4dp"/>
</shape>
|
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
|
<?xml version="1.0" encoding="utf-8"?>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/iv"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/ic_launcher"
android:layout_alignParentRight="true"/>
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="name"
android:textColor="#333333"
android:layout_toLeftOf="@+id/iv"
android:layout_marginRight="16dp"/>
<TextView
android:id="@+id/tv_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello android"
android:layout_below="@+id/tv_name"
android:layout_toLeftOf="@id/iv"
android:layout_marginRight="16dp"
android:padding="12dp"
android:maxWidth="250dp"
android:background="@drawable/back_et_mymsgbox"
android:textColor="#ffffff"/>
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="13:35"
android:textSize="12sp"
android:layout_toLeftOf="@+id/tv_msg"
android:layout_marginRight="8dp"
android:layout_alignBottom="@id/tv_msg"/>
</RelativeLayout>
|
1
2
3
4
5
6
7
8
9
|
<?xml version="1.0" encoding="utf-8"?>
android:shape="rectangle">
<solid android:color="#ffffff"/>
<stroke android:color="@color/colorPrimaryDark" android:width="1dp"/>
<corners android:radius="4dp"/>
</shape>
|
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
|
<?xml version="1.0" encoding="utf-8"?>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/iv"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/ic_launcher"
/>
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="name"
android:textColor="#333333"
android:layout_toRightOf="@+id/iv"
android:layout_marginLeft="16dp"/>
<TextView
android:id="@+id/tv_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello android"
android:layout_below="@+id/tv_name"
android:layout_toRightOf="@id/iv"
android:layout_marginLeft="16dp"
android:padding="12dp"
android:maxWidth="250dp"
android:background="@drawable/back_et_othermsgbox"
android:textColor="#333333"/>
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="13:35"
android:textSize="12sp"
android:layout_toRightOf="@+id/tv_msg"
android:layout_marginLeft="8dp"
android:layout_alignBottom="@id/tv_msg"/>
</RelativeLayout>
|
이제 정보를 화면에 보여줄 Adapter를 만들자.
MainActivity로 가서 ListView와 Adapter 연결
이제 Send 버튼 기능을 작성하자.
ChatActivity.java
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
|
package com.lcw.ex85firebasechatting;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.Calendar;
public class ChatActivity extends AppCompatActivity {
EditText et;
ListView listView;
ArrayList<MessageItem> messageItems=new ArrayList<>();
ChatAdapter adapter;
//Firebase Database 관리 객체참조변수
FirebaseDatabase firebaseDatabase;
//'chat'노드의 참조객체 참조변수
DatabaseReference chatRef;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
//제목줄 제목글시를 닉네임으로(또는 채팅방)
getSupportActionBar().setTitle(G.nickName);
et=findViewById(R.id.et);
listView=findViewById(R.id.listview);
adapter=new ChatAdapter(messageItems,getLayoutInflater());
listView.setAdapter(adapter);
//Firebase DB관리 객체와 'caht'노드 참조객체 얻어오기
firebaseDatabase= FirebaseDatabase.getInstance();
chatRef= firebaseDatabase.getReference("chat");
//firebaseDB에서 채팅 메세지들 실시간 읽어오기..
}
public void clickSend(View view) {
//firebase DB에 저장할 값들( 닉네임, 메세지, 프로필 이미지URL, 시간)
String nickName= G.nickName;
String message= et.getText().toString();
String pofileUrl= G.porfileUrl;
//메세지 작성 시간 문자열로..
Calendar calendar= Calendar.getInstance(); //현재 시간을 가지고 있는 객체
//firebase DB에 저장할 값(MessageItem객체) 설정
MessageItem messageItem= new MessageItem(nickName,message,time,pofileUrl);
//'char'노드에 MessageItem객체를 통해
//EditText에 있는 글씨 지우기
et.setText("");
//소프트키패드를 안보이도록..
InputMethodManager imm=(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
//처음 시작할때 EditText가 다른 뷰들보다 우선시 되어 포커스를 받아 버림.
//즉, 시작부터 소프트 키패드가 올라와 있음.
//그게 싫으면...다른 뷰가 포커스를 가지도록
//즉, EditText를 감싼 Layout에게 포커스를 가지도록 속성을 추가!![[XML에]
}
}
|
시작부터 키패드를 안띄우려면 Edit View를 감싸고 있는 LinearLayout에 focus를 줘야한다.
이제 채팅창에 쓴 메세지를 띄워보자.
ChatActivity,java
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
package com.lcw.ex85firebasechatting;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.Calendar;
public class ChatActivity extends AppCompatActivity {
EditText et;
ListView listView;
ArrayList<MessageItem> messageItems=new ArrayList<>();
ChatAdapter adapter;
//Firebase Database 관리 객체참조변수
FirebaseDatabase firebaseDatabase;
//'chat'노드의 참조객체 참조변수
DatabaseReference chatRef;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
//제목줄 제목글시를 닉네임으로(또는 채팅방)
getSupportActionBar().setTitle(G.nickName);
et=findViewById(R.id.et);
listView=findViewById(R.id.listview);
adapter=new ChatAdapter(messageItems,getLayoutInflater());
listView.setAdapter(adapter);
//Firebase DB관리 객체와 'caht'노드 참조객체 얻어오기
firebaseDatabase= FirebaseDatabase.getInstance();
chatRef= firebaseDatabase.getReference("chat");
//firebaseDB에서 채팅 메세지들 실시간 읽어오기..
//'chat'노드에 저장되어 있는 데이터들을 읽어오기
//chatRef에 데이터가 변경되는 것으 듣는 리스너 추가
chatRef.addChildEventListener(new ChildEventListener() {
//새로 추가된 것만 줌 ValueListener는 하나의 값만 바뀌어도 처음부터 다시 값을 줌
@Override
public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
//새로 추가된 데이터(값 : MessageItem객체) 가져오기
MessageItem messageItem= dataSnapshot.getValue(MessageItem.class);
//새로운 메세지를 리스뷰에 추가하기 위해 ArrayList에 추가
messageItems.add(messageItem);
//리스트뷰를 갱신
adapter.notifyDataSetChanged();
}
@Override
public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
public void clickSend(View view) {
//firebase DB에 저장할 값들( 닉네임, 메세지, 프로필 이미지URL, 시간)
String nickName= G.nickName;
String message= et.getText().toString();
String pofileUrl= G.porfileUrl;
//메세지 작성 시간 문자열로..
Calendar calendar= Calendar.getInstance(); //현재 시간을 가지고 있는 객체
//firebase DB에 저장할 값(MessageItem객체) 설정
MessageItem messageItem= new MessageItem(nickName,message,time,pofileUrl);
//'char'노드에 MessageItem객체를 통해
//EditText에 있는 글씨 지우기
et.setText("");
//소프트키패드를 안보이도록..
InputMethodManager imm=(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
//처음 시작할때 EditText가 다른 뷰들보다 우선시 되어 포커스를 받아 버림.
//즉, 시작부터 소프트 키패드가 올라와 있음.
//그게 싫으면...다른 뷰가 포커스를 가지도록
//즉, EditText를 감싼 Layout에게 포커스를 가지도록 속성을 추가!![[XML에]
}
}
|
<최종 실행 화면>
기존에 했던 대화들도 보이고 상대와 나의 말이 구분된다.
이앱을 다른 기기에서 실행하면 같이 대화할 수 있다!!! (신기)
복붙용 코드
@layout
activity_main.xml
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
|
<?xml version="1.0" encoding="utf-8"?>
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/et_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:maxLength="10"
android:layout_centerInParent="true"
android:hint="Input nick name"
android:inputType="text"
android:gravity="center"
android:padding="8dp"
android:background="@drawable/back_et_name"
android:textCursorDrawable="@drawable/et_name_cursor"/>
<!-- ems최소 글자로-->
<!-- android:textCursorDrawable="@null" 이면 글씨 색과 같은 커서색-->
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/iv_profile"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_centerInParent="true"
android:layout_above="@id/et_name"
android:layout_marginBottom="16dp"
android:src="@mipmap/ic_launcher"
android:clickable="true"
android:onClick="clickImage"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="입장"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_margin="16dp"
android:onClick="clickBtn"/>
</RelativeLayout>
|
activity_char.xml
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
|
<?xml version="1.0" encoding="utf-8"?>
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ChatActivity">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:divider="#ffffff"
android:dividerHeight="0dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="4dp"
android:background="@color/colorPrimary"
android:gravity="center_vertical"
android:focusable="true"
android:focusableInTouchMode="true">
<EditText
android:id="@+id/et"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="Input message"
android:inputType="textMultiLine"
android:padding="10dp"
android:maxLines="3"
android:background="@drawable/back_et_name"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="send"
android:backgroundTint="#ff8800"
android:layout_marginLeft="8dp"
android:onClick="clickSend"/>
</LinearLayout>
</LinearLayout>
|
my_msgbox.xml
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
|
<?xml version="1.0" encoding="utf-8"?>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/iv"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/ic_launcher"
android:layout_alignParentRight="true"/>
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="name"
android:textColor="#333333"
android:layout_toLeftOf="@+id/iv"
android:layout_marginRight="16dp"/>
<TextView
android:id="@+id/tv_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello android"
android:layout_below="@+id/tv_name"
android:layout_toLeftOf="@id/iv"
android:layout_marginRight="16dp"
android:padding="12dp"
android:maxWidth="250dp"
android:background="@drawable/back_et_mymsgbox"
android:textColor="#ffffff"/>
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="13:35"
android:textSize="12sp"
android:layout_toLeftOf="@+id/tv_msg"
android:layout_marginRight="8dp"
android:layout_alignBottom="@id/tv_msg"/>
</RelativeLayout>
|
other_msgbox.xml
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
|
<?xml version="1.0" encoding="utf-8"?>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/iv"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/ic_launcher"
/>
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="name"
android:textColor="#333333"
android:layout_toRightOf="@+id/iv"
android:layout_marginLeft="16dp"/>
<TextView
android:id="@+id/tv_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello android"
android:layout_below="@+id/tv_name"
android:layout_toRightOf="@id/iv"
android:layout_marginLeft="16dp"
android:padding="12dp"
android:maxWidth="250dp"
android:background="@drawable/back_et_othermsgbox"
android:textColor="#333333"/>
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="13:35"
android:textSize="12sp"
android:layout_toRightOf="@+id/tv_msg"
android:layout_marginLeft="8dp"
android:layout_alignBottom="@id/tv_msg"/>
</RelativeLayout>
|
@drawable
back_et_name.xml
1
2
3
4
5
6
7
8
9
|
<?xml version="1.0" encoding="utf-8"?>
android:shape="rectangle">
<solid android:color="#ffffff"/>
<stroke android:color="@color/colorPrimary" android:width="2dp"/>
<corners android:radius="4dp"/>
</shape>
|
back_et_mymsgbox.xml
1
2
3
4
5
6
7
8
9
|
<?xml version="1.0" encoding="utf-8"?>
android:shape="rectangle">
<solid android:color="@color/colorPrimary"/>
<stroke android:color="@color/colorPrimaryDark" android:width="1dp"/>
<corners android:radius="4dp"/>
</shape>
|
back_et_othermsgbox.xml
1
2
3
4
5
6
7
8
9
|
<?xml version="1.0" encoding="utf-8"?>
android:shape="rectangle">
<solid android:color="#ffffff"/>
<stroke android:color="@color/colorPrimaryDark" android:width="1dp"/>
<corners android:radius="4dp"/>
</shape>
|
et_name_cursor.xml
1
2
3
4
5
6
7
8
9
|
<?xml version="1.0" encoding="utf-8"?>
android:shape="rectangle">
<solid android:color="@color/colorPrimary"/>
<size android:width="2dp"/>
</shape>
|
@java
MainActivity.java
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
package com.lcw.ex85firebasechatting;
import androidx.annotation.Nullable;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.squareup.picasso.Picasso;
import java.text.SimpleDateFormat;
import java.util.Date;
import de.hdodenhof.circleimageview.CircleImageView;
public class MainActivity extends AppCompatActivity {
EditText etName;
CircleImageView ivProfile;
Uri imgUri;//선택한 프로필 이미지 경로 Uri
boolean isFirst= true; //앱을 처음 실행한 것인가?
boolean isChanged= false; //프로필을 변경한 적이 있는가?
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etName=findViewById(R.id.et_name);
ivProfile=findViewById(R.id.iv_profile);
//폰에 저장되어 있는 프로필 읽어오기
loadData();
if(G.nickName!=null){
etName.setText(G.nickName);
//처음이 아니다, 즉, 이미 접속한 적이 있다.
isFirst=false;
}
}
public void clickImage(View view) {
//프로필 이미지 선택하도록 Gallery 앱 실행
Intent intent= new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent,10);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode){
case 10:
if(resultCode==RESULT_OK){
imgUri= data.getData();
//Glide는 이미지를 읽어와서 보여줄때 내 device의 외장메모리에 접근하는 퍼미션이 요구됨.
//(퍼미션이 없으면 이미지가 보이지 않음.)
//Glide를 사용할 때는 동적 퍼미션 필요함.
//Picasso 라이브러리는 퍼미션 없어도 됨.
//변경된 이미지가 있다.
isChanged=true;
}
break;
}
}
public void clickBtn(View view) {
//바꾼것도 없고, 처음 접속도 아니고..
if(!isChanged && !isFirst){
//ChatActivity로 전환
Intent intent= new Intent(this, ChatActivity.class);
startActivity(intent);
finish();
}else{
//1. save작업
saveData();
}
}
void saveData(){
//EditText의 닉네임 가져오기 [전역변수에]
G.nickName= etName.getText().toString();
//이미지를 선택하지 않았을 수도 있으므로
if(imgUri==null) return;
//Firebase storage에 이미지 저장하기 위해 파일명 만들기(날짜를 기반으로)
SimpleDateFormat sdf= new SimpleDateFormat("yyyMMddhhmmss"); //20191024111224
String fileName= sdf.format(new Date())+".png";
//Firebase storage에 저장하기
FirebaseStorage firebaseStorage= FirebaseStorage.getInstance();
final StorageReference imgRef= firebaseStorage.getReference("profileImages/"+fileName);
//파일 업로드
UploadTask uploadTask=imgRef.putFile(imgUri);
uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
//이미지 업로드가 성공되었으므로...
//곧바로 firebase storage의 이미지 파일 다운로드 URL을 얻어오기
imgRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(Uri uri) {
//파라미터로 firebase의 저장소에 저장되어 있는
//이미지에 대한 다운로드 주소(URL)을 문자열로 얻어오기
G.porfileUrl= uri.toString();
Toast.makeText(MainActivity.this, "프로필 저장 완료", Toast.LENGTH_SHORT).show();
//1. Firebase Database에 nickName, profileUrl을 저장
//firebase DB관리자 객체 소환
FirebaseDatabase firebaseDatabase=FirebaseDatabase.getInstance();
//'profiles'라는 이름의 자식 노드 참조 객체 얻어오기
DatabaseReference profileRef= firebaseDatabase.getReference("profiles");
//닉네임을 key 식별자로 하고 프로필 이미지의 주소를 값으로 저장
//2. 내 phone에 nickName, profileUrl을 저장
SharedPreferences preferences= getSharedPreferences("account",MODE_PRIVATE);
editor.putString("nickName",G.nickName);
editor.putString("profileUrl", G.porfileUrl);
//저장이 완료되었으니 ChatActivity로 전환
Intent intent=new Intent(MainActivity.this, ChatActivity.class);
startActivity(intent);
finish();
}
});
}
});
}//saveData() ..
//내 phone에 저장되어 있는 프로필정보 읽어오기
void loadData(){
SharedPreferences preferences=getSharedPreferences("account",MODE_PRIVATE);
G.nickName=preferences.getString("nickName", null);
G.porfileUrl=preferences.getString("profileUrl", null);
}
}
|
G.java
1
2
3
4
5
6
7
8
|
package com.lcw.ex85firebasechatting;
public class G {
public static String nickName;
public static String porfileUrl;
}
|
MessageItem.java
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
|
package com.lcw.ex85firebasechatting;
public class MessageItem {
String name;
String message;
String time;
String profileUrl;
public MessageItem(String name, String message, String time, String pofileUrl) {
this.name = name;
this.message = message;
this.time = time;
this.profileUrl = pofileUrl;
}
//firebase DB에 객체로 값을 읽어올 때..
//파라미터가 비어있는 생성자가 핑요함.
public MessageItem() {
}
//Getter & Setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getPofileUrl() {
return profileUrl;
}
public void setPofileUrl(String pofileUrl) {
this.profileUrl = pofileUrl;
}
}
|
ChatAdapter.java
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
|
package com.lcw.ex85firebasechatting;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import java.util.ArrayList;
import de.hdodenhof.circleimageview.CircleImageView;
public class ChatAdapter extends BaseAdapter {
ArrayList<MessageItem> messageItems;
LayoutInflater layoutInflater;
public ChatAdapter(ArrayList<MessageItem> messageItems, LayoutInflater layoutInflater) {
this.messageItems = messageItems;
this.layoutInflater = layoutInflater;
}
@Override
public int getCount() {
return messageItems.size();
}
@Override
public Object getItem(int position) {
return messageItems.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View view, ViewGroup viewGroup) {
//현재 보여줄 번째의(position)의 데이터로 뷰를 생성
MessageItem item=messageItems.get(position);
//재활용할 뷰는 사용하지 않음!!
View itemView=null;
//메세지가 내 메세지인지??
if(item.getName().equals(G.nickName)){
itemView= layoutInflater.inflate(R.layout.my_msgbox,viewGroup,false);
}else{
itemView= layoutInflater.inflate(R.layout.other_msgbox,viewGroup,false);
}
//만들어진 itemView에 값들 설정
CircleImageView iv= itemView.findViewById(R.id.iv);
TextView tvName= itemView.findViewById(R.id.tv_name);
TextView tvMsg= itemView.findViewById(R.id.tv_msg);
TextView tvTime= itemView.findViewById(R.id.tv_time);
tvName.setText(item.getName());
tvMsg.setText(item.getMessage());
tvTime.setText(item.getTime());
return itemView;
}
}
|
ChatActivity.java
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
package com.lcw.ex85firebasechatting;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.Calendar;
public class ChatActivity extends AppCompatActivity {
EditText et;
ListView listView;
ArrayList<MessageItem> messageItems=new ArrayList<>();
ChatAdapter adapter;
//Firebase Database 관리 객체참조변수
FirebaseDatabase firebaseDatabase;
//'chat'노드의 참조객체 참조변수
DatabaseReference chatRef;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
//제목줄 제목글시를 닉네임으로(또는 채팅방)
getSupportActionBar().setTitle(G.nickName);
et=findViewById(R.id.et);
listView=findViewById(R.id.listview);
adapter=new ChatAdapter(messageItems,getLayoutInflater());
listView.setAdapter(adapter);
//Firebase DB관리 객체와 'caht'노드 참조객체 얻어오기
firebaseDatabase= FirebaseDatabase.getInstance();
chatRef= firebaseDatabase.getReference("chat");
//firebaseDB에서 채팅 메세지들 실시간 읽어오기..
//'chat'노드에 저장되어 있는 데이터들을 읽어오기
//chatRef에 데이터가 변경되는 것으 듣는 리스너 추가
chatRef.addChildEventListener(new ChildEventListener() {
//새로 추가된 것만 줌 ValueListener는 하나의 값만 바뀌어도 처음부터 다시 값을 줌
@Override
public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
//새로 추가된 데이터(값 : MessageItem객체) 가져오기
MessageItem messageItem= dataSnapshot.getValue(MessageItem.class);
//새로운 메세지를 리스뷰에 추가하기 위해 ArrayList에 추가
messageItems.add(messageItem);
//리스트뷰를 갱신
adapter.notifyDataSetChanged();
}
@Override
public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
public void clickSend(View view) {
//firebase DB에 저장할 값들( 닉네임, 메세지, 프로필 이미지URL, 시간)
String nickName= G.nickName;
String message= et.getText().toString();
String pofileUrl= G.porfileUrl;
//메세지 작성 시간 문자열로..
Calendar calendar= Calendar.getInstance(); //현재 시간을 가지고 있는 객체
//firebase DB에 저장할 값(MessageItem객체) 설정
MessageItem messageItem= new MessageItem(nickName,message,time,pofileUrl);
//'char'노드에 MessageItem객체를 통해
//EditText에 있는 글씨 지우기
et.setText("");
//소프트키패드를 안보이도록..
InputMethodManager imm=(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
//처음 시작할때 EditText가 다른 뷰들보다 우선시 되어 포커스를 받아 버림.
//즉, 시작부터 소프트 키패드가 올라와 있음.
//그게 싫으면...다른 뷰가 포커스를 가지도록
//즉, EditText를 감싼 Layout에게 포커스를 가지도록 속성을 추가!![[XML에]
}
}
|
'안드로이드 웹앱 콘테츠 개발자 양성(국비지원) > Firebase' 카테고리의 다른 글
Android Studio(기능) Firebase - Database+Storage [채팅창 만들기2] (0) | 2019.10.24 |
---|---|
Android Studio(기능) Firebase - Database+Storage [채팅창 만들기1] (1) | 2019.10.24 |
Android Studio(기능) Firebase - Cloud Storage (1) | 2019.10.23 |
Android Studio(기능) Firebase - Realtime Database (1) | 2019.10.23 |
댓글