Android 공부 (1)

백상휘·2025년 8월 31일
0

Android

목록 보기
1/3

AndroidManifest.xml

  • 핵심 구성요소 정의
  • activity 와 그 하위 intent 사용
  • activity 는 화면, intent 중 name 이 android.intent.action.MAIN 이 들어있으면 앱의 주 진입점 의미. android.intent.category.LAUNCHER 는 런처에 표시된다는 의미.
    • 특정 기능들은 권한까지 정의해야 하는데 Normal(네트워크, 블루투스 등 권한) / Signature(동일 인증서 서명 앱들인지 구분) / Dangerous(개인 정보 접근)

웹뷰 표시해보기

  • MainActivity 의 onCreate 에서 시작한다. setContentView 에 webView 를 추가한다.

  • 오류난다. 인터넷 권한 허용해야 한다.

  • AndroidManifest.xml 열어 아래 코드 추가한다. 태그 위에 추가한다.

Gradle

  • 빌드 도구. 라이브러리 간 의존성 관리. 그루비라는 자바 가상 머신에서 사용하는 언어를 사용.
  • 빌드 및 구성 정보가 저장되는 파일은 build.gradle.kts
    • 최상위 하나(프로젝트 수준), 앱에 별도로 하나(앱 수준) 총 2개 만들어진다.
  • 프로젝트 수준의 build.gradle.kts 는 몇 줄 안된다. 각각의 의미는 다음과 같다.
plugins { // Gradle 은 플러그인 시스템을 기반으로 동작한다.
    alias(libs.plugins.android.application) apply false // 앱 생성을 지원한다. apply false 는 플러그인을 하위 프로젝트 및 모듈에만 적용하고 프로젝트 루트 수준엔 적용하지 않는다는 의미.
    alias(libs.plugins.kotlin.android) apply false // 프로젝트에서 코틀린 지원 제공
}
  • 앱 수준의 build.gradle.kts 는 좀 길다. 각각의 의미는 다음과 같다.
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
}

