KoreaTech

[모바일 프로그래밍] 비만도 계산기 앱 만들기

졸려질려 2020. 4. 2. 00:40
반응형

Anko 라이브러리

Kotlin 개발자가 개발한 Kotlin 전용 Android 개발 라이브러리이다. 그러나 현재는 더 이상 사용되지 않고 있다. this page로 들어가보면 Anko 라이브러리가 더 이상 쓰이지 않는 이유를 볼 수 있다. 그리고 Anko를 대신할 라이브러리들을 소개해준다.

지금은 Anko 라이브러리를 써보자...

dependency에 Anko 추가한다.

implementation "org.jetbrains.anko:anko:$anko_version"

현재 anko_version이라는 변수값이 존재한다. 이 변수값은 Project 레벨의 Module에서 값을 추가해준다.

그리고 Sync Now 버튼을 클릭한다.


Main Activity Layout

1. 키(height)를 입력할 EditText 추가

  • layout_width : match_parent 또는 0dp로 설정을 한다.
  • inputType : 키를 입력할 곳이기 때문에 "number"로 설정한다.
  • hint : 하드코딩을 해도 좋으나, string.xml에 값을 넣어서 불러온다.

2. 몸무게(weight)를 입력할 EditText 추가


hint 값을 짧게 바꿨다 ㅎㅎ

  • layout_constraintTop_toBottomOf : 키를 입력하는 EditText의 아래에 위치하도록 설정한다.

3. 계산을 실행할 Button 추가

이제 Button을 클릭했을 때 결과값을 새로운 Activity에서 보여주려 한다.


Result Ativity

1. Result Activity를 추가한다.

  • Activity Name : ResultActivity
  • Layout Name : activity_result
  • Activity를 바로 추가한 것이기 때문에 manifests에 자동으로 추가된다.

2. Result Activity Layout에 결과값을 보여줄 TextView 추가

  • textSize : 기기마다 다른 글자 크기를 대응하기 위해 sp 라는 단위를 사용한다.
  • layout_constraintVertical_bias : 값을 설정하여 살짝 위쪽에 배치한다.

3. Vector Image 추가

원하는 클립아트를 선택하고 바로 추가한다.

이와 같은 방법으로 매우 불만족, 보통 표정의 클립아트를 2개 더 추가한다.

4. ImageView 추가

  • tint : 이미지 위에 색상을 오버레이 해주는 기능이다. 개인적으로 민트 불호

5. Vector Image를 사용하기 위해 build.gradle 수정

Vector Image는 Vector Drawable로 따로 분류되기 때문에 build.gradle에 설정을 추가해야한다.

vectorDrawables.useSupportLibrary = true

그러면 activity_result.xml에서 오류가 발생한다.

오류 메시지에 따라 android:src="~~" 에서 app:srcCompat="~~~"으로 바꿔주면 해결된다.


Main Activity에서 Button Click Listener 구현

1. Button의 Click Listener 추가

package com.example.bmicalculator

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        result_button.setOnClickListener { 
            // 버튼을 클릭했을 때 실행될 동작
        }
    }
}

import kotlinx.android.synthetic.main.activity_main.*

  • Android Studio의 자동 완성 기능을 통해 위 import 문을 추가해준다.
  • Kotlin의 Android 코드는 위 import 문을 통해 자동으로 뷰바인딩이 되기 때문에 더 간결하다.

2. Intent를 통해 ResultActivity로 이동

package com.example.bmicalculator

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import android.content.Intent

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        result_button.setOnClickListener {
            // 버튼을 클릭했을 때 실행될 동작
            val intent = Intent(this, ResultActivity::class.java)
            startActivity(intent)
        }
    }
}

위 코드를 Anko Library가 지원하는 코드로 바꿀 수 있다.

package com.example.bmicalculator

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import android.content.Intent
import org.jetbrains.anko.startActivity

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        result_button.setOnClickListener {
            // 버튼을 클릭했을 때 실행될 동작
            startActivity<ResultActivity>();
        }
    }
}

3. manifests를 수정하여 ResultActivity의 Toolbar에 Back Button 추가

android:parentActivityName=".MainActivity"

속성을 추가하여 Toolbar에 Back Button을 추가할 수 있다.

4. Intent를 통해 ResultActivity로 결과값 보내기

MainActivity

package com.example.bmicalculator

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import android.content.Intent
import org.jetbrains.anko.startActivity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        result_button.setOnClickListener {
            // 버튼을 클릭했을 때 실행될 동작
            val intent = Intent(this, ResultActivity::class.java)
            intent.putExtra("height", height_edittext.text.toString())
            intent.putExtra("weight", weight_edittext.text.toString())
            startActivity(intent)
        }
    }
}

ResultActivity

package com.example.bmicalculator

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast

class ResultActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_result)
        val height = intent.getStringExtra("height").toInt()
        val weight = intent.getStringExtra("weight").toInt()

        Toast.makeText(this, "$height $weight", Toast.LENGTH_LONG).show()
    }
}

이번엔 Anko Library를 사용해본다.

MainActivity

package com.example.bmicalculator

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import android.content.Intent
import org.jetbrains.anko.startActivity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        result_button.setOnClickListener {
            // 버튼을 클릭했을 때 실행될 동작
            startActivity<ResultActivity>(
                "height" to height_edittext.text.toString(),
                "weight" to weight_edittext.text.toString())
        }
    }
}

