1.  activity_main.xml

데이터 피커, 에디트텍스트, 버튼을 1개씩 생성

<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">

    <DatePicker
        android:id="@+id/datePicker"
        android:datePickerMode="spinner"
        android:calendarViewShown="false"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:padding="10dp"
        android:background="#F0FFE6"
        android:layout_weight="1"
        android:lines="8"/>

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:enabled="false"
        android:text="BUTTON" />

</LinearLayout>

 

android:lines="8"

 

    ✓  에디트텍스트를 8행으로 설정

 

android:enabled="false"


    ✓  초기에 버튼을 disable 되게 하고, java 코드에서 enable 되게 함

 


 

2.  Kotlin 코드 작성 및 수정

  • activity_main의 3개 위젯에 대응할 위젯 변수 3개를 선언
  • 파일 이름을 저장할 문자열 변수 1개를 선언. 파일 이름을 '연_월_일.txt'로 지정
  • 위젯 변수에 activity_main.xml의 위젯을 대입
  • 데이트피커를 설정
  • Calendar 클래스를 이용하여 현재 날짜의 년, 월, 일을 구한 후 데이트피커를 초기화 ➡️ 데이트피커의 날짜가 변경되면 변경된 날짜에 해당하는 일기 파일(연_월_일.txt)의 내용을 에디트텍스트로 보여줌
  • 현재 날짜의 파일(연_월_일.txt)을 읽어 일기의 내용을 반환하는 readDiary() 메서드 완성
class MainActivity : AppCompatActivity() {
    lateinit var datePicker: DatePicker
    lateinit var editText: EditText
    lateinit var btn: Button
    lateinit var fileName: String // 파일 이름 저장

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        title = "간단 일기장"

        datePicker = findViewById(R.id.datePicker)
        editText = findViewById(R.id.editText)
        btn = findViewById(R.id.btn)

        val calendar = Calendar.getInstance()
        val calendarYear = calendar.get(Calendar.YEAR)
        val calendarMonth = calendar.get(Calendar.MONTH)
        val calendarDay = calendar.get(Calendar.DAY_OF_MONTH)

        fileName = "${calendarYear}_${calendarMonth + 1}_${calendarDay}.txt"
        val str = readDiary(fileName) // 날짜에 해당하는 일기 파일을 읽기
        editText.setText(str) // 에디트텍스트에 일기 내용을 출력
        btn.isEnabled = true // 버튼 활성화


        datePicker.init(
            calendarYear,
            calendarMonth,
            calendarDay,
            DatePicker.OnDateChangedListener() { datePicker: DatePicker, year: Int, month: Int, day: Int ->
                fileName = "${year}_${month + 1}_${day}.txt"
                val string = readDiary(fileName) // 날짜에 해당하는 파일을 읽기
                editText.setText(string) // 에디트텍스트에 일기 내용을 출력
                Toast.makeText(applicationContext, fileName, Toast.LENGTH_SHORT).show()
                btn.isEnabled = true // 버튼 활성화
            })

        btn.setOnClickListener {
            val outputStream = openFileOutput(fileName, Context.MODE_PRIVATE)
            val string = editText.text.toString()
            outputStream.write(string.toByteArray())
            outputStream.close()
            Toast.makeText(applicationContext, "${fileName} 이 저장됨", Toast.LENGTH_SHORT).show()
        }
    }

    private fun readDiary(fileName: String): String? {
        var diaryStr: String? = null
        val inputStream: FileInputStream

        try {
            inputStream = openFileInput(fileName)
            val txt = ByteArray(inputStream.available())
            inputStream.read(txt)
            inputStream.close()
            diaryStr = txt.toString(Charsets.UTF_8).trim()
            btn.text = "수정하기"
        } catch (e: IOException) {
            editText.hint = "일기 없음"
            btn.text = "새로 저장"
        }
        return diaryStr;
    }

}

 

inputStream = openFileInput(fileName)


    ✓ 일기 파일을 열어 입력 파일 스트림에 저장. 파일이 없으면 예외가 발생해서 catch 구문이 실행

 


 

