[Android] 나머지 폭 차지하기

UI를 배치할때 나머지 공간을 차지하게 하고 싶은 경우가 있다. 예를들면 아래와 같이..

사진이라는 문자열이 들어간 TextView는 가로폭의 길이가 정해져 있지 않고 나머지 공간을 모두 차지한다. 코드는 다음과 같다.

<?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"
    android:layout_width="match_parent"
    android:layout_height="44dp"
    android:paddingHorizontal="10dp"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/ivIcon"
        android:src="@drawable/ic_layers_black"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:tint="#444444" />

    <TextView
        android:textSize="@dimen/normal_text_size"
        android:id="@+id/tvCaption"
        android:layout_marginLeft="10dp"
        android:layout_width="match_parent"
        android:layout_weight="1"
        android:layout_height="wrap_content" />

    <TextView
        android:textSize="@dimen/small_text_size"
        android:id="@+id/tvDate"
        android:text="2020/12/31 12:13:13"
        android:layout_width="100dp"
        android:textAlignment="center"
        android:layout_height="wrap_content" />

</LinearLayout>

사진에 해당하는 TextView의 layout_weight를 1로 지정하고 있다는게 핵심이다.

[Android] Activity 호출하기

Activity를 호출하는 코드를 정리합니다.

먼저 Activity를 하나 만듭니다. 이 녀석 이름이 DataCollectingListActivity라고 하겠습니다. 이 엑티비트를 호출하는 코드는 다음과 같습니다. 전달해줄 인자는 Intent에 전달해 줍니다.

val intent = Intent(this, DataCollectingListActivity::class.java)
intent.putExtra("featureId", fid);
startActivityForResult(intent, REQUEST_DATA_COLLECTION_LIST)

REQUEST_DATA_COLLECTION_LIST는 엑티비티를 띄우고 닫을때 해당 엑티비를 구분하기 위한 상수값인데요, 아래처럼 선언합니다.

private val REQUEST_DATA_COLLECTION_LIST = 100

다음은 DataCollectingListActivity에서 전달받은 인자를 처리하는 코드입니다.

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

    val fid = this.intent.getIntExtra("featureId", -1)
    Log.v("DIP2K", "받은 FID: $fid")

    ....
}

엑티비티를 닫는 코드입니다. 닫을때 자신을 호출한 엑티비티에 결과를 전달할 수 있습니다.

val intent = Intent()
intent.putExtra("data", "전달할 데이터")
setResult(RESULT_OK, intent)
finish()

setResult 함수를 통해 대화상자의 개념으로 사용자에게 확인(RESULT_OK)인지, 취소(RESULT_CANCEL)인지에 대한 의사전달이 가능합니다.

띄운 엑티비티가 결과값으로 전달한 내용을 읽는 코드입니다.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    if (resultCode == RESULT_OK) {
        when (requestCode) {
            REQUEST_DATA_COLLECTION_LIST -> {
                Log.v("DIP2K", data!!.getStringExtra("data")!!)
            }
        }
    }
}

[Android] ActionBar에 메뉴 넣기

ActionBar에 메뉴를 하나 넣어보자. 먼저 넣을 Activity의 onCreate를 추가한다.

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

    supportActionBar?.setDisplayHomeAsUpEnabled(true)
}

메뉴에 대한 리소스를 추가한다. (위치는 res/menu)

<?xml version="1.0" encoding="utf-8"?>
<menu 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">
    <item
        app:showAsAction="always|withText"
        android:icon="@drawable/ic_add"
        android:id="@+id/menu_add"
        android:title="추가" />
</menu>

물론 위에 메뉴에 대한 이미지 리소스를 추가해야 한다. (위치는 res/drawable)

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
  <path
      android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"
      android:fillColor="#ffffff"/>
</vector>

메뉴 리소스를 엑티비티에 반영한다.

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    getMenuInflater().inflate(R.menu.data_collecting_list_menu, menu) ;
    return true
}

메뉴 터치시 실행되는 코드를 작성한다.

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    when (item.getItemId()) {
        R.id.menu_add -> {
            // At Here!
        }
    }

    return super.onOptionsItemSelected(item)
}

그러면 다음과 같이 메뉴가 추가된다.

[Android] Activity 상태 변경에 대한 Event 호출 순서

Activity를 사용자가 조작하면서 그 상태가 변경되는데, 그 변경에서 발생하는 이벤트에 대한 정리입니다. 다양한 상태 변화가 존재하지만 이 글에서는 2가지의 경우로 나눠 기술합니다.

첫번째는 activity를 실행하고 바로 뒤로 가기 버튼을 눌러 종료할때에 대한 이벤트 호출 순서입니다.

두번째는 activity를 실행하고 단말기를 회전하여 activity를 회전시켰을때에 대한 이벤트 호출 순서입니다.

Kotlin 고유의 표준함수 (let, also, apply, with, run, …)

다시 고등학생이 된 기분입니다. let, also, with 등 매우 기초적인 영어 단어를 외우는 기분인데요. 원래는 표준함수로 불리지만 Kotlin에서 고유하게 제공하므로 고유의 표준함수라는 제목을 붙였습니다. 이 함수들을 사용하여 코드를 좀더 최적화 시켜줄 수 있으며 불필요한 코딩량을 줄일 수 있습니다.

