앱이 시작될 때 자동으로 로그인이 진행될 수 있도록, 스플래시 화면에 비회원 로그인 로직을 추가해보자. 이를 위해 먼저 스플래시 화면을 구성해야 한다.
① scheduler라는 이름으로 프로젝트를 생성한다.
② default 패키지 하위로 SplashActivity를 생성한다.
③ 이전 포스팅의 내용을 참고하여 SplashActivity를 3초 후에 MainActivity로 전환하는 코드를 추가하고, Manifest 설정을 진행한다.
>> 스플래시 화면 구성하기
④ activity_splash.xml 파일을 아래와 같이 수정한다.
android:background="#000000"
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="To Do List"
android:textStyle="bold"
android:gravity="center"
android:textColor="#ffffff"
android:textSize="40sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
① 이전 포스팅의 내용을 참고하여 To do list라는 이름의 파이어베이스 프로젝트를 생성하고, 익명 로그인 토글을 ON으로 변경하는 것까지 진행한다.
>> 파이어베이스로 로그인 구현하기
② SplashActivity 파일에 아래의 코드를 추가한다.
private lateinit var auth: FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
auth = Firebase.auth
try {
Log.d("Splash", auth.currentUser!!.uid)
Handler().postDelayed({
startActivity(Intent(this, MainActivity::class.java))
finish()
}, 3000)
} catch (e : Exception) {
Log.d("Splash", "회원가입 필요")
auth.signInAnonymously()
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
Toast.makeText(this, "비회원 로그인 성공", Toast.LENGTH_SHORT,).show()
Handler().postDelayed({
startActivity(Intent(this, MainActivity::class.java))
finish()
}, 3000)
} else {
Toast.makeText(this, "비회원 로그인 실패", Toast.LENGTH_SHORT,).show()
}
}
}
}
이제 애플리케이션을 구동만 하면, 자동으로 비회원 로그인이 처리된다.
① 먼저 일정을 작성하기 위한 버튼으로 연필 아이콘을 drawable 패키지 안에 넣어주자.
② activity_main.xml 파일에 아래의 내용을 추가한다.
<ListView
android:id="@+id/mainLV"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:id="@+id/pen"
android:layout_width="88dp"
android:layout_height="72dp"
android:layout_margin="20dp"
android:src="@drawable/pen"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
③ 안드로이드 대화상자(Dialog)를 만들기 위해 layout 디렉토리 하위로 dialog라는 이름의 Layout Resource File을 추가한다.
④ dialog.xml 파일의 레이아웃을 LinearLayout으로 변경하고 orientation을 vertical로 변경한다.
android:orientation="vertical"
⑤ LinearLayout 컨테이너 안에 아래의 태그를 추가한다.
<EditText
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_margin="20dp"
android:hint=" 일정 입력하기"
android:background="@android:color/transparent"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginRight="10dp"
android:layout_marginLeft="10dp"
android:background="#999999"/>
<Button
android:id="@+id/date"
android:text="날짜 선택하기"
android:layout_margin="20dp"
android:layout_width="match_parent"
android:layout_height="40dp"/>
① MainActivity 파일에 아래의 내용을 입력한다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val pen = findViewById<ImageView>(R.id.pen)
pen.setOnClickListener {
val dialogView = LayoutInflater.from(this).inflate(R.layout.dialog, null)
val builder = AlertDialog.Builder(this)
.setView(dialogView)
.setTitle("일정 메모")
builder.show()
}
}
② Dialog에서 findViewById로 버튼을 찾기 위해 builder.show()의 값을 alertDialog라는 변수에 할당한 후 클릭 이벤트 리스너를 등록한다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val pen = findViewById<ImageView>(R.id.pen)
pen.setOnClickListener {
val dialogView = LayoutInflater.from(this).inflate(R.layout.dialog, null)
val builder = AlertDialog.Builder(this)
.setView(dialogView)
.setTitle("일정 메모")
val alertDialog = builder.show()
val dateBtn = alertDialog.findViewById<Button>(R.id.date)
dateBtn?.setOnClickListener {
val today = GregorianCalendar()
val year: Int = today.get(Calendar.YEAR)
val month: Int = today.get(Calendar.MONTH)
val date: Int = today.get(Calendar.DATE)
val dialog = DatePickerDialog(this, object : DatePickerDialog.OnDateSetListener {
override fun onDateSet(
view: DatePicker?,
year: Int,
month: Int,
dayOfMonth: Int
) {
TODO("Not yet implemented")
}
}, year, month, date)
dialog.show()
}
}
}
③ 이제 날짜를 선택했을 때의 이벤트를 처리해주어야 한다. 위 코드의 TODO("Not yet implemented") 부분에 아래의 내용을 입력한다.
dateBtn.setText("${year} / ${month+1} / ${dayOfMonth}")
① dialog.xml 파일에 저장하기 버튼을 추가하자. 날짜 선택 버튼 바로 아래에 작성하면 된다.
<Button
android:id="@+id/save"
android:text="저장하기"
android:textSize="20sp"
android:layout_margin="20dp"
android:layout_width="match_parent"
android:layout_height="50dp"/>
② 저장하기 버튼에 대한 클릭 이벤트 리스너를 등록한다.
val saveBtn = alertDialog.findViewById<Button>(R.id.save)
saveBtn?.setOnClickListener {
}
③ 파이어베이스의 프로젝트에 들어가 좌측 메뉴 > Realtime Database 버튼을 클릭한다.
④ 데이터베이스 만들기 버튼을 클릭한다.
⑤ 데이터베이스 위치는 미국으로 설정한다.
⑥ 테스트 모드에서 시작을 선택하고 사용 설정 버튼을 클릭한다.
⑦ 아래의 공식 문서를 참조하여 Realtime Database 관련 설정을 진행한다.
>> Realtime Database 관련 파이어베이스 공식 문서
implementation("com.google.firebase:firebase-database-ktx")
saveBtn?.setOnClickListener {
val database = Firebase.database
val myRef = database.getReference("message")
myRef.setValue("Hello, World!")
}
⑧ 선택한 날짜를 저장할 수 있는 변수를 하나 선언하자.
var selectedDate = ""
dateBtn.setText("${year} / ${month+1} / ${dayOfMonth}")
selectedDate = "${year} / ${month+1} / ${dayOfMonth}"
⑨ 사용자가 입력한 일정을 저장하기 위해 dialog.xml 파일의 EditText 태그에 id 속성을 지정해주자.
android:id="@+id/schedule"
⑩ 이제 저장하기 버튼을 클릭했을 때, 일정과 날짜를 모두 저장할 수 있도록 클래스를 작성해야 한다.
class Schedule (
val date : String = "",
val memo : String = ""
)
⑪ 다시 MainActivity로 돌아와서 저장하기 버튼에 대한 클릭 이벤트 리스너로 Schedule 타입 데이터가 저장되도록 만들어주자.
saveBtn?.setOnClickListener {
val memo = alertDialog.findViewById<EditText>(R.id.schedule)?.text.toString()
val database = Firebase.database
val myRef = database.getReference("mySchedule")
val schedule = Schedule(selectedDate, memo)
myRef.push().setValue(schedule)
alertDialog.dismiss()
}
⑫ 파이어베이스의 Realtime Database에 가서 결과를 확인해 볼 수 있다.
① MainActivity에 첫 화면에서 바로 일정표를 확인할 수 있도록 List View를 구성한다.
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val database = Firebase.database
val myRef = database.getReference("mySchedule")
val listView = findViewById<ListView>(R.id.mainLV)
② ListView를 위한 Adapter 클래스를 생성한다.
class Adapter(val List : MutableList<Schedule>) : BaseAdapter() {
override fun getCount(): Int {
return List.size
}
override fun getItem(position: Int): Any {
return List[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
}
}
③ listview_item이라는 이름으로 Layout Resource File을 layout 디렉토리 하위로 추가한다.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp">
<TextView
android:id="@+id/ListViewDate"
android:text="날짜"
android:textSize="30sp"
android:layout_margin="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/ListViewMemo"
android:text="일정"
android:textSize="15sp"
android:layout_margin="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
④ Adapter 클래스의 getView 메서드를 정의해주자.
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
var convertView = convertView
if(convertView == null) {
convertView = LayoutInflater.from(parent?.context).inflate(R.layout.listview_item, parent, false)
}
val date = convertView?.findViewById<TextView>(R.id.ListViewDate)
val memo = convertView?.findViewById<TextView>(R.id.ListViewMemo)
date!!.text = List[position].date
memo!!.text = List[position].memo
return convertView!!
}
⑤ MainActivity로 돌아와서 Adpater 클래스 관련 설정을 진행한다.
val schedule = mutableListOf<Schedule>()
val adapter = Adapter(schedule)
listView.adapter = adapter
myRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
for(dataModel in snapshot.children) {
schedule.add(dataModel.getValue(Schedule::class.java)!!)
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
⑥ 코드를 실행시켜보면, 아무 item도 ListView에 나타나지 않는다. 이는 파이어베이스의 비동기 처리와 연관이 있다. 그러므로 schedule List에 모든 값이 추가되면, Adapter를 새롭게 구성할 수 있도록 아래의 코드를 추가해야 한다.
adapter.notifyDataSetChanged()
⑦ onDataChange는 데이터베이스에 변경사항이 생길 때마다 콜백되는 메서드인데, for문을 사용하여 모든 데이터를 schedule List에 추가하다보니 데이터가 추가될 때마다, 기존 데이터가 함께 List에 추가되는 문제가 발생한다.
override fun onDataChange(snapshot: DataSnapshot) {
schedule.clear()
for(dataModel in snapshot.children) {
schedule.add(dataModel.getValue(Schedule::class.java)!!)
}
adapter.notifyDataSetChanged()
}
지금까지 만든 scheduler는 모든 유저가 공유하는 형태이다. 이를 personal scheduler로 만들기 위해서는 사용자의 UID 값을 사용해 데이터를 저장하고 불러오는 로직을 추가해야 한다.
① saveBtn의 클릭 이벤트 리스너의 myRef 값을 아래와 같이 변경한다.
val myRef = database.getReference("mySchedule").child(Firebase.auth.currentUser!!.uid)
② 파이어베이스에서 결과를 확인해보자.
① 데이터를 불러올 때도 마찬가지로 UID를 이용해 불러와야 한다. myRef의 addValueEventListener 부분을 아래와 같이 수정하자.
myRef.child(Firebase.auth.currentUser!!.uid).addValueEventListener(object : ValueEventListener {
② 안드로이드 Emulator에서 결과를 확인할 수 있다.