3.   프로젝트 실행 및 결과 확인

선택된 날짜에 쓴 일기가 있다면 일기 내용이 보이고 버튼이 <수정하기>로 바뀜
선택된 날짜에 일기가 없다면 에디트텍스트에 '일기 없음' 힌트가 보이고 버튼이 <새로 저장>으로 바뀜

 

 

 


4. 파일 확인

AVD가 켜진 상태에서 Divice File Explorer 실행
View  ▶️  Tool Windows  ▶️  Divice File Explorer

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]


1.  파일 처리의 응용

음악, 영상, 그림 파일 등은 여러 응용 프로그램에서 사용되는 경우가 많음.
  📍  예를 들어 MP3 파일을 안드로이드에 내장된 앱에서 들을 수도 있고 별도의 음악 플레이어로 들을 수도 있음
         ➡️  이런 경우 SD 카드에 저장하여 활용

안드로이드에서는 SD 카드에 저장된 데이터에 특별한 인증 절차 없이 접근 가능하며 제한된 공간의 내장 메모리보다 훨씬 큰 공간을 사용할 수 있으며 확장성도 뛰어남. 실제 안드로이드폰의 경우 주로 마이크로 SD 카드를 장착하여 사용. AVD에도 가상 SD 카드를 장착할수 있음.

 

AVD Manager를 실행 후 해당 AVD 이름의 오른쪽에 있는 Edit 아이콘을 클릭한후 Show Advanced Settings를 클릭하면 사용하는 AVD에 장착된 SD 카드를 확인할 수 있다. 만약 가상 SD 카드가 만들어져 있지 않으면 원하는 크기를 새로 입력.

 

 

Target SDK 버전 변경


    Android SDK 30 버전에서 SD 카드의 처리 방법이 대폭 변경되어 접근 거부가 발생할 수 있음
      ➡️  이럴 때는 Target SDK 버젼을 29로 변경

android {
    compileSdk 32

    defaultConfig {
        applicationId "kr.somin.sdkstream"
        minSdk 26
        targetSdk 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

 

1)  SD 카드에서 파일 읽기

💫  폴더 경로가 다른 것만 제외하면 SD 카드의 기본적인 경로는 내장 메모리에서 파일을 읽을 때와 별다른 차이가 없음
💫  먼저 Device File Explorer에서 /sdcard 폴더 또는 /storage/emulated/0 폴더에 적당한 텍스트 파일을 upload
💫  AVD에서 sd 카드의 경로는 절대적인 것이 아니며 SDK 버전에 따라 달라질수 있음

 

 

💫  AndroidManifest.xml 파일에 SD 카드를 사용할 수 있도록 퍼미션을 지정해야 함
       ➡️  AndroidManifest.xml에 퍼미션 및 application 관련 속성을 추가

  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <application
        android:requestLegacyExternalStorage="true"

 

SD 카드에서 파일 읽기
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnRead"
        android:text="SD 카드에서 파일 읽기" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editSD"
        android:lines="10" />

</LinearLayout>
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        val btnRead = findViewById<Button>(R.id.btnRead)
        val editSD = findViewById<EditText>(R.id.editSD)
        
        ActivityCompat.requestPermissions(this, 
            arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE),
            Context.MODE_PRIVATE)
        
        btnRead.setOnClickListener {
            try {
                val inputStream = FileInputStream("/storage/emulated/0/Download/test.txt")
                val txt = ByteArray(inputStream.available())
                inputStream.read(txt)
                editSD.setText(txt.toString(Charsets.UTF_8))
                inputStream.close()
            } catch (e: IOException) {
                println(e.printStackTrace())
                Toast.makeText(applicationContext, "에러", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

 

ActivityCompat.requestPermissions(this,
    arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE),
    Context.MODE_PRIVATE)


    앱에서 파일 엑세스 작업을 허용할지를 묻는 창이 나옴. <허용>을 클릭.

val inputStream = FileInputStream("/storage/emulated/0/Download/test.txt")


    파일 입력 스트림을 생성할 때 SD 카드의 절대 경로를 FileInputStream() 생성자에 지정

 


2)  SD 카드에 폴더 및 파일 생성

SD 카드의 파일을 좀 더 정밀하게 처리하려면 우선 Environment 클래스의 정적 메소드를 이용하여 SD 카드의 동작 여부와 관련 폴더 경로를 구해야 함

SD 카드에 폴더 및 파일 생성
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnMkDir"
        android:text="SD 카드에 디렉토리 생성" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnRmDir"
        android:text="SD 카드에 디렉토리 삭제" />

</LinearLayout>
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.layout_01)
        
        ActivityCompat.requestPermissions(this, 
            arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE),
            Context.MODE_PRIVATE)

        val btnMkdir = findViewById<Button>(R.id.btnMkDir)
        val btnRmdir = findViewById<Button>(R.id.btnRmDir)

        val strSDpath = Environment.getExternalStorageDirectory().absolutePath
        val myDir = File("${strSDpath}/MyDir")

        btnMkdir.setOnClickListener {
            myDir.mkdir()
        }

        btnRmdir.setOnClickListener {
            myDir.delete()
        }
    }
}

 

