앱 프로젝트 - 01 (BMI 계산기) - R이란?, sp란?, dp란? Log찍기, TextView, EditText, LinearLayout, Intent(값 실어 보내고 받기), Button(리스너설정)

하이루·2021년 12월 23일
0
post-thumbnail

소개

키와 체중을 이용해서 비만도를 측정

레이아웃 소개


코드 소개

MainActivity.kt

package fastcampus.aop.part1.aop_part2_chapter01

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.Toast

class MainActivity : AppCompatActivity() {

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



        val heightEditText: EditText = findViewById(R.id.heightEditText)
        val weightEditText = findViewById<EditText>(R.id.weightEditText)
        val resultButton = findViewById<Button>(R.id.resultButton)

        resultButton.setOnClickListener {
            Log.d("MainActivity","ResultButton 이 클릭되었습니다.")


            if(heightEditText.text.isEmpty() || weightEditText.text.isEmpty()) {
                Toast.makeText(this, "빈 값이 있습니다.", Toast.LENGTH_SHORT).show()
                
                return@setOnClickListener
		// OnCreate함수와 setOnClickListener의 람다함수가 겹쳐있기 때문에 
                //어느 함수를 return할지 명시해줘야함
            } 
            
            //if에서 빈 값일 경우 return하도록 예외처리를 했기 떄문에 이 아래로는 절대 빈 값이 올 수 없음
            
            
            val height: Int = heightEditText.text.toString().toInt()
            val weight: Int = weightEditText.text.toString().toInt()
//해당 부분의 값이 비어있을 경우 앱이 죽어버리기 떄문에 위에서 if문을 사용해서 비어있는지 확인할 것임

            val intent = Intent(this, ResultActivity::class.java)

            intent.putExtra("height",height)
            intent.putExtra("weight",weight)
//Intent로 액티비티를 이동할 때, height와 weight 값을 같이 전달해주어야 함

            startActivity(intent)
            

            Log.d("MainActivity","height = $height, weight = $weight")
// "$변수명"을 하면 문자열 안에서도 해당 변수의 값을 바로 불러올 수 있다.


        }

    }
}

위 코드에서 주목할 부분들

시작 class의 형태 (Main클래스)

 class MainActivity : AppCompatActivity() {

--> MainActivity가 AppCompatActivity()를 상속 받고 있음


해당 파일의 ContentView 설정

setContentView(R.layout.activity_main)

--> R.layout.activity_main 이라는 파일을 가져와서 이 파일의 ContentView로 삼겠다는 뜻

R파일이란 무엇인가?

R파일의 의미?

--> 컴퓨터는 주소값을 가져와서 파일을 사용하지만, 우리가 주소값을 다 외우고 있을 수는 없음
그래서 R이라는 파일에 그런 주소값들이 저장되어 있어서 주소값을 가져오는 것

예시 )

위의 activity_main을 클릭해보면 툴팁으로 주소값이 나옴 --> 나의 경우 1300086임
즉, activity_main의 주소값이 1300086이며, 이 값을 일일이 외울 수 없기 때문에
R파일에 기록해 놓은 뒤, 참조해오는 것 --> ( 해당위치에 1300086을 입력해도 같은 효과를 볼 수 있음 )

이후에 나오는 컴포넌트들 역시 R파일에서 해당 컴포넌트의 주소값을 받아서 사용하는 것이다.

위의 동일하게 R에서 주소값을 받아오는모습 --> 1000328

--> 컴포넌트에 id를 설정하는 것 또한 R.id.컴포넌트이름으로 불러옴


findViewById를 이용한 참조

  val heightEditText: EditText = findViewById(R.id.heightEditText)
  val weightEditText = findViewById<EditText>(R.id.weightEditText)

--> findViewById를 사용해서 컴포넌트를 가져올 때, 이것이 어떤 타입인지 알 수 없음
따라서 우리가 명시해줘야함

  1. heightEditText처럼 변수 선언시에 타입을 선언해서 알려줄수도 있고,
  2. weightEditText처럼 findViewById의 제네릭 타입으로 알려줄 수도 있음

Log함수 --> Log찍기 ,,, Log.d()

 Log.d("MainActivity","ResultButton 이 클릭되었습니다.")

--> 로그옆의 .d라는 것은 로그의 수준을 나타낸다.

Logcat을 보면 Verbose, Debug, Info, Warn 등등의 구분이 있는데 이것을 나타낸 것이다.
따라서 .d라는 것은 Debug의 약자이며 해당 로그는 Debug영역의 log로 나타나게 된다.

-----------

어느 함수의 return인지 명시하기

return@setOnClickListener

위의 예시의 경우 OnCreate함수 내부에 setOnClickListener함수 내에 이 코드에 있는 상황이다.

OnCreate함수와 setOnClickListener의 람다함수가 겹쳐있기 때문에 어느 함수를 return할지 명시해줘야함


값의 타입 변환

val weight: Int = weightEditText.text.toString().toInt()