let

예제 코드는 다음과 같습니다.

data class WHAT(val name: String, val age: Int)

fun main() {
    println("Case 1")

    var w: WHAT? = null

    val v = w?.let {
        println(it)
    } ?: "Known"

    println(v)

    println("Case 2")

    var W: WHAT? = WHAT("Dip2K", 43)

    val V = W?.let {
        println(it)
    } ?: "Known"

    println(V)
}

결과는 다음과 같다.

Case 1
Known
Case 2
WHAT(name=Dip2K, age=43)
kotlin.Unit

let은 받은 객체(w)를 람다 함수 내부에서 it으로 받아 사용할 수 있습니다. 반환값은 람다 함수의 가장 마지막 코드의 반환값이 됩니다.

also

예제 코드는 다음과 같습니다.

data class WHAT(val name: String, val age: Int)

fun main() {
    println("Case 1")

    var w: WHAT? = null

    val v = w?.also {
        println(it)
    } ?: "Known"

    println(v)

    println("Case 2")

    var W: WHAT? = WHAT("Dip2K", 43)

    val V = W?.also {
        println(it)
    } ?: "Known"

    println(V)
}

결과는 다음과 같다.

Case 1
Known
Case 2
WHAT(name=Dip2K, age=43)
WHAT(name=Dip2K, age=43)

also 함수는 받은 객체를 it으로 받아 사용할 수 있으며, 받은 객체를 그대로 그대로 반환합니다.

apply

예제 코드는 다음과 같습니다.

data class WHAT(var name: String, var age: Int)

fun main() {
    val w = WHAT("Jackass", 16)

    val r = w.apply {
        name = "Dip2K"
        age = 43
    }

    println(w)
    println(r)
}

결과는 다음과 같다.

WHAT(name=Dip2K, age=43)
WHAT(name=Dip2K, age=43)

apply는 받은 객체를 람다 함수 내부에서 it이 아닌 this로 처리합니다. 받은 객체를 그대로 반환합니다.

with

예제 코드는 다음과 같습니다.

package with

data class WHAT(var name: String, var age: Int)

fun main() {
    val w = WHAT("Jackass", 16)

    val r = w?.let {
        with(it) {
            name = "Dip2K"
            age = 43
            "Good day!"
        }
    }

    println(w)
    println(r)
}

결과는 다음과 같다.

WHAT(name=Dip2K, age=43)
Good day!

with 함수는 인자로 받은 객체를 람다 함수 안에서 this로 사용할 수 있습니다. 반환값은 람다 함수를 구성하는 가장 마지막 코드입니다.

run

예제 코드는 다음과 같습니다.

data class WHAT(var name: String, var age: Int)

fun main() {
    val v = run {
        val w = WHAT("Jackass", 16)
        w
    }

    println(v)

    val vv = v.run {
        name = "Dip2K"
        age = 43

        "Good day !"
    }

    println(v)
    println(vv)
}

결과는 다음과 같다.

WHAT(name=Jackass, age=16)
WHAT(name=Dip2K, age=43)
Good day !

run은 객체를 전달받지 않고 독립적으로 실행하는 경우와 개체를 전달받아 처리하는 경우로 구분됩니다. 객체를 전달받아 처리하는 경우 전달받은 객체를 람다 함수 내부에서 this로 받아 사용할 수 있습니다. 반환값은 람다 함수의 마지막 코드입니다.

use

예제 코드는 다음과 같습니다.

import java.io.File

fun main() {
    val f = File("d:/a.txt")
    f.bufferedWriter().use {
        it.appendln("Hello, GIS Developer.")
        it.append("Good Day !")
    }
}

use 함수는 전달 받은 객체를 it으로 람다 함수 내부에서 사용하며, 람다 함수 종료시 close 함수를 자동으로 호출해 줍니다.

takeIf, takeUnless

예제 코드는 다음과 같습니다.

data class WHAT(var name: String, var age: Int)

fun main() {
    val w = WHAT("Jackass", 16)

    val v = w?.takeIf { it.age < 20 }
    //val v = w?.takeUnless { it.age >= 20 } <- 위의 코드와 동일함
    println(v)

    var r = v?.apply {
        name = "Dip2K"
    }
    println(r)
}

결과는 다음과 같다.

WHAT(name=Jackass, age=16)
WHAT(name=Dip2K, age=16)

takeIf 함수는 받은 객체를 람다 함수 내부에서 it으로 처리하며, 반환값이 true일때 받은 객체를 반환하고, false일때 null을 반환합니다.

measureTimeMillis

예제 코드는 다음과 같습니다.

import kotlin.system.measureTimeMillis

fun main() {
    val time = measureTimeMillis {
        Thread.sleep(10000)
    }

    println(time)
}

결과는 다음과 같다.

10000

measureTimeMillis은 어떤 코드를 실행하는데 소요되는 시간을 측정하는데 사용됩니다.