val strSDpath = Environment.getExternalStorageDirectory().absolutePath

 

    ✓  SD 카드의 절대 경로를 돌려주는데, 대개 /sdcard 폴더 또는 /storage/emulated/0

    ✓  getExternalStorageDirectory() 이외에도 시스템 폴더를 반환하는 getRootDirectory(),

         데이터 폴더를 반환하는 getDataDirectory()등이 있음

 

val myDir = File("${strSDpath}/MyDir")


    ✓  SD 카드의 경로 아래에 MyDir 폴더를 생성하기 위한 File형 변수를 설정

 

 

 


3)  특정 폴더의 하위 폴더 및 파일 목록

지정한 폴더의 하위 폴더 및 파일 목록에 접근하려면 File.listFiles() 메서드를 사용  ➡️  반환형은 File[] 형

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnFileList"
        android:text="시스템 폴더의 폴더/파일 목록" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editFileList" />

</LinearLayout>
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.layout_02)

        val btnFileList = findViewById<Button>(R.id.btnFileList)
        val editFileList = findViewById<EditText>(R.id.editFileList)
        btnFileList.setOnClickListener {
            val sysDir = Environment.getRootDirectory().absolutePath
            val sysFiles = File(sysDir).listFiles()

            var strFname: String
            for (i in sysFiles.indices) {
                if (sysFiles[i].isDirectory == true)
                    strFname = "<폴더> " + sysFiles[i].toString()
                else
                    strFname = "<파일> " + sysFiles[i].toString()

                editFileList.setText(editFileList.text.toString() + "\n" + strFname)
            }
        }
    }
}

 

val sysDir = Environment.getRootDirectory().absolutePath


    ✓  안드로이드 시스템 경로를 반환

 

val sysFiles = File(sysDir).listFiles()

 

    ✓  시스템 폴더의 폴더 및 파일 목록을 구해서 배열로 반환

 

if (sysFiles[i].isDirectory == true)


    ✓  현재 파일이 폴더인지 파일인지 확인

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]


1.  파일 처리의 기본

안드로이드에서 파일을 읽고 쓰는 방법에는 Java에서 제공하는 파일 관련 클래스를 사용하는 방법과 안드로이드에서 제공하는 파일 관련 클래스를 사용하는 방법이 있음. 아무 곳에서나 파일을 읽고 쓸 수는 없고 제한된 폴더나 SD 카드 등에서만 가능.


1)  내장 메모리 파일 처리

🐰  앱을 종료했다가 다음에 다시 실행할 때 사용하던 곳 부터 이어서 작업하고 싶은 경우,내장 메모리에 파일을 저장하고 읽어오는 방식을 활용
🐰  내장 메모리의 저장 위치는 /data/data/패키지명/files 폴더
      📍 일반적으로 앱마다 다른 패키지명을 사용하므로 앱별로 고유의 저장 공간이 있다고 생각하면 됨