EditText에 text속성에 있는 값( 즉, 입력값 )을 참조하여 가져온 후, toString()함수로 문자열로 만든 뒤 toInt()함수로 정수로 바꿔서 저장

Intent 함수 --> 액티비티를 실행하기 위한 함수

 val intent = Intent(this, ResultActivity::class.java)

Intent > 액티비티를 시작하기 위한 함수,,
첫번째 파라미터는 현재 액티비티를 주고ㅡ, 두번째 파라미터는 이동할 액티비티를 준다.

--> 이외에 서비스 시작, 브로드캐스트 전달의 역할도 맡고 있다.

	kotlin코드로 Intent를 짤 때는 이동할 액티비티를 설정하는 부분이 조금 차이가 있으므로 주의 
    ------->   ResultActivity::class.java

Intent로 액티비티를 이동하기 위해서는 AndroidManifest파일에 액티비티들을 등록해놓아야 한다.
( Intent와 상관없이 앱에서 사용하는 액티비티는 모두 등록해야한다. )


Intent함수로 값을 실어 보내기

intent.putExtra("height",height)
intent.putExtra("weight",weight)

Intent로 액티비티를 실행할 때, 실행한 액티비티는 이전 액티비티의 내용을 전혀 알 수 없음
따라서 앱 진행과정에서 필요에 따라 값을 실어보낼 필요성이 존재함

첫번째 파라미터가 값의 이름( 이 이름을 가지고 값을 특정하여 받을 것임 ), 두번쨰 파라미터가 그 이름에 해당하는 값

height와 weight란 이름으로 각각 값을 담고 있음 --> 이후 이 Intent로 액티비티를 시작하면 해당 액티비티에서 이 값을 받을 수 있음


문자열 중간에 변수넣어서 문자열 구성하기

Log.d("MainActivity","height = $height, weight = $weight")

"$변수명"의 구성으로 문자열 중간에 넣으면,
문자열 안에서도 해당 변수의 값을 바로 불러올 수 있다.

--> 위의 코드의 경우
height = 170, weight = 50
height와 weight의 값에 따라 대충 이런 느낌으로 출력됨


람다함수형식의 리스너 설정 --> kotlin의 특징

val resultButton = findViewById<Button>(R.id.resultButton)

resultButton.setOnClickListener {  
// 람다함수의 실행부
}

람다형식으로 리스너를 설정하고 있음 ( 코들린의 특징 )
--> resultButton에 Click이라는 액션을 받아오고 있음


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"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity"
    tools:ignore="ExtraText">


    <TextView
        android:textSize="20sp"
        android:text="@string/height"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:textColor="@color/custom_black"/>



    <EditText
        android:id="@+id/heightEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="number"
        android:layout_marginTop="10dp"/>



    <TextView
        android:textSize="20sp"
        android:text="체중"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:textColor="#000000"/>


    <EditText
        android:id="@+id/weightEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="number"
        android:layout_marginTop="10dp"/>

    <Button
        android:id="@+id/resultButton"
        android:text="확인하기"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>


</LinearLayout>

위 코드에서 주목할 부분들

dp란?

안드로이드 화면의 기본 단위

왜 dp를 쓰지?

안드로이드 기기 마다 해상도와 화면 사이즈가 다를 수 있기 때문에 통일해 준 것이다.


sp란?

글자 크기의 단위
--> sp 단위로 정할 경우 사용자가 글자크기에 대한 설정을 바꿨을 때 [ 작게, 중간, 크게 ]
그에 따라 조금씩 사이즈가 수정되서 나타나게 된다.

반면 글자 사이즈를 dp 단위로 정할 경우, 사용자가 글자크기에 대한 설정을 바꿔도 해당 글자의 크기에는
반영되지 않으므로 글자 크기를 완전히 고정하고 싶다면 dp 단위로 하는 것이 좋다.

LinearLayout --> 정해진 방향(가로, 세로)에 따라 하나씩 컴포넌트를 쌓아가는 방식

orientation -> LinearLayout의 방향을 나타내는 속성


TextView --> 텍스트를 쓰기 위한 뷰

text --> text의 내용을 넣을 수 있음, 일반적으로 res폴더의 strings파일에서 정의한 뒤 참조하여 사용
--> "@string/height" 이런식으로

testSize --> text의 크기를 정할 수 있음

textStyle --> text의 스타일을 선택할 수 있음 bold(굵게) italic(기울여서) normal(일반)

textColor --> text의 색을 선택할 수 있음 "#000000"과 같이 RGB로 하거나, @color/black과 같이 res폴더에 있는 colors파일에서 참조해서 선택할 수도 있다.


EditText --> 키보드를 통해 데이터를 입력받기 위한 뷰

inputType --> 입력할 문자 제한 , 헤당 문자에 대한 키보드가 나타남
( number, numberSigned(소수점가능숫자), numberDecimal(실수) text, textMultiLine(여러줄가능문자열), phone 등등 )


일반적으로 공통되는 속성

