* 이 글은 인프런 강의 <[왕초보편] 앱 8개를 만들면서 배우는 안드로이드 코틀린>을 보고 정리한 글입니다.
폰트 적용하기
자신이 원하는 폰트를 텍스트에 적용하자.
1. 폰트를 다운로드 한 후, 파일명의 대문자를 소문자로 변경한다.
2. 리소스 폴더에 font 폴더를 생성하여 폰트 파일을 넣는다.
3. android:fontFamily="@font/bmjua_ttf"
텍스트뷰에 적용할 폰트를 설정한다.
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="40sp"
android:fontFamily="@font/bmjua_ttf"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
ListView, Adapter
데이터를 리스트뷰에 표현하기 위해 어댑터를 사용하는 방법을 알아보자.
1. activity_main.xml에 리스트뷰를 배치한다.
<ListView
android:id="@+id/mainListview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:id="@+id/mainListview"
리스트뷰의 아이디값을 mainListview로 지정한다.
2. 리스트 뷰의 내용을 넣을 ListViewModel이라는 데이터 클래스를 만들어준다.
data class ListViewModel (
var title : String = "",
var content : String = ""
)
var title : String = "",
var content : String = ""
title(제목)과 content(내용)을 String(문자열)로 받겠다.
3. listview_item.xml에서 리스트뷰에서 제목과 내용을 어떻게 표시할지 레이아웃을 배치한다.
<TextView
android:id="@+id/listViewItem"
android:textSize="30sp"
android:layout_margin="5dp"
android:text="리스트뷰 아이템"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/listViewItem2"
android:textSize="20sp"
android:layout_margin="5dp"
android:text="리스트뷰 아이템2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
4. 데이터를 전달할 어댑터 클래스 ListViewAdapter를 만든다.
class ListViewAdapter(val List : MutableList<ListViewModel>) : 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 { // 리스트 뷰를 생성하는 함수
var converView = convertView
if (converView == null) {
converView = LayoutInflater.from(parent?.context).inflate(R.layout.listview_item, parent, false)
}
val title = converView!!.findViewById<TextView>(R.id.listViewItem)
val content = converView!!.findViewById<TextView>(R.id.listViewItem2)
title.text = List[position].title
content.text = List[position].content
return converView!!
}
}
class ListViewAdapter(val List : MutableList<ListViewModel>) : BaseAdapter() {
BaseAdapter 형식으로 List를 가져오는 어댑터 클래스
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
어댑터를 통해 데이터를 activity_main.xml의 리스트뷰에 전달하기 위한 함수
var converView = convertView
뷰를 converView 변수로 지정
if (converView == null) {
converView = LayoutInflater.from(parent?.context).inflate(R.layout.listview_item, parent, false)
}
listview_item.xml의 레이아웃을 가져옴
val title = converView!!.findViewById<TextView>(R.id.listViewItem)
val content = converView!!.findViewById<TextView>(R.id.listViewItem2)
listview_item.xml의 텍스트뷰의 아이디를 통해 listViewItem(제목 표시), listViewItem2(내용 표시)을 각각 title과 content 변수로 선언
title.text = List[position].title
content.text = List[position].content
리스트의 title부분(ListViewModel 데이터 클래스의 변수)을 title(위에 변수로 선언한 listview_item.xml의 listViewItem 부분)의 텍스트로 지정.
content도 마찬가지.
return converView!!
뷰를 반환함. (어댑터를 통해 listview_item.xml에 title과 content를 전달함)
5. MainActivity에 리스트뷰에 넣을 데이터를 만들고 어댑터에 전달한다. 그 후 어댑터의 반환값을 activity_main.xml의 리스트뷰에 전달한다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val list_item = mutableListOf<ListViewModel>()
list_item.add(ListViewModel("a", "b"))
list_item.add(ListViewModel("c", "d"))
list_item.add(ListViewModel("e", "f"))
val listview = findViewById<ListView>(R.id.mainListview)
val listAdapter = ListViewAdapter(list_item)
listview.adapter = listAdapter
}
}
val list_item = mutableListOf<ListViewModel>()
ListViewModel 형식(데이터클래스)의 리스트를 만든다.
list_item.add(ListViewModel("a", "b"))
"a"와 "b"는 각각 ListViewModel 형식의 title, content이다. 이것을 리스트에 넣는다. (데이터 만듦)
val listview = findViewById<ListView>(R.id.mainListview)
activity_main.xml의 리스트뷰 아이디 값이 mainListview이다. 이것을 listview라는 변수에 넣는다.
val listAdapter = ListViewAdapter(list_item)
만든 데이터 list_item을 어댑터(ListViewAdpater)에 넣는다. 어댑터의 반환값은 listAdpater라는 변수가 갖는다.
listview.adapter = listAdapter
activity_main.xml의 리스트뷰(listview)가 어댑터를 통한 반환값 listAdpater를 받는다.
뒤로가기 버튼 이벤트
안드로이드에서 뒤로가기 버튼을 두 번 눌렀을 때 앱이 종료되도록 하자.
class MainActivity : AppCompatActivity() {
private var isDouble = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onBackPressed() {
Log.d("MainActivity", "backbutton")
if (isDouble == true) {
super.onBackPressed()
}
isDouble = true
Toast.makeText(this, "종료하실려면 더블클릭", Toast.LENGTH_LONG).show()
Handler().postDelayed({
isDouble = false
}, 2000)
}
}
private var isDouble = false
두번 눌렀는지의 변수로 false 초기값 형성
override fun onBackPressed() {
뒤로가기 버튼을 눌렀을 때의 처리 함수
Log.d("MainActivity", "backbutton")
뒤로가기 버튼을 눌렀을 때 "MainActivity" 이름으로 "backbutton" 로그가 잡히는지 확인 (디버깅)
if (isDouble == true) {
super.onBackPressed()
}
isDouble이 true일 때 앱이 종료됨.
isDouble = true
뒤로가기 버튼을 눌렀을 때 isDouble을 true로 설정
Toast.makeText(this, "종료하실려면 더블클릭", Toast.LENGTH_LONG).show()
"종료하실려면 더블클릭" 토스트 메시지 띄움
Handler().postDelayed({
isDouble = false
}, 2000)
2000밀리초(2초) 후 isDouble이 false가 됨.
즉, 2초 안에 뒤로가기를 누르지 않으면 앱이 종료가 되지 않는다.
목표
첫 화면에는 랜덤으로 명언 한 줄과 전체 명언 보기 버튼이 있다.
전체 명언 보기 버튼을 누르면 리스트뷰로 명언들의 리스트들이 적혀져 있다.
1. build.gradle.kts
dataBinding을 사용하겠다고 작성한다.
android {
...
dataBinding{
enable = true
}
}
2. activity_main.xml
첫 화면인 MainActivity의 레이아웃을 구성한다.
명언이 한 줄 나오고 전체명언을 보기위한 버튼을 만든다.
<?xml version="1.0" encoding="utf-8"?>
<layout>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
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:background="@color/black"
tools:context=".MainActivity">
<Button
android:id="@+id/showAllSentenceBtn"
android:layout_width="150dp"
android:layout_height="50dp"
android:text="전체명언보기"
android:layout_margin="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/goodWordTextArea"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="명언이 들어갈 위치입니다"
android:fontFamily="@font/bmjua_ttf"
android:layout_margin="20dp"
android:gravity="center"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<layout>
데이터 바인딩이 layout을 관리한다.
android:background="@color/black"
배경색은 검은색으로 한다.
<Button
android:id="@+id/showAllSentenceBtn"
버튼의 아이디값은 showAllSentenceBtn으로 한다.
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
버튼이 오른쪽(End) 위(Top)에 위치하도록 한다.
<TextView
android:id="@+id/goodWordTextArea"
텍스트뷰의 아이디값은 goodWordTextArea로 한다.
android:fontFamily="@font/bmjua_ttf"
폰트는 bmjua_ttf으로 설정
3. MainActivity
데이터 바인딩을 이용하여 layout을 조정한다.
또한 인텐트(Intent)를 이용하여 버튼을 클릭하면 SentenceActivity 화면으로 전환하도록 한다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
lateinit var binding : ActivityMainBinding
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val sentenceList = mutableListOf<String>()
sentenceList.add("검정화면에 대충 흰글씨 쓰면 명언같다.")
sentenceList.add("삶이 있는 한 희망은 있다 -키케로")
sentenceList.add("하루에 3시간을 걸으면 7년 후에 지구를 한바퀴 돌 수 있다. -사무엘존슨")
sentenceList.add("언제나 현재에 집중할수 있다면 행복할것이다. -파울로 코엘료")
sentenceList.add("산다는것 그것은 치열한 전투이다. -로망로랑")
sentenceList.add("피할수 없으면 즐겨라 – 로버트 엘리엇")
sentenceList.add("진정으로 웃으려면 고통을 참아야하며 , 나아가 고통을 즐길 줄 알아야 해 -찰리 채플린")
sentenceList.add("직업에서 행복을 찾아라. 아니면 행복이 무엇인지 절대 모를 것이다 -엘버트 허버드")
Log.d("MainActivity", sentenceList.random())
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.showAllSentenceBtn.setOnClickListener {
val intent = Intent(this, SentenceActivity::class.java)
startActivity(intent)
}
binding.goodWordTextArea.setText(sentenceList.random())
}
}
val sentenceList = mutableListOf<String>()
명언들을 담을 String 형태의 리스트를 변수를 선언한다.
sentenceList.add("검정화면에 대충 흰글씨 쓰면 명언같다.")
리스트에 명언들을 추가한다.
Log.d("MainActivity", sentenceList.random())
MainActivity라는 이름으로 명언이 랜덤하게 나오는지 로그를 본다. (디버깅)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
데이터 바인딩을 이용하여 layout을 관리한다.
binding.showAllSentenceBtn.setOnClickListener {
val intent = Intent(this, SentenceActivity::class.java)
startActivity(intent)
}
showAllSentenceBtn(버튼)을 클릭했을 때 SentenceActivity 화면으로 전환시킨다.
binding.goodWordTextArea.setText(sentenceList.random())
goodWordTextArea(명언이 보일 위치)에 명언이 랜덤하게 나오도록 한다.
4. activity_sentence.xml
SentenceActivity의 레이아웃에 리스트뷰를 구성한다.
<ListView
android:id="@+id/sentenceListView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ListView
android:id="@+id/sentenceListView"
리스트뷰의 아이디 값은 sentenceListView
5. listview_item.xml
리스트뷰의 각 요소가 어떻게 레이아웃 될지 구성한다. 텍스트뷰를 배치한다.
<TextView
android:id="@+id/listViewTextArea"
android:textSize="15sp"
android:fontFamily="@font/bmjua_ttf"
android:layout_margin="10dp"
android:text="TextArea"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/listViewTextArea"
텍스트뷰의 아이디는 listViewTextArea
android:fontFamily="@font/bmjua_ttf"
폰트는 bmjua_ttf으로 설정하였다.
6. ListViewAdapter
데이터를 전달할 어댑터를 만든다.
class ListViewAdapter(var List :MutableList<String>) : BaseAdapter() {
...
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 listViewText = convertView?.findViewById<TextView>(R.id.listViewTextArea)
listViewText!!.text = List[position]
return convertView!!
}
}
if(convertView == null) {
convertView = LayoutInflater.from(parent?.context).inflate(R.layout.listview_item, parent, false)
}
listview_item 레이아웃을 가져온다.
val listViewText = convertView?.findViewById<TextView>(R.id.listViewTextArea)
listview_item.xml의 텍스트뷰의 아이디 값 listViewTextArea를 listViewText 변수에 넣는다.
listViewText!!.text = List[position]
데이터들을 listVewText(텍스트뷰)의 텍스트로 설정한다.
return convertView!!
listview_item 레이아웃에 대해 위와 같이 설정된 뷰를 반환한다.
7. SentenceActivity
데이터를 만든다. 그리고 그 데이터를 어댑터에 전달하여 어댑터의 반환값을 activity_sentence.xml의 리스트뷰에 적용한다.
class SentenceActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sentence)
val sentenceList = mutableListOf<String>()
sentenceList.add("검정화면에 대충 흰글씨 쓰면 명언같다.")
sentenceList.add("삶이 있는 한 희망은 있다 -키케로")
sentenceList.add("하루에 3시간을 걸으면 7년 후에 지구를 한바퀴 돌 수 있다. -사무엘존슨")
sentenceList.add("언제나 현재에 집중할수 있다면 행복할것이다. -파울로 코엘료")
sentenceList.add("산다는것 그것은 치열한 전투이다. -로망로랑")
sentenceList.add("피할수 없으면 즐겨라 – 로버트 엘리엇")
sentenceList.add("진정으로 웃으려면 고통을 참아야하며 , 나아가 고통을 즐길 줄 알아야 해 -찰리 채플린")
sentenceList.add("직업에서 행복을 찾아라. 아니면 행복이 무엇인지 절대 모를 것이다 -엘버트 허버드")
val sentenceAdapter = ListViewAdapter(sentenceList)
val listview = findViewById<ListView>(R.id.sentenceListView)
listview.adapter = sentenceAdapter
}
}
val sentenceList = mutableListOf<String>()
명언들(데이터)를 넣을 String 형태의 리스트 변수를 선언
sentenceList.add("검정화면에 대충 흰글씨 쓰면 명언같다.")
리스트 변수에 명언들(데이터)를 넣는다.
val sentenceAdapter = ListViewAdapter(sentenceList)
어댑터에 데이터를 넣는다. sentenceAdapter는 어댑터의 반환값이다.
val listview = findViewById<ListView>(R.id.sentenceListView)
activity_sentence.xml의 리스트뷰를 listview 변수에 넣는다.
listview.adapter = sentenceAdapter
어댑터의 반환값을 리스트뷰에 적용한다.