| 파일 또는 폴더명 | 설명 |
|---|---|
| AndroidManifest.xml | 안드로이드 앱 설정 정보 파일 앱의 구성요소 및 속성 등을 정의 (시작 액티비티 지정) -> OS한테 전해짐 |
| 패키지/ MainActivity (.kt) | 자동으로 추가하는 기본 액티비티의 구현(코틀린) 파일 -> 앱 화면의 기능을 구현 |
| res/layout/activity_main.xml | 자동으로 추가하는 기본 액티비티의 레이아웃 XML 파일 |
| res/menu 폴더 | 앱에서 사용할 메뉴의 구성 정의 |
| res/ mipmap 폴더 | 앱에서 사용하는 아이콘 이미지 파일을 화면 밀도(density)별로 생성 (새로 추가됨) |
| res/ drawable 폴더 | 앱에서 사용할 이미지 파일을 저장 |
| res / values 폴더 | 앱에서 사용할 값(문자열, 수치값, 색상값 등)을 XML 파일로 저장 |
| build.gradle (Module: ...) | 앱을 빌드하기 위한 설정정보 파일 필요한 외부 라이브러리 등의 정보를 기록 |
R.class (R.jar) : Build 시 자동 생성 -> (resource)[ID] : 특정 뷰를 참조하기 위한 식별정보[activity_main.xml] 열기@[+]id/view_id : + 는 ID를 새로 지을 때만 표시[TextView]의 경우android:id="@+id/textView"[layout_width] / [layout_height]val editText = findViewById<EditText>(R.id.editText)[text]를 사용하여 현재 쓰여진 값을 읽어옴 -> getter 쓰지 않음val text : String = editText.text.toString().toString() 으로 String 변환 -> text는 Editable 객체 반환Toast myToast = Toast.makeText(this, "Hello", Toast.LENGTH_SHORT);
myToast.show();
Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show();
// this : Context 객체 (Activity 상속)
//"Hello" : 출력 메시지
// Toast.LENGTH_SHORT : 출력 시간 (or LENGTH_LONG)
// show() 를 실행시켜야 화면에 표시됨
setContentView(R.layout.activity_linear)<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="top"
android:text="Button" />
<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/btn2"
android:layout_alignParentTop="true"
android:layout_marginLeft="78dp"
android:layout_marginTop="50dp"
android:text="ButtonA" />
<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/btn1"
android:layout_marginLeft="97dp"
android:layout_marginTop="43dp"
android:text="ButtonB" />
setContentView(...)에 전달하여 Activity에 배치class MainActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
val layout = LinearLayout(this).apply{ // 초기화 객체
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
}
val btn1= Button(this)
val btn2 = Button(this)
layout.addView(btn1) // add : linearlayout에 버튼 만들어줌, 오버로딩한 것
layout.addView(btn2)
setContentView(layout)
}
}
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
steContentView(R.layout.activity_linear)
}
fun onClick(view: View) {
val layout = findViewById<LinearLayout>(R.id.linearLayout)
layout.orientation = LinearLayout.HORIZONTAL
}
}
findViewByID<TYPE>(ID) 사용fun onClick(view: View){
val textView = findViewByID<TextView>(R.id.textView) // 지역변수이므로 재사용 불가,
// 멤버변수 선언시 불필요한 멤버 증가
textView.text= "Hello"
}
android{
...
}
viewBinding{ // 추가 !!!
enable = true
}
class MainActivity : AppCompatActivity() {
lateinit var binding = ActivityMainBinding // Binding 객체 선언: 타입 자동 생성
// activity_main.xml -> ActivityMainBinding
override fun onCreate(savedInstanceState : Bundle?){
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater) // Binding 객체 생성: XML의 View와 Binding 객체 연결
val root = binding.root // 최상위 레이아웃 객체
setContentView(root) // 액티비티 화면에 보여줄 객체 지정
}
fun onClick(view: View){
binding.textView.text = "Hello!" // binding: Binding객체, textView: XML View의 ID, text: View의 속성
}
}
import android.view.View // View import 수행
// 수행 후 alt+enter 로 @JvmOverloads 추가
class MyCustomView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
override fun onDraw(canvas: Canvas){ // onDraw() : 뷰 모양을 그리는 함수
super.onDraw(canvas)
canvas.drawColor(Color.LTGRAY) // canvas : 모양을 그리는 공간
val paint = Paint() // paint : 그리기 도구
paint.color = Color.GREEN
canvas.drawCircle(200.toFloat(), 200.toFloat(), 100.toFloat(), paint)
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.contraintlayout.widget.ConstraintLayout xml ...
...
<TextView .../>
<ddwu.com.mobile.customviewtest.MyCustomView // 풀패키지명으로 클래스 지정
android:id="@+id/myView"
android:layout_width="100dp"
android:layout_height="100dp" /> // 너비 높이 필수 지정, 편의를 위해 임의 크기 지정
</androidx.constraintlayout.width.ContraintLayout>
//점그리기
canvas.drawPoint(x: Float, y:Float, paint: Paint)
// 선 그리기
canvas.drawLine(startXLFloat, startY:Float, stopX: Float, stopY:Float, paint:Paint)
//원그리기
canvas.drawCircle(s:Float, y:Float, radius:Float, paint:Float)
// 사각형 그리기
canvas.drawRect(left:Float, top: Float, right: Float, bottom: Float, paint: Float)
//텍스트 그리기
canvas.drawText(text:String, x: Float, y: Float, paint: Float)
val paint = Paint() // Paint 객체 생성
// val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.isAntiAlias = true // Anti-Alias 지정
paint.setARGB(255, 255, 255, 0) // 색상 및 투명도 지정
// paint.color = Color.GREEN
paint.strokeWidth = 5.0f // 그리기 선의 두께 지정
class MyCustomView @JvmOverloads constructor ( ..){
var radius = 100.0f // 그리기 관련 정보 멤버변수 추가
override fun onDraw(canvas: Canvas){
...
canvas.drawCircle(200.toFloat, 200.toFloat, radius, paint)
}
}
class MainActivity : AppCompatActivity(){
...
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.myView.radius = 200.0f // 멤버변수 값 변경
binding.myView.invalidate() // invalidate() 호출을 통해 onDraw( ) 간접 호출
}
}
class MyCustomView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr){
var posX = 200.0f
var posY = 200.0f
var radius = 100.0f
var color = Color.GREEN
override fun onDraw(canvas: Canvas){
super.onDraw(canvas)
canvas.drawColor(Color.LTGRAY)
val paint = Paint()
paint.color = color
canvas.drawCircle(posX, posY, radius, paint)
}
override fun onTouchEvent(event: MotionEvent?) : Boolean { // 상속받은 onTouchEvent() 재정의
Toast.makeText(context, "Touch!", Toast.LENGTH_SHORT).show()
Log.i("MyCustomView", "x: ${event?.x} y: ${event?.y}")
return true // 이벤트 처리 완료했을 경우 true, 다른 처리가 필요할 경우 false
}
}
class MainActivity : AppCompatActivity(){
lateinit var binding = ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
...
binding = AcitvityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
...
val myClick = MyClick(this)
binding.button.setOnClickListner(myClick) // 객체 생성 후 이벤트 처리가 필요한 View에 연결
}
class MyClick(val context: Context) : View.OnClickListener { // 클릭에 해당하는 OnClickListener 인터페이스
override fun onClick(view : View?){
Toast.makeText(context, "리스너 인터페이스 클래스 구현)", Toast.LENGTH_SHORT).show()
}
}
}
class MainActivity : AppCompatActivity(), View.OnClickListener{ // 리스너 인터페이스 상속
lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInsatanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
...
binding.button.setOnClickListener(this) // 현재 액티비티가 리스너 인터페이스 구현 객체이므로 this 사용
}
override fun onClick(view: View?){ // 상속한 리스너 인터페이스의 멤버함수를 액티비티에서 구현
Toast.makeText(this, "액티비티가 리스너 인터페이스 구현", Toast.LENGTH_SHORT).show()
}
}
class MainActivity : AppCompatActivity() {
lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
...
binding.button.setOnClickListener(myClick) // 객체 연결
}
val myClick = object: View.OnClickListener { // 리스너 인터페이스에서 직접 객체 생성
override fun onClick(view: View?){ // object : 상속 클래스 구현 없이 기존 클래스 또는 인터페이스를 구현한 객체 생성
Toast.makeText(this@MainActivity, "익명 객체로 리스너 인터페이스 구현", Toast.LENGTH_SHORT).show()
} // this@MainActivity: this키워드가 MainActivity 객체가 아닌
// object에 의해 생성된 객체를 지시하므로 MainAcitivity로 명확히 지정
}
}
class MainActivity : AppCompatActivity(){
lateinit var binding : AcitivityMainBinding
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
...
binding.button.setOnClickListener{ // ( ) 가 생략된 형태임.
Toast.makeText(this, "SAM 적용 형태로 구현", Toast.LEGNTH_SHORT).show()
} // View.setOnClickListener에는 OnClickListener의 단일 함수를 전달하게 약속되어 있으므로 객체 및 함수명 생략 가능
}
}
import android.util.log // Log 클래스 패키지
class MainActivity : AppCompatAcitvity() {
val TAG = "MainActivity" // Log의 TAG 문자열 보편적으로 클래스명 기재
...
override fun onCreate(savedInstanceState: Bundle?){
...
binding.button.setOnClickListener{
Toast.makeText(this, "SAM 적용 ", Toast.LENGTH_SHORT).show()
Log.i(TAG, "SAM 적용 형태로 구현") // 로그를 사용하여 필요 정보 출력
}
}
}
override fun onCreate(savedInstanceState: Bundle?){
...
binding.button.setOnClickListener{
val builder: AlertDialog.Builder = AlertDialogBuilder(this).apply{ // 객체.apply{...} : 코틀린 범위지정함수,
// 객체 생성후 필요 set 수행
setTitle("대화상자 제목") // 대화상자 제목
setMessage("대화상자 메시지") // 대화상자 본문 메시지
setIcon(R.mipmap.ic_launcher) // 제목 옆 아이콘
setPositiveButton("확인", posClick) // 확인 버튼 추가
setNeutralButton("대기", null) // 대기 버튼 추가
setNegativeButton("취소", null) // 취소버튼 추가
setCancelable(false) // 대화상자 외부/back 버튼을 클릭해도 대화상자 유지
}
val dialog: Dialog = builder.create() // 대화상자 생성
dialog.show() .. 대화상자 표시
// builder.show() -> 위에 2줄을 이렇게 한 번에 표시 가능
}
}
// 1번
val posClick = DialogInterface.OnClickListener{ // 사용하는 리스너 인터페이스 표시
dialogInterface: DialogInterface?, whichButton: Int -> // Lambda 함수 형식 사용 시
Toast.makeText(this@MainActivity, "확인!", Toast.LENGTH_SHORT).show()
}
// 2번
binding.button.setOnClickListener{
val builder: AlertDialog.Builder = AlertDialog.Builder(this).apply{
...
setPositiveButton("확인", posClick)
setNeutralButton("대기", null)
setNegativeButton("취소", { di, _ ->
Toast.makeText(this@MainActivity, "종료!", Toast.LENGTH_SHORT).show() // 이벤트 핸들러 직접 추가
di.cancel()
})
setCancelable(false)
}
}
val posClick = DialogInterface.OnClickListener{
_, _ ->
Toast.makeText(this@MainActivity, "확인", Toast.LENGTH_SHORT).show()
}
-> modal도 modaless도 아님
binding.btnList.setOnClickListener{
val builder: AlertDialog.Builder = AlertDialog.Builder(this).apply{
setTitle("목록 대화상자") // 대화상자 제목
setIcon(R.mipmap.ic_launcher) // 제목 옆 아이콘
setItems(R.arrays.foods){ _, index ->
val foods = resources.getStringArray(R.array.foods) // res에 작성한 리소스를 ID를 사용하여 가져옴
Log.i(TAG, "Selected Item: ${foods[index]}")
}
setPositiveButton("닫기", null)
}
builder.show()
}
var selectedIndex = 0 // 선택 항목 인덱스
binding.btnRadio.setOnClickListener {
val builder: AlertDialog.Builder = AlertDialog.Builder(this).apply{
setTitle("선택 대화상자")
setIcon(R.mipmap.ic_launcher)
setSingleChoiceItems(R.array.foods, selectedIndex) {
_, index -> selectedInex = index // 현재 선택 항목 인덱스를 변수에 보관
}
setPositiveButton("선택 완료") { _, _ ->
val foods = resources.getStringArray(R.array,foods)
Log.i(TAG, "Selected Item: ${foods[selectedInex]}") // 현재 선택한 항목의 index를 사용하여 선택항목 확인
}
}
builder.show()
}
val selectedItems = booleanArrayOf(false, false, false) // 체크항목 상태 기록
binding.btnCheck.setOnClickListener{
val builder: DialogAlert.Builder = DialogAlert.Builder(this).apply{
setTitle("다중선택 대화상자")
setIcon(R.mipmap.ic_launcher)
setMultiChoiceItems(R.arrays.foods, selectedItems){ // 체크상태 기록
_ : DialogInterface?, index: Int, isChecked: Boolean ->
selectedItems[index] = isChecked // isChecked(true/false) : 현재 항목의 체크 상태
}
setPositiveButton("선택 완료"){ _, _ ->
val foods = resources.getStringArray(R.arrays.foods)
for((index, isChecked) in selectedItems.withIndex())
Log.i(TAG,"Items: ${foods[index]} : ${isChecked}")
}
}
builder.show()
}
val dialogBinding = DialogCustomBinding.inflate(layoutInflater) // 레이아웃 inflation : 대화상자용 레이아웃
binding.btnCustom.setOnClickListener{
val builder: DialogAlert.Builder = DialogAlert.Builder(this).apply{
setTitle("대화상자")
setIcon(R.mipmap.ic_launcher)
setView(dialogBinding.root) // 레이아웃 사용
setPositiveButton("입력"){ _, _ ->
val id = dialogBinding.etId.text.toString() // 레이아웃에 배치한 뷰 활용
val pwd = dialogBinding.etPwd.text.toString()
Log.i(TAG, "User : ${id} / ${pwd} ")
}
}
builder.show()
}