padding --> 해당 컴포넌트의 내부에 대해 해당 수치만큼 떨어뜨린단 뜻

margin --> 해당 컴포넌트의 외부에 대해 해당 수치만큼 떨어뜨린단 뜻

id --> 해당 컴포넌트의 식별자ㅡ, 이 xml를 사용하는 java 파일에서 각각의 컴포넌트를 식별하기 위해 사용 ( R파일의 id영역에 해당 컴포넌트의 주소값을 넣은 것임 )

textIsSelectable = (boolean) --> 해당 텍스트를 포커싱 가능하게 만들어준다
( 텍스트에 나온 값을 복사하기 위해서 설정 )


ResultActivity.kt

package fastcampus.aop.part1.aop_part2_chapter01


import android.os.Bundle
import android.os.PersistableBundle
import android.util.Log
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import kotlin.math.pow

class ResultActivity : AppCompatActivity(){

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

        val height = intent.getIntExtra("height",0)
        val weight = intent.getIntExtra("weight",0)

        Log.d("ResultActivity","height : $height, weight : $weight")


        val bmi = weight / (height / 100.0).pow(2.0)

        val resultText = when {
            bmi >= 35.0 -> "고도 비만"
            bmi >= 30.0 -> "중정도 비만"
            bmi >= 25.0 -> "경도 비만"
            bmi >= 23.0 -> "과체중"
            bmi >= 18.5 -> "정상 체중"
            else -> "저체중"
        }

        val resultValueTextView = findViewById<TextView>(R.id.bmiResultTextView)
        val resultStringTextView = findViewById<TextView>(R.id.resultTextView)

        resultValueTextView.text = bmi.toString()
        // text속성은 String타입을 받아야하기 때문에 toString()함수로 치환해준다.
        resultStringTextView.text = resultText
    }

    }

위 코드에서 주목할 부분들

Intent로 값 받아오기

val height = intent.getIntExtra("height",0)
val weight = intent.getIntExtra("weight",0)

kotlin에서는 java코드와는 다르게 intent에서 바로 값을 받아올 수 있음
( i가 소문자인 것에 주의 +
위의 전체 코드를 보면 따로 선언하지 않고 사용하고 있음 --> 메모!!!!!!!!!! )

getIntExtra는 Int형식으로 값을 받는다는 것이며, 뒤의 첫번째 파라미터는 받을 값의 이름이고, 두번째 파라미터는 그 이름의 값이 없을 경우 반환되는 값이다.


pow 함수와 실수 정수 계산

val bmi = weight / (height / 100.0).pow(2.0)

bmi 계산식 --> pow는 제곱해주는 함수 ,, double을 double로 제곱해주는 함수이기 때문에 각각의 숫자에 ".0"을 추가하여 실수형으로 바꾸었다

--> 정수와 실수의 나눗셈을 하면 실수이므로 height/100.0도 실수가 된다.


TextView의 text속성에 들어갈 수 있는 값 타입

resultValueTextView.text = bmi.toString()

text속성은 String타입을 받아야하기 때문에 toString()함수로 치환해준다.


activity_result.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">
    <!--    gravity속성 > 레이아웃 내의 컴포넌트들을 정한 방향으로 정렬시켜줌-->

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal">

        <TextView

            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="BMI : " />

        <TextView
            android:id="@+id/bmiResultTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""
            tools:text="23.111111" />
        <!--        tools를 받아서 속성을 사용하면 안드로이드 스튜디오의 디자인 부분에만 적용되고, 실제 앱을 실행했을 때는 적용되지 않는다.-->


    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="결과는 : " />

        <TextView
            android:id="@+id/resultTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="과체중입니다." />

    </LinearLayout>


</LinearLayout>

tools로 부터 참조해오는 속성

        <TextView
            android:id="@+id/bmiResultTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""
            tools:text="23.111111" />
            

tools를 받아서 속성을 사용하면 안드로이드 스튜디오의 디자인 부분에만 적용되고, 실제 앱을 실행했을 때는 적용되지 않는다.


AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="fastcampus.aop.part1.aop_part2_chapter01">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Aoppart2chapter01">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".ResultActivity">

        </activity>
    </application>

</manifest>

--> 앱에서 사용하는 액티비티들은 모두 manifest에 등록해줘야함


res폴더에 있는 colors.xml과 strings.xml파일

--> 앱에서 사용하는 색은 colors.xml에 정리하고 참조하여 사용
--> 앱에서 사용하는 택스트는 strings.xml에서 정리하고 참조하여 사용

왜 굳이 colors.xml과 strings.xml에서 정리해서 사용할까?

--> 앱의 내용을 수정하고 싶을 때, 이렇게 참조구조로 되어 있으면 매우 쉽게 변경이 가능하기 때문

colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
    <color name="custom_black">#222222</color>
</resources>

strings.xml

<resources>
    <string name="app_name">aop-part2-chapter01</string>
    <string name="height">신장</string>
</resources>
profile
ㅎㅎ

0개의 댓글