🐰  파일을 읽기 위해 먼저 안드로이드 Context 클래스의 openFileInput() 메서드를 사용 ▶️ FileInputStream을 반환
🐰  파일을 쓰기 위해 openFileOutput() 메서드를 사용 ▶️ FileOutputStream을 반환
🐰  Java에서 제공하는 파일을 읽거나 쓰는 java.io.FileInputStream 클래스와 java.io.FileOuptputStream 클래스의 read(), write() 메서드를 사용하여 파일을 처리

 

내장 메모리에서 파일을 읽거나 쓰는 일반적인 절차
  • openFileInput() / openFileOutput() 으로 파일 열기 : FileInputStream / FileOutputStream 반환
  • read() / write()로 파일 읽기 / 쓰기
  • close()로 파일 닫기
xml 코드
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnWrite"
        android:text="내장 메모리에 파일 쓰기" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnRead"
        android:text="내장 메모리에 파일 읽기" />

</LinearLayout>

 

kotlin 코드
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        val btnWrite = findViewById<Button>(R.id.btnWrite)
        val btnRead = findViewById<Button>(R.id.btnRead)

        btnWrite.setOnClickListener {
            val outputStream = openFileOutput("file.txt", Context.MODE_PRIVATE)
            val string = "파일 처리 테스트"
            outputStream.write(string.toByteArray())
            outputStream.close()
            Toast.makeText(applicationContext, "file.txt가 생성됨.", Toast.LENGTH_SHORT).show()
        }

        btnRead.setOnClickListener {
            try {
                val inputStream = openFileInput("file.txt")
                val txt = ByteArray(30)
                inputStream.read(txt)
                val string = txt.toString(Charsets.UTF_8)
                Toast.makeText(applicationContext, string, Toast.LENGTH_SHORT).show()
            } catch (e : Exception) {
                Toast.makeText(applicationContext, "파일 없음", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

 

val outputStream = openFileOutput("file.txt", Context.MODE_PRIVATE)


    ✓  file.txt로 파일을 쓰기 모드로 열기. 파일의 경로는 /data/data/패키지명/files/file.txt가 됨
    ✓  파일 모드에는 쓰기(write)를 위해 MODE_PRIVATE (생성한 앱에서만 사용 가능)나 MODE_APPEND (파일이 존재하는 경우 내용 추가) 등을 사용

  • MODE_PRIVATE : 혼자만 사용하는 모드
  • MODE_APPEND : 파일이 존재할 경우 기존내용에 추가하는 모드
  • MODE_WORLD_READABLE : 다른 응용 프로그램과 파일 읽을 수 있음
  • MODE_WORLD_WRITABLE : 다른 응용 프로그램이 파일 기록할 수 있음
val txt = ByteArray(30)
inputStream.read(txt)
val string = txt.toString(Charsets.UTF_8)

 

    ✓  byte[] 형 변수 txt에 입력 파일에서 읽어온 데이터를 저장. txt를 문자열로 변환.

 


 

2) raw 폴더 파일 처리


🐰  프로젝트의 /res/raw 폴더에 필요한 파일을 저장하기 위해 사용하는 방법
    📍 기본적으로 /res 아래에 raw 폴더를 생성하고 프로젝트에서 사용할 파일을 넣어둠


🐰  Java 코드에서 openRawResource() 메서드를 사용하여 접근할 수 있으며 FileInputStream 클래스 대신 InputStream 클래스를 사용
🐰  /res/raw는 프로젝트에 포함된 폴더이므로 읽기 전용으로만 사용 가능하고 쓰기는 할 수 없음. 쓰기에는 내장 메모리나 SD 카드를 사용해야 함.

 

프로젝트의 res 폴더에 raw 폴더를 생성하고, 임의의 내용을 입력한 *.txt파일을 복사
텍스트 파일은 UTF-8 형식으로 저장

 

xml 코드
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnRead"
        android:text="/res/raw에서 파일 읽기" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editRaw"
        android:lines="10" />

</LinearLayout>

 

kotlin 코드
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.layout_01)

        val btnRead = findViewById<Button>(R.id.btnRead)
        val editRaw = findViewById<EditText>(R.id.editRaw)

        btnRead.setOnClickListener {
            val inputStream = resources.openRawResource(R.raw.test)
            val txt = ByteArray(inputStream.available())
            inputStream.read(txt)
            editRaw.setText(txt.toString(Charsets.UTF_8))
            inputStream.close()
        }
    }
}

 