ResultActivity

package com.example.bmicalculator

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import org.jetbrains.anko.toast

class ResultActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_result)
        val height = intent.getStringExtra("height").toInt()
        val weight = intent.getStringExtra("weight").toInt()

        toast("$height $weight")
    }
}

Logic 구현

BMI = 몸무게(weight)를 키(height)의 제곱으로 나눈 값

ResultActivity

package com.example.bmicalculator

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_result.*
import org.jetbrains.anko.toast

class ResultActivity : AppCompatActivity() {

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

        // Get Data from MainActivity
        val height = intent.getStringExtra("height").toInt()
        val weight = intent.getStringExtra("weight").toInt()

        // Calculate BMI
        val bmi = weight / Math.pow((height/100.0), 2.0)

        // Show Result with TextView
        when {
            bmi >= 35 -> result_textview.text = "고도 비만"
            bmi >= 30 -> result_textview.text = "2단계 비만"
            bmi >= 25 -> result_textview.text = "1단계 비만"
            bmi >= 23 -> result_textview.text = "과체중"
            bmi >= 18.5 -> result_textview.text = "정상"

            else -> result_textview.text = "저체중"
        }

        // Show Image
        when {
            bmi >= 23 -> result_imageview.setImageResource(R.drawable.ic_sentiment_dissatisfied_black_24dp)
            bmi >= 18.5 -> result_imageview.setImageResource(R.drawable.ic_sentiment_satisfied_black_24dp)
            else -> result_imageview.setImageResource(R.drawable.ic_sentiment_neutral_black_24dp)
        }

        toast("키: $height, 몸무게: $weight, BMI: $bmi")
    }
}

결과


SharedPreference 사용

이번에는 ResultActivity에서 MainActivity로 돌아와도 값이 유지되도록 SharedPreference를 사용해서 값을 저장한다.

package com.example.bmicalculator

import android.os.Bundle
import android.preference.PreferenceManager
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import org.jetbrains.anko.startActivity

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

        // Check Saved Data
        loadData()

        result_button.setOnClickListener {
            // Save Data
            saveData(height_edittext.text.toString().toInt(), weight_edittext.text.toString().toInt())

            // Go To ResultActivity
            startActivity<ResultActivity>(
                "height" to height_edittext.text.toString(),
                "weight" to weight_edittext.text.toString())
        }
    }

    private fun loadData() {
        val pref = PreferenceManager.getDefaultSharedPreferences(this)
        val height = pref.getInt("KEY_HEIGHT", 0)
        val weight = pref.getInt("KEY_WEIGHT", 0)

        if(height != 0 && weight != 0) {
            height_edittext.setText(height.toString())
            weight_edittext.setText(weight.toString())
        }
    }

    private fun saveData(height: Int, weight: Int) {
        val pref = PreferenceManager.getDefaultSharedPreferences(this)
        val editor = pref.edit()
        editor.putInt("KEY_HEIGHT", height)
            .putInt("KEY_WEIGHT", weight)
            .apply()
    }
}

SharedPreference는 Android 내부 저장소에 값을 저장해주는 기능으로, KEY&VALUE 쌍으로 데이터를 저장하고 불러온다.

코드를 입력해보면 PreferenceManager가 deprecated 된 것을 볼 수 있다.


Deprecated

위에서 PreferenceManager가 deprecated 상태인것을 확인하였다. 앱은 정상적으로 실행되지만, deprecated 된 PreferenceManager를 대신하여 어떤 것을 써야할까?

간단하게 androidx 패키지로 가서 사용하면 된다.

androidx.preference.PreferenceManager

1. implementaion 추가

Gradle에서 implementation을 추가해준다.

implementation "androidx.preference:preference-ktx:1.1.0"

2. 새로운 PreferenceManager 추가

위에서 구현했던 코드에서 import 부분을 수정하면 끝이다.

import android.preference.PreferenceManager // Before

import androidx.preference.PreferenceManager // After

이렇게 간단하게 BMI 계산기를 만들 수 있었다. 이전까지는 Java만 사용했었는데, Kotlin을 사용하니 신세계가 열린 느낌이다. Kotlin으로 얼른 완전히 환승해야겠다. ㅋㅋ


More Anko

Anko를 통해 코드만으로 UI를 만들 수 있다.(UI와 Logic을 하나로 합칠 수 있다.)

위 코드를 한 번 사용해보자.

package com.example.bmicalculator

import android.os.Bundle
import org.jetbrains.anko.*
import org.jetbrains.anko.sdk25.coroutines.onClick

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

        verticalLayout {
            val name = editText()
            button("Say Hello") {
                onClick { toast("Hello, ${name.text}!") }
            }
        }
    }
}

결과에서 볼 수 있듯이 XML 파일을 수정하지 않고도 verticalLayout을 생성하고, editText와 button을 차례로 만들 수 있다. 다만, 위 코드를 쓰기 전까지 만들었던 코드와 같이 사용하면 이전에 사용한 코드에서 NullException이 발생한다. 따라서, Anko Layout 코드를 사용할 때는 그것만 사용하는 것이 좋을 것 같다.

반응형