국비교육(22-23)

69일차(2)/Android App(12) : ListView, Adapter 활용 예제 / java to kotlin 작성 연습

서리/Seori 2023. 1. 15. 21:27

69일차(2)/Android App(12) : ListView, Adapter 활용 예제 / java to kotlin 작성 연습

 

- 사용자에게 입력받은 값을 ListView에 추가하기

- 같은 코드 Kotlin으로 작성해보기

 

 

- 새 모듈 생성

- java로 생성하기!!

 

step05example

package com.example.step05example;

import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;

import androidx.appcompat.app.AppCompatActivity;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    //필요한 필드 선언
    EditText editText;
    List<String> names;
    ArrayAdapter<String> adapter;
    ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //필요한 UI의 참조값을 UI에 부여된 id를 이용해서 얻어오기
        listView=findViewById(R.id.listView);
        editText=findViewById(R.id.editText);
        Button addBtn=findViewById(R.id.addBtn);
        //버튼 리스너 등록
        addBtn.setOnClickListener(this);
        //model생성
        names=new ArrayList<>();
        //adapter 생성
        adapter=new ArrayAdapter<>(this,
                android.R.layout.simple_list_item_1,
                names);
        //listView에 아답타 연결하기
        listView.setAdapter(adapter);

    }

    @Override
    public void onClick(View view) {
        //1. EditText에 입력한 문자열을 읽어온다.
        String inputName=editText.getText().toString();
        //2. 모델에 데이터 추가
        names.add(inputName);
        //3. ListView가 업데이트할 수 있도록 아답타에 모델이 수정되었다고 전달
        adapter.notifyDataSetChanged();
        //4. EditText에 입력한 문자열 삭제
        editText.setText("");
    }
}

 

activity_main.xml

<?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">
    <ListView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/listView"
        android:layout_weight="1"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/editText"
            android:hint="이름 입력..."/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="추가"
            android:id="@+id/addBtn"/>
    </LinearLayout>

</LinearLayout>

 

- 기본 Contstraint Layout에서 Linear Layout 으로 바꾸고싶다면 이렇게 코드에서 직접 수정해주면 된다.

- ListView, editText, Button요소 추가해주기

 

 

- 코드로 작성시 android: 는 입력하지 않아도 된다. 앞의 속성명만 작성하면 자동완성된다.

 

 

- 위와 같은 코드로 이런 형태가 된다.

- 아래쪽 텍스트 입력란과 버튼은 수평 정렬이고, 수평 폭을 나눠가지고 있다.

- 리스트뷰는 높이를 나누어가지고 있다. 하단 리니어가 일부 가지고, 남은 높이를 listview가 전부 가지고 있는 것.

 

- "vertical" 로 지정된 LinearLayout 의 자식 요소들은 수직 높이를 가지고 경쟁하게 된다.

- listview는 폭을 가지지 않고(0dp), 아래쪽 리니어는 필요한만큼만 가진다. 이후 listView가 남는 폭을 전부 가진다.

 

- 기준이 수평(horizontal)인 LinearLayout 자식요소들은 폭을 가지고 경쟁한다.

- EditText는 일단 0으로 하고, 버튼은 필요한 만큼의 폭만 가지고, 남은 폭은 EditText가 전부 가진다.

- 이렇게 레이아웃 전략을 잘 짜서 만들어보기!

 

 

- 문자열을 입력하고 추가버튼을 누르면 문자열이 리스트에 하나씩 출력되도록 할 예정!

- 버튼에 리스너를 등록해서 입력된 값을 읽어와서 리스트뷰에 하나씩 추가되도록 하면 된다.

 

- findViewById로 UI의 참조값을 얻어와서 리스너 등록해주기

- 다른 메소드에서 필요한 값은 필드로 선언해서 필드에 저장!

 

 

- 읽어와서 모델에 추가하고, notifyDataSetChanged() 로 아답타에 알리기

 

- 내가 입력한 값으로 리스트에 동적으로 아이템이 추가되는 것을 볼 수 있다.

 


 

- 똑같은 기능을 하는 메인 액티비티를 코틀린으로 만들어보기

 

- MainActivity가 들어있는 패키지에 우클릭- new - Activity - Empty Activity

 

- 언어를 코틀린으로 설정!

 

package com.example.step05example

import android.os.Bundle
import android.view.View
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.EditText
import android.widget.ListView
import androidx.appcompat.app.AppCompatActivity

/*
    extends AppCompatActivity 상속 => : AppCompatActivity
    implements View.OnClickListener 인터페이스 구현=> , View.OnClickListener
 */
class MainActivity2 : AppCompatActivity(), View.OnClickListener {
    //null로 초기화하기 위해서는 type 뒤에 ? 가 필요하다.
    var editText:EditText?=null;
    var names:MutableList<String>?=null;
    var adapter:ArrayAdapter<String>?=null
    //위의 선언이 불편하다면 아래와 같이 뒤늦은 초기화도 가능하다.
    lateinit var listView:ListView //참조값을 나중에 넣고 싶으면 lateinit 예약어를 사용하면 된다.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        listView=findViewById(R.id.listView)
        editText=findViewById(R.id.editText)
        //findViewById<UI의 type>
        var addBtn=findViewById<Button>(R.id.addBtn)
        addBtn.setOnClickListener(this)
        names=mutableListOf()
        adapter=ArrayAdapter(this,
            android.R.layout.simple_list_item_1,
            names!!) //names는 null이 확실히 아니니까 그냥 받아 주세요! 라는 의미이다.