val inputStream = resources.openRawResource(R.raw.test)

 

   ✓  resources는 현재 패키지의 리소스를 반환. openRawResource()- /res/raw 파일을 읽기용으로 열기.

 

val txt = ByteArray(inputStream.available())


   ✓  inputStream.available() : 입력 스트림에서 읽을 수 있는 바이트 수를 반환. 결국 txt 파일의 크기 만큼 지정.

 

 

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]


1.  날짜와 시간 관련 위젯

날짜와 시간과 관련된 위젯으로는 타임피커, 데이트피커, 캘린더뷰, 크로노미터, 아날로그시계, 디지털시계 등이 있음

 

1)  아날로그시계와 디지털시계

 

🤓  아날로그 시계 AnalogClock와 디지털시계 DigitalClock는 화면에 시간을 표시하는 위젯으로 시계를 표현하는 용도로 쓰임. 이 둘은 View 클래스에서 상속받기 때문에 background 속성들을 설정할 수 있음. 디지털 시계는 textColor 같은 속성도 설정할 수 있음.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <AnalogClock
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <DigitalClock
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center" />

</LinearLayout>

 


2)  크로노미터

 

크로노미터 Chronometer는 타이머 형식의 위젯이며 일반적으로 시간을 측정할 때 많이 사용
사용되는 메서드로는 start(), stop(), reset() 등이 있는데 이는 각각 크로노미터를 시작, 정지, 초기화.

 

android:format="시간 측정 : %s"
  ➡️ format 속성에서 타이머 앞의 문자열을 지정
<Chronometer
        android:id="@+id/chronometer1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:format="시간 측정 : %s"
        android:gravity="center"
        android:textSize="30dp" />

 


3) 타임피커, 데이트피커, 캘린더뷰


타임피커 TimePicker는 시간을, 데이트피커 DatePicker와 캘린더뷰 CalendarView는 날짜를 표시하고 조절하는 기능을

    ⚡️  캘린더뷰는 XML 속성이 여러가지인데, 그중에서 디폴트가 true인 showWeekNumber 속성은 현재 몇 주 차인지를 각주
의 맨 앞에 출력. 하지만 showWeekNumber 속성은 false로 하는 것이 더 깔끔하고 보기 좋음

롤리팝 API21 이후부터 타임피커와 데이트피커의 모양이 대폭 변경

 

  📍  이전 모양의 버전을 사용하려면
        타임피커는 android:timePickerMode="spinner" 속성을,
        데이트피커는 android:datePickerMode="spinner" 속성을 추가

    <TimePicker
        android:timePickerMode="spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <DatePicker
        android:datePickerMode="spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />


2.  기타 위젯

1)  자동완성텍스트뷰와 멀티자동완성텍스트뷰

 

