안드로이드 : 레이아웃과 이벤트 처리
FrameLayout
무조건 왼쪽 상단을 기준으로 배치하는 레이아웃
자식뷰를 2개 이상 배치하게 되면 겹쳐서 출력됩니다.
-> 동적으로 visivility 속성을 수정해서 보이게 하고 안보이게 하고 해서 출력
-> addView(View view), removeView를 이용해서 뷰를 추가하거나 삭제하는 형태로 사용
ConstraintLayout
다른 뷰나 Parent를 기준으로 뷰를 배치하는 기법
bias 속성을 이용해서 비율을 설정 가능
margin을 이용해서 다른 뷰와의 간격 설정 가능
ratio를 이용해서 자신의 너비와 높이를 비율로 설정하는 것도 가능
AbsoluteLayout
왼쪽 상단을 기준으로 좌표를 설정해서 배치하는 레이아웃
> deprecated
ScrollView
GUI 시스템에서는 뷰의 크기보다 큰 콘텐츠를 배치할 수 있지만 전부 출력할 수 없습니다.
이런 경우에는 ScrollView 를 배치하고 그 안에 뷰를 배치해야 합니다.
ScrollView 안에는 하나의 View 만 배치 가능
여러개의 View 를 배치할 것이라면 Layout 을 만들어서 View들을 배치하고
그 이후에 Layout 을 ScrollView 에 배치해야 합니다.
package kr.co.tjoeun.layouttest2
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
// 클릭 이벤트 처리를 위한 클래스를 생성
inner class EventHandler : View.OnClickListener {
override fun onClick(v: View?) {
// 내부 클래스 안에서 외부 클래스의 인스턴스를 지정 할 때는
// this@외부클래스이름 으로 작성 합니다.
// 외부 클래스의 멤버를 사용하고자 하면 class 앞에 inner 를 붙여야 합니다.
Toast.makeText(this@MainActivity,
"터치 이벤트", Toast.LENGTH_LONG).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 버튼의 클릭 이벤트 연결
btn1.setOnClickListener(EventHandler())
val clickListener:View.OnClickListener = object : View.OnClickListener{
override fun onClick(v: View?) {
Toast.makeText(
this@MainActivity, "익명 클래스 이용",
Toast.LENGTH_LONG).show()
}
}
btn1.setOnClickListener(clickListener)
// 람다 표현식을 이용 - 안드로이드가 추천
btn2.setOnClickListener {
Toast.makeText(this, "람다 이용", Toast.LENGTH_LONG).show()
}
// 버튼이 숨겨지게 됩니다.
//btn.visibility = View.INVISIBLE
}
// 터치 이벤트 처리를 위한 메소드를 재정의
override fun onTouchEvent(event: MotionEvent?): Boolean {
// 토스트 출력
// 내부 클래스나 람다에서 Activity 를 지정할 때는 this@Activity이름
Toast.makeText(this, "화면 터치", Toast.LENGTH_LONG).show()
return super.onTouchEvent(event)
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
tools:context=".MainActivity"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:id="@+id/textView" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="버튼1"
android:id="@+id/btn1"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="버튼2"
android:id="@+id/btn2"
/>
</LinearLayout>
Android 의 Event Handling
1. 종류
1) Hierarchy Event Model: 제공 되는 메소드를 오버라이딩 해서 처리하는 방식
2) Delegate Event Model: 자신에게 발생한 이벤트를 다른 인스턴스에게 처리해 달라고 하는 방식
2. Hierarchy Event Model
여러 클래스들에서 공통된 이벤트 처리 메소드나 자주 사용하는 이벤트 처리 메소드인 경우
- View 들의 Touch 이벤트, 4 대 컴포넌트의 수명 주기 관련 메소드
> 이벤트 처리 메소드 매개 변수가 이벤트에 대한 정보만 소유하고 있습니다.
3. Delegate Event Model
하나의 클래스에서만 사용 되거나 일부분의 클래스에서만 사용되는 이벤트인 경우
이벤트 처리를 위한 Listener Interface를 내부 인터페이스로 소유하고 있습니다.
> 이벤트 처리 메소드의 첫번째 매개 변수는 이벤트가 발생한 객체를 확인 할 수 있는 것으로 만들어져 있습니다.
View 에는 Touch 이벤트 처리를 위한 onTouchEvent
(event: MotionEvent): Boolean 이라는 메소드 존재
Toast
짦은 문자열을 출력해주는 작은 팝업 대화 상자
화면 하단에 플로팅 형태로 출력 되었다가 사라지는 대화 상자
사용자의 이벤트는 받지 못합니다.
시스템이 제공하기 때문에 앱이 종료 되어도 화면에 출력이 됩니다.
Toast.makeText(context:Context, resId:Int, duration:Int)
Toast.makeText(context:Context, text:CharSequence, duration:Int)
=> 생성한 후 show 라는 메소드를 호출하면 화면에 출력이 됩니다.
안드로이드에서 문자열을 사용할 때 가끔 CharSequence 나 Editable 이 사용 되는 경우가 있는 이들은 전부 String 의 상위 인터페이스 입니다.
대입을 할 때는 String 을 대입하면 됩니다.
CharSequence 나 Editable 로 리턴 되는 경우에는 toString() 을 호출 해서 String 으로 변환 해서 사용해야 합니다.
String str = edit.getText().toString()
Context: 그리기 위한 정보를 가진 객체Context를 구현한 클래스가 View 하고 Activity 입니다.Android 에서 Context를 요청하면 Activity 클래스의 인스턴스를 대입하면 됩니다.
Delegate Event Model
이벤트를 처리할 수 있는 메소드를 가진 인스턴스를 이벤트를 처리할 뷰에 이벤트 처리 리스너로 설정해서 메소드를 호출하는 방식
뷰.setOn???Listener(On???Listener 인터페이스를 구현한 클래스의 인스턴스) 로 연결
=> 인터페이스를 구현한 클래스의 인스턴스 생성
1. 클래스를 만들고 인스턴스 생성
2. 클래스를 만들지 않고 바로 인스턴스 생성
3. 람다를 이용하는 방법 - 인터페이스에 메소드가 1개인 경우만 사용 가능
4. 인터페이스에 메소드가 1개인 경우더라도 이벤트 라우팅을 하고자 하는 경우에는 1, 2 번 방법을 사용
이벤트 라우팅: 여러 뷰의 이벤트를 하나의 인스턴스로 처리하는 방법
View 에는 ClickEvent 를 처리 할 수 있는 Listener 가 존재Button 의 Click 이벤트 처리하기
이벤트 처리 메소드의 매개 변수를 확인
Listener 인터페이스의 첫번째 매개 변수는 무조건 이벤트가 발생한 객체두번째 매개 변수 부터는 이벤트에 대한 정보를 전달하는 객체입니다.
상속 받은 메소드를 재정의하는 구조에서는 첫번 째 매개 변수부터 이벤트에 대한 정보를 가진 객체 입니다.
1. Touch 이벤트 - OnTouchListener
MotionEvent 라는 매개 변수가 전달 됩니다.
터치를 한 좌표에 대한 정보를 리턴해주는 메소드와 동작을 리턴해주는 getAction 메소드가 있고 getAction 메소드의 리턴값은 ACTION_DOWN, ACTION_MOVE, ACTION_UP 입니다.
2. Key 이벤트 - OnKeyListener
키보드를 눌렀을 때 발생하는 이벤트KeyEvent라는 매개 변수가 전달 됩니다. 그리고 Int 타입으로 KeyCode 가 전달 됩니다.KeyCode 는 누른 키보드 값입니다.키보드 값은 KeyCode 의 상수로 전달
3. Click 이벤트 - OnClickListener
클릭했을 때 발생하는 이벤트별도로 매개 변수가 전달 되지 않습니다.
4. LongClick 이벤트 - onLongClickListener
길게 눌렀을 떄 발생하는 이벤트별도로 매개 변수가 전달 되지 않습니다.
5. Click 과 LongClick 이벤트에 한해서는
xml 파일에서 onClick 과 onLongClick 속성에 이벤트 처리 메소드 이름을 기재 해서 처리가 가능
6. Event Routing 2개 이상의 뷰에게 발생한 이벤트를 하나의 객체가 처리하도록 하는 것첫번째 매개 변수가 이벤트가 발생한 뷰이기 때문에 가능
package kr.co.tjoeun.layouttest2
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.activity_main2.*
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
val clickListener : View.OnClickListener = object : View.OnClickListener{
override fun onClick(v: View?) {
if(v?.id == R.id.button1) {
label.setText("버튼 1 누름")
}else if(v?.id == R.id.button2){
label.setText("버튼 2 누름")
}else{
label.setText("버튼 3 누름")
}
}
}
// 이벤트 라우팅
button1.setOnClickListener(clickListener)
button2.setOnClickListener(clickListener)
button3.setOnClickListener(clickListener)
/*
button1.setOnClickListener {
label.text = "버튼 1을 눌렀습니다."
}
*/
}
}
package kr.co.tjoeun.layouttest2
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.activity_main2.*
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
val clickListener : View.OnClickListener = object : View.OnClickListener{
override fun onClick(v: View?) {
label.setText("${v?.id} 누름")
}
}
// 이벤트 라우팅
button1.setOnClickListener(clickListener)
button2.setOnClickListener(clickListener)
button3.setOnClickListener(clickListener)
/*
button1.setOnClickListener {
label.text = "버튼 1을 눌렀습니다."
}
*/
}
}
package kr.co.tjoeun.layouttest2
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import kotlinx.android.synthetic.main.activity_main2.*
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
val clickListener : View.OnClickListener = object : View.OnClickListener{
override fun onClick(v: View?) {
var btn : Button = v as Button
label.setText("${v?.text} 누름")
}
}
// 이벤트 라우팅
button1.setOnClickListener(clickListener)
button2.setOnClickListener(clickListener)
button3.setOnClickListener(clickListener)
/*
button1.setOnClickListener {
label.text = "버튼 1을 눌렀습니다."
}
*/
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
tools:context=".MainActivity2"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/label"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="버튼1"
android:id="@+id/button1"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="버튼2"
android:id="@+id/button2"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="버튼3"
android:id="@+id/button3"/>
</LinearLayout>