        /*
            [ java ]
            .setXXX(value)
            [ kotlin ]
            .xxx = value
         */
        //아답타를 ListView에 연결
        listView.adapter=adapter
    }

    override fun onClick(p0: View?) {
        //editText?.text는 editText 안의 값이 null이 아니면 .text를 참조하겠다는 의미
        val inputName:String=editText?.text.toString()
        names?.add(inputName)
        adapter?.notifyDataSetChanged()
        editText?.setText("")
    }
}

 

** Kotlin에서는

- : 클래스명() 으로 클래스의 상속을 표기한다. () 는 기본 생성자를 호출하기 위한 것.

- : 인터페이스명 으로 인터페이스의 구현을 표기한다.

- 연이어서 클래스, 인터페이스의 상속, 구현을 할 때에는 , 로 표기해주면 된다.

- override는 메소드 앞에 override 예약어만 붙이면 된다.

- 레이아웃 xml문서는 똑같이 사용한다(activity_main).

 

- 필드 선언하기

- 코틀린에서는 선언하는 것만으로는 null이 되지않는다. 직접 null을 넣어주어야 한다.(초기화)

- 그리고 null을 넣어주려면 필드명 뒤에 ? 표기가 필요하다.

 

- 만약 만들어만 놓고 값은 나중에 넣고싶다면 lateinit 예약어를 사용

 

 

- java 코드와 직접 비교하면 이렇다. 비교하며 작성해보고 공부하기!

 

- 필요한 ui 의 참조값 얻어오기

- 인터페이스를 구현하고, 메소드를 override해준다.

- 이제 아래에서 버튼의 리스너를 this 로 등록할 수 있다.

 

- 필드에서 names에 이미 타입이 지정되어 있기 때문에 MutableList 의 타입은 지정하지 않아도 된다.

- List 대신 MutableList로 선언한다.

 

 

- java: (type)names

 Kotlin: names as type 

- 만약 type 캐스팅을 할 일이 있다면 이렇게 하면 된다.

 

- 오류가 발생해서 보니 null이 들어갈 수 없게 되어있다. null을 들어가지 않게 하려면 !! 를 넣으면된다.

- 이건 null이 아니니까 받아달라! 는 뜻이라고 생각하고 사용하면 된다..

 

 

- java :  .setXXX(value)
- kotlin:  .xxx = value

 

- 코틀린의 특징! java에서 setXXX.(value) 으로 값을 넣어주는 메소드를 코틀린에서는 그냥 = 으로 넣는다.

- 바로 대입연산자로 대입한다고 생각하면 된다.

 

- 아답타에 그냥 대입연산자로 넣어버린다! 그냥 필드에 값을 담듯이 넣으면 된다.

 

- 메소드에도 .getText() 가 없다. 이것도 그냥 필드 참조하듯이 text() 를 사용해서 넣으면 된다.

- 그냥 text.toString() 해주면 된다.

 

- null일 가능성이 있는 변수나 필드를 만들면 값을 가져올때도 ? 를 써주어야 하는 경우가 있다.

- editText?.text()editText 안의 값이 null이 아니면 .text를 참조하겠다는 의미

- 번거로운 면이 있다... 그래서 코틀린에서는 null을 그다지 안 쓰는 것이 편하다...

 

 

- override한 메소드. null일 가능성이 있는 view타입을 의미한다.

- 항상 ?를 넣어주어야해서 좀 불편하다..

 

- manifest에서 intent-filter 를 코틀린 쪽으로 옮겨오고 테스트하기!

 

- manifest에서 exported="true" 로 바꾸어주어야 나온다!(안그러면 오류 표시된다)

 

- java와 똑같이 작동되는 것을 확인할 수 있다.

 

- 사전에 필드 선언시 번거로운 부분이 많아서 lateinit 예약어를 쓸 일이 많다. 나중에 값을 집어넣으려고 할 경우에 쓰기!

 


 

- java 코드로 작성된 문서를 kotlin으로 변환하는 것을 요약하면,

 

- 클래스 상속은 : 클래스명 ()

- 인터페이스 구현은 : 인터페이스명

 

- null일 가능성이 있는 필드를 선언한다면, ? 를 붙여주기

- 자주 사용하던 java의 ArrayList<> 같은 경우에는 MutableList<> 로 받아주기

 

- lateinit : 지금은 null로 초기화하지만 나중에 꼭 null이 아닌 값을 넣을 것이다! 라는 의미

(만약 나중에 init 하지 않으면 당연히 nullPointException이 발생한다.)

 

- !! : null을 허용하지 않는 공간에 null 일 가능성이 있는 값을 전달하려면

      여기 들어가게 될 값이 null이 아니라는 것을 단언해야 한다!! 그런 의미로 사용.

 

 

- setter 메소드는 필드를 참조하는 것처럼 사용한다.

- setXXX라는 메소드가 없다.

- Null일 가능성이 있는 필드를 참조할때는 ? 를 붙여서 참조하기!