자동완성텍스트뷰 AutoCompleteTextView와 멀티자동완성텍스트뷰 MultiAutoCompleteTextView는 텍스트뷰 보다는 에디트텍스트 속성이 더 강함.  사용자가 단어의 일부만 입력해도 자동 완성되는데, 자동완성텍스트뷰는 단어 1개가 자동 완성되고, 멀티자동완성텍스트뷰는 쉼표(,)로 구분하여 여러 개의 단어가 자동 완성.


  ✓  자동 완성 단어는 주로 Java 코드에서 배열로 설정하며 setAdapter() 메서드를 사용
  ✓  completionHint : XML 속성 줌 목록에 나타날 힌트

    completionThreshold : 자동 완성 시작되기 전에 사용자가입력해야 하는 최소 문자 수

   <AutoCompleteTextView
        android:id="@+id/autoCompleteTextView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:completionHint="선택하세요"
        android:completionThreshold="2"
        android:hint="자동완성텍스트뷰">
    </AutoCompleteTextView>

    <MultiAutoCompleteTextView
        android:id="@+id/multiAutoCompleteTextView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:completionHint="선택하세요"
        android:completionThreshold="2"
        android:hint="멀티자동완성텍스트뷰">
    </MultiAutoCompleteTextView>
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.layout_04)

        // 자동완성텍스트뷰의 Java코드
        val items = arrayOf("CSI-뉴욕", "CSI-라스베가스", "CSI-마이애미", "Friend", "Fringe", "Lost")
        val auto = findViewById<AutoCompleteTextView>(R.id.autoCompleteTextView1)
        val adapter = ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, items)
        auto.setAdapter(adapter)

        val multi = findViewById<MultiAutoCompleteTextView>(R.id.multiAutoCompleteTextView1)
        val tokenizer = MultiAutoCompleteTextView.CommaTokenizer()
        multi.setTokenizer(tokenizer)
        multi.setAdapter(adapter)

    }
}

 

val items = arrayOf("CSI-뉴욕", "CSI-라스베가스", "CSI-마이애미", "Friend", "Fringe", "Lost")

     > 자동 완성될 문자열을 배열로 정의

val adapter = ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, items)

    > ArrayAdapter는 뷰와 데이터를 연결

    > 자동완성텍스트뷰와 items를 연결하는 역할을 하여 자동완성텍스트뷰에 items 배열의 내용이 출력
    > 생성자의 두 번째 파라미터는 목록이 출력될 모양을 결정. simple_dropdown_item_1line 외에도 다양한 모양을 선택할 수 있음

val tokenizer = MultiAutoCompleteTextView.CommaTokenizer()
multi.setTokenizer(tokenizer)
multi.setAdapter(adapter)

    > 쉼표로 구분하기 위한 객체를 생성하고 멀티자동완성텍스트 뷰에 설정

 

 


 

2) 프로그레스바, 시크바, 레이팅바

 

프로그레스바, 시크바, 레이팅바는 진행 상태를 표시하는 기능

 

 프로그레스바 ProgressBar

 

  🚀  작업의 진행 상태를 바 Bar 또는 원 형태로 제공
  🚀  바 형태는 어느 정도 진행되었는지를 확인할 수 있지만, 원 형태는 현재 진행 중이라는 상태만 보여줌
  🚀  주로 사용되는 XML 속성에는 범위를 지정하는 max, 시작 지점을 지정하는 progress,
         두 번째 프로그레스바를 지정하는 secondaryProgress 등이 있음

 

시크바 SeekBar


  🚀  프로그레스바의 하위 클래스로, 프로그레스바와 대부분 비슷하며 사용자가 터치로 임의 조절이 가능
  🚀  음량을 조정하거나 동영상 재생 시 사용자가 재생 위치를 지정하는 용도로 사용할 수 있음

 

레이팅바 RatingBar


  🚀  진행 상태를 별 모양으로 표시. 프로그레스바의 하위 클래스이므로 사용법이 비슷하며 서적, 음악, 영화 등에 대한 선호도를 나타낼 때 주로 사용
  🚀  별의 개수를 정하는 numStars, 초깃값을 지정하는 rating, 한 번에 채워지는 개수를 정하는 stepSize 속성이 주로 사용

 

   <ProgressBar style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        android:max="100"
        android:progress="20"
        android:secondaryProgress="50" />

    <SeekBar
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        android:progress="20" />

    <RatingBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"    
        android:layout_margin="20dp"
        android:numStars="5"
        android:rating="1.5"
        android:stepSize="0.5" />

 

style="?android:attr/progressBarStyleHorizontal"
> 프로그레스바의 모양을 지정. 이 스타일을 생략하면 원 모양이 나타남

    

android:max="100"
android:progress="20"
 > 프로그레스바의 범위를 최대 100으로 설정하고, 초깃값을 20으로 설정.

   

android:secondaryProgress="50"
> 프로그레스바의 보조 프로세스 초깃값을 50으로 설정. 메인 프로그레스보다 흐리게 표시.

    