android {
    namespace = "com.example.myapplication" // 프로젝트 패키지 명. 빌드 및 리소스 식별자 생성에 사용
    compileSdk = 36 // 앱 컴파일 API 레벨. 하위호환 제공

    defaultConfig {
        applicationId = "com.example.myapplication" // 구글플레이에서의 앱 아이디
        minSdk = 24 // 앱이 지원하는 최소 API 레벨. 버전 낮은 기기에서는 이 값에 의해 구글플레이에 노출되지 않을 수 있음.
        targetSdk = 36 // 앱의 타겟 API 레벨. 가장 최적화된 버전 표시
        versionCode = 1 // 앱 업데이트 시 하나 업데이트 해야 함.
        versionName = "1.0" // 버전 이름. X.Y.Z 로 표기

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" // UI 테스트에 활용할 실행기
    }

    buildTypes { // 릴리즈 빌드 관련
        release {
            isMinifyEnabled = false // true 로 하면 사용 안하는 코드 제거 후 앱 난독화하여 앱 크기 줄임
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions { // 자바, 바이트코드 언어 수준 명시
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    kotlinOptions { // kotlin gradle 플러그인에서 사용할 jvm 라이브러리를 나타낸다.
        jvmTarget = "11"
    }
}

dependencies { // SDK 내 앱이 사용할 라이브러리 지정. 버전 표기는 groupId, artifactId, versionId 를 : 로 구분

    implementation(libs.androidx.core.ktx) // 젯팩컴포즈
    implementation(libs.androidx.appcompat) // 하위호환 + 젯팩컴포즈
    implementation(libs.material) // Material 디자인 구성요소
    implementation(libs.androidx.activity) // 액티비티
    implementation(libs.androidx.constraintlayout) // ConstraintLayout ViewGroup
    testImplementation(libs.junit) // Unit-Test 라이브러리
    androidTestImplementation(libs.androidx.junit) // UI-Test 라이브러리
    androidTestImplementation(libs.androidx.espresso.core) // 테스트 관련 에스프레소 라이브러리
}

Setting

  • 모듈 추가할 때 쓴다. 아래가 처음 생성되는 파일 내용인데 맨 밑에 app 만 추가되어 있다.
pluginManagement {
    repositories {
        google {
            content {
                includeGroupByRegex("com\\.android.*")
                includeGroupByRegex("com\\.google.*")
                includeGroupByRegex("androidx.*")
            }
        }
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "My Application"
include(":app")

Material Design - Theme

  • 새 프로젝트를 생성하고 build.gradle.kts 파일을 찾는다. libs.materal 이 dependencies 에 정의되어 있는지 확인한다.
  • themes.xml 파일을 연다. (app/src/main/res/values, app/src/main/res/values/night) 다음과 같이 수정한다.
<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Base.Theme.MyApplication" parent="Theme.Material3.DayNight.NoActionBar">
        <!-- Customize your light theme here. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
    </style>

    <style name="Theme.MyApplication" parent="Base.Theme.MyApplication" />
</resources>

AppCompatActivity 는 안드로이드 액티비티 클래스로 액티비티 Life-Cycle 을 관리(콜백 함수 실행)한다. 처음 프로젝트 생성 시 만들어지는 아래와 비슷하며, 각각의 의미는 주석으로 달아놓았다.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) { // 이전에 저장한 상태를 Bundle 매개변수에 저장해서 전달해준다.
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main) // 액티비티에 표시하려는 레이아웃을 로드한다.
    }
}

최초 생성되는 파일들을 더 살펴보자.

  • ExampleInstrumentedTest : UI-Test example
  • ExampleUnitTest : Unit-Test example
  • ic_launcer_background.xml / ic_launcher_foreground.xml : Launcher Icon as vector
  • activity_main.xml : MainActivity Layout file. Starts header -> ViewGroup -> Views..

ConstraintsLayout 은 제약조건으로 뷰를 그리는데 상당히 유용하다. XML Namespace 3 개가 나오니 살펴보자.

  • xmlns:android : Android namespace. SDK 내 모든 속성과 값에 사용. (거의 필수적으로 들어가는 듯)
  • xmlns:app : Library namespace. SDK 에 포함되어 있지는 않고 라이브러리로서 추가 됨.
  • xmlns:tools : Metadata namespace. tools:context 를 통해 레이아웃이 사용되는 코드 위치를 나타내고, 미리보기 텍스트를 정의하는 데도 사용하는 등 메타데이터를 추가하는데 이용된다.

webp 파일은 각기 다른 픽셀 밀도를 가진 기기에 이미지를 대응하기 위한 파일이다. 구글에 의해 만들어 졌으며, PNG 에 비해 압축률이 크다. 위의 ic_launcher_background.xml 처럼 벡터 형식 이전에는 webp 파일을 사용했다.

픽셀 밀도는 다음과 같다.

  • nodpi: dpi 독립적
  • ldpi : 120dpi
  • mdpi : 160dpi. 여기가 기준 값
  • hdpi : 240dpi
  • xhdpi : 320dpi
  • xxhdpi : 480dpi
  • xxxhdpi : 640dpi
  • tvdpi : 약 213dpi

밑으로 갈수록 (tvdpi 빼고) 픽셀의 수가 높고, 더 큰 화면에 대응한다. 픽셀6 에뮬의 픽셀 밀도가 411 dpi 인데 이는 xxhdpi(480dpi) 를 사용한다.

픽셀 밀도를 대체하는 법은 Bitmap-Drawable 이라는 것도 있다. 스케일 비율 3:4:6:8:12:16 을 따라야 한다.

  • mdpi : 48x48
  • hdpi : 72x72
  • xhdpi : 96x96
  • xxhdpi : 144x144
  • xxxhdpi : 192x192

Tip : 런쳐 아이콘은 일반 이미지보다 약간 크게 생성한다. 일부 런처는 이미지를 확대해 사용할 수 있기 때문에 흐려지는 경우가 있다. 이를 방지하자.

앱에 사용되는 리소스는 아래와 같다.

  • colors.xml
  • strings.xml
  • themes.xml : AndroidManifest.xml 파일에 액티비티, 최상위 테마를 지정할 수 있음.
  • styles.xml : View 요소에서 직접 접근할 수 있는 스타일을 지정한다. xml 형태이니 각 요소에 대해 item 태그로 추가하면 된다.
<style name="screen_layout_margin">
    <item name="android:layout_margin">12dp</item>
</style>

Text 를 입력하는 데엔 TextView, EditText 등을 사용할 수 있다. 디자인 적용을 더 하고 싶으면 TextInputLayout, TextInputEditText 를 사용하자. Button 말고 MaterialButton 도 좋은 선택이다.

TextView, EditTextView 유효성 검사는 중요하다. 아래 속성들은 미리 입력을 제한하는 속성들이다.

  • android:digits="0123456789." : 허용할 개별 문자 정의
  • android:maxLength="15" : 최대 문자 수 정의
  • android:inputType : "textPassword" 는 비밀번호, "Phone" 은 전화번호

예제 소스코드 작성

주요 파일만 작성함.

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

        findViewById<Button>(R.id.calculateButton)?.setOnClickListener {
            fun getText(textView: TextView): String {
                return textView.text.toString().trim()
            }

            val tRed = findViewById<TextView>(R.id.redText)
            val tGreen = findViewById<TextView>(R.id.greenText)
            val tBlue = findViewById<TextView>(R.id.blueText)

            val colorHex = "#${getText(tRed)}${getText(tGreen)}${getText(tBlue)}"

            val color = colorHex.toColorInt()

            findViewById<Button>(R.id.calculateButton)?.setBackgroundColor(color)
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    style="@style/screen_layout"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/channel_red"
        android:id="@+id/redTitle"
        style="@style/text_view_margin"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/redChannel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/redTitle"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" >

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/redText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/hint_red"
            android:maxLength="2"
            android:digits="0123456789ABCDEF" />
    </com.google.android.material.textfield.TextInputLayout>

    <TextView
        android:id="@+id/greenTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/channel_green"
        style="@style/text_view_margin"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/redChannel" />

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/greenChannel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/greenTitle"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/greenText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/hint_green"
            android:maxLength="2"
            android:digits="0123456789ABCDEF" />
    </com.google.android.material.textfield.TextInputLayout>

    <TextView
        android:id="@+id/blueTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/channel_blue"
        style="@style/text_view_margin"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/greenChannel" />

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/blueChannel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/blueTitle"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/blueText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/hint_blue"
            android:maxLength="2"
            android:digits="0123456789ABCDEF" />
    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.button.MaterialButton
        android:id="@+id/calculateButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/button_title_calculate"
        style="@style/button_margin"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/blueChannel"/>
</androidx.constraintlayout.widget.ConstraintLayout>
profile
plug-compatible programming unit

0개의 댓글