android:numStars="5"
> 별의 개수를 설정. 디폴트는 5개.

 

android:rating="1.5"
> 초깃값을 설정. 1.5개가 채워진 상태로 표현.

 

android:stepSize="0.5"
> 한 스탭의 크기를 지정. 이 경우에는 별이 5개이므로 총 열 번의 스탭으로 구성.

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]


1. 테이블 레이아웃

테이블레이아웃은 위젯을 표 형태로 배치할 때 주로 활용. 테이블레이아웃을 사용하여 행과 열의 수를 정하고 그 안에 위젯을 배치. <TableRow>가 행의 수, 열의 수는 <TableRow> 안에 포함된 위젯의 수로 결정

3행 4열의 테이블레이아웃

 


1) 테이블레이아웃의 속성

 

  • layout_span과 layout_column은 테이블레이아웃 안에 포함된 위젯에 설정하는 속성
    - layout_span은 열을 합쳐서 표시하라는 의미로, 예를 들어 layout_span="2"는 현재 셀부터 2개의 셀을 합쳐서 표시
    - layout_column은 지정된 열에 현재 위젯을 표시하라는 의미
  • stretchColumns은 <TableLayout> 자체에 설정하는 속성으로, 지정된 열의 너비를 늘이라는 의미
    - stretchColumns="*"는 각 셀을 모두 같은 크기로 확장하여 전체 화면을 꽉 차는 효과를 냄. 열번호는 0부터 시작.
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TableRow>
        <Button
            android:layout_width="60dp"
            android:text="1" />

        <Button
            android:layout_width="60dp"
            android:layout_span="2"
            android:text="2" />

        <Button
            android:layout_width="60dp"
            android:text="3" />
    </TableRow>

    <TableRow>
        <Button
            android:layout_width="60dp"
            android:layout_column="1"
            android:text="4" />

        <Button
            android:layout_width="60dp"
            android:text="5" />

        <Button
            android:layout_width="60dp"
            android:text="6" />
    </TableRow>

</TableLayout>


2.  그리드 레이아웃

테이블레이아웃과 마찬가지로 위젯을 표 형태로 배치할 때 사용하지만 좀 더 직관적
테이블레이아웃에서는 다소 어려웠던 행 확장도 간단하게 할 수 있어서 유연한 화면 구성에 적합.
  ✓  행과 열을 지정하는 방법 :  2행 3열을 지정하려면 layout_row 속성은 1로, layout_column 속성은 2로 설정

 

1)  그리드레이아웃의 속성

  • rowCount : 행의 수
  • columnCount : 열의 수
  • orientation : 그리드를 수평 방향으로 우선할 것인지, 수직 방향으로 우선할 것인지를 결정
     -  그리드레이아웃 안에 포함될 위젯에서 자주 사용되는 속성
  • layout_row : 자신이 위치할 행 번호(0번부터 시작)
  • layout_column : 자신이 위치할 열 번호(0번부터 시작)
  • layout_rowSpan : 행을 지정된 수만큼 확장
  • layout_columnSpan : 열을 지정된 수만큼 확장
  • layout_gravity : 주로 fill, fill_vertical, fill_horizontal 등으로 지정

    💡  layout_rowSpan이나 layout_columnSpan으로 셀 확장 시 위젯을 확장된 셀에 꽉 채우는 효과를 줌
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:rowCount="2"
    android:columnCount="4">

    <Button
        android:layout_gravity="fill_vertical"
        android:layout_column="0"
        android:layout_row="0"
        android:layout_rowSpan="2"
        android:text="1" />

    <Button
        android:layout_gravity="fill_horizontal"
        android:layout_column="1"
        android:layout_columnSpan="2"
        android:layout_row="0"
        android:text="2" />

    <Button
        android:layout_column="3"
        android:layout_row="0"
        android:text="3" />

    <Button
        android:layout_column="1"
        android:layout_row="1"
        android:text="4" />

    <Button
        android:layout_column="2"
        android:layout_row="1"
        android:text="5" />

    <Button
        android:layout_column="3"
        android:layout_row="1"
        android:text="6" />

</GridLayout>


3.  프레임 레이아웃

 

 

단순히 레이아웃 안의 위젯을 왼쪽 상단부터 겹쳐서 출력
프레임레이아웃 그 자체를 사용하기 보다는 탭 위젯 등과 혼용할 때 유용

 

👩🏻‍💻  프레임레이아웃의 속성

  •  foreground : 프레임레이아웃의 전경 이미지를 지정
  •  foregroundGravity : 전경 이미지의 위치를 지정. fill, right, left, top, bottom 등의 값을 사용

 

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:foreground="@drawable/dog2"
    android:foregroundGravity="center|fill_horizontal">

    <RatingBar
        android:id="@+id/ratingBar1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="80dp"/>

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher" />

    <CheckBox
        android:id="@+id/checkBox1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CheckBox" />

</FrameLayout>

 

 

 

[ 내용 참고 : IT 학원 강의 ]


1.  RelativeLayout

👩🏻‍🚀  렐러티브레이아웃은 상대 레이아웃이라고도 하며, 이름처럼 레이아웃 내부에 포함된 위젯을 상대적인 위치로 배치
👩🏻‍🚀  렐러티브레이아웃 안에 포함된 위젯은 렐러티브레이아웃의 어디쯤에 위치시킬 것인지 지정해야 함
👩🏻‍🚀  렐러티브레이아웃에 있는 위젯의 위치와 관련된 속성은 크게 두 부류로 나눌 수 있음
      1) 렐러티브레이아웃의 상하좌우에 배치하는 경우  2) 다른 위젯의 상대 위치에 배치하는 경우

 

상하좌우에 배치

  • layout_alignParentLeft
  • layout_alignParentRight
  • layout_alignParentTop
  • layout_alignParentBottom
  • layout_centerHorizontal
  • layout_centerVertical
  • layout_centerInParent

  ⚡️ 렐러티브레이아웃에 있는 위젯을 부모(렐러티브레이아웃)의 어느 부분에 위치시킬지를 결정. 각 속성의 값은 true 또는 false.
  ⚡️  예를 들어 우측하단에 위젯을 배치하려면 layout_alignParentBottom 과 layout_alignParentRight 속성에 true를 설정하면 됨

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:text="위쪽" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:text="좌측" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="중앙" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:text="우측" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="아래" />

</RelativeLayout>

 

 


 

다른 위젯의 상대 위치에 배치 

 

👾  렐러티브레이아웃 안에서 다른 위젯의 특정한 곳에 배치하는 방법. 다른 위젯과 관련된 속성.

👾  각 속성의 값에는 다른 위젯의 아이디를 지정하면 되는데 '@+id/기준 위젯의 아이디'와 같은 형식으로 사용

 

 

  • 상하좌우에는 layout_above, layout_below, layout_toLeftOf, layout_toRightOf
  • 상단, 중앙, 하단  layout_alignTop, layout_alignBaseline, layout_alignBottom
  • 좌측, 우측 기준에는 layout_alignLeft, layout_alignRight

 

        <Button
            android:id="@+id/baseButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="기준 위젯"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@+id/baseButton"
            android:text="above"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/baseButton"
            android:text="below"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toLeftOf="@+id/baseButton"
            android:text="toLeftOf"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@+id/baseButton"
            android:text="toRightOf"/>


 <Button
        android:id="@+id/baseButton"
        android:layout_width="wrap_content"
        android:layout_height="400dp"
        android:layout_centerInParent="true"
        android:text="기준 위젯"/>
    <Button
        android:layout_width="100dp"
        android:layout_height="70dp"
        android:layout_alignTop="@+id/baseButton"
        android:text="alignTop"/>
    <Button
        android:layout_width="100dp"
        android:layout_height="70dp"
        android:layout_alignBaseline="@+id/baseButton"
        android:text="alignBaseline"/>
    <Button
        android:layout_width="100dp"
        android:layout_height="70dp"
        android:layout_alignBottom="@+id/baseButton"
        android:text="alignBottom"/>

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]

+ Recent posts