ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TECHIT 앱 스쿨 2기: Android 54일차 (23.07.14)
    [THEC!T] 앱 스쿨2기 : Android 2023. 7. 15. 22:38
    728x90

    자료 출처 : 안드로이드 앱스쿨 2기 윤재성 강사님 수업 내용

     

    9 patch 이미지에 대한 설명으로 설명을 해주셨습니다.

    View의 배경으로 이미지를 지정하였을 경우 이미지의 가로 세로 길이보다 View의 가로 세로 길이가 더 길면 이미지가 늘어나게 됩니다. 이때 만약 이미지가 말풍선과 같은 이미지라면 하기 이미지 처럼 보기 좋지 않을 수 있습니다.

     

    9 patch 이미지

    이미지의 늘어날 부분을 설정한 이미지 이며, 늘어나는 영역으로 설정된 부분만 늘어나고 그렇지 않는 부분은 늘어나지 않기 때문에 이미지가 늘어나도라도 이상하게 나타나지 않게 할 수 있습니다.

    좌측 상단 부분 : 이미지가 확대 되는 영역

    우측 하단 부분 : 이미지 안의 내용 부분이 확대 되는 영역

     

    9 patch 이미지 만들기

    해당 파일을 만드는 것은 어렵지 않습니다.  배경으로 지정할 사진 파일을 drawable 폴더에 넣어 준 후 해당 파일을 우클릭 하여 하기에 있는 Crate 9-Patch file.. 을 클릭한 후 파일명을 조금 변경한 후 OK 를 눌러주면 9 patch 이미지가 만들어 집니다. 이름은 변경하는 이유는 앞에 있는 이름을 가지고 안드로이드 스튜디오가 판단하기때문에 동일 파일로 인식하기 때문입니다.

     

     

    이제 간단하게 늘어날 부분을 클릭하여 지정할 수 있으며, 취소를 할 경우에는 Shift 키를 누른 상태로 드래그 하여 취소 할 수 있습니다. 그리고 옆에 있는 3가지의 그림은 위아래로 늘어났을 때의 이미지, 좌우로 늘어났을 때의 이미지, 상하좌우로 늘어 났을 때의 이미지를 미리 보기로 보여주는 것 입니다.

    좌측 상단 부분 : 이미지가 확대 되는 영역

    우측 하단 부분 : 이미지 안의 내용 부분이 확대 되는 영역

     

     

    다음으로는 해당 사진을 배경으로 한 텍스트 뷰들의 차이점을 확인해 보겠습니다.

    처음에 있는 것은 그냥 배경을 일반 이미지로 한 것 이고, 두 번째 는 9Patch 이미지를 적용한 것입니다.

    해당 사진은 제가 글을 어디에 배치할 것인가 에 대하여 드래그를 넓게 하여 생긴 것이기 때문에 좀 더 이쁘게 하려면 영상에 나온 것보다 위아래를 좀 더 짧게 드래그 하면 더욱 이뻐 집니다.

     

    다음으로는 단말기 대응에 대한 설명을 해주셨습니다.

    안드로이드의 경우 단말기의 상태에 따라서 대응할 수 있도록 지원하고 있습니다.

    언어, 지역, 화면 해상도, 크기, 안드로이드 버전 등 다양한 옵셩을 설정하여 단말기의 상태에 대응할 수 있습니다.

    res 폴더 내의 폴더에 수식을 설정해주면 수식어와 일치하는 단말기에 대해서 이미지, 문자열 등을 선택해서 사용할 수 잇도록 제공할 수 있습니다.

     

    지역화에 대한 설명

    지역화는 하나의 애플리케이션으로 다양한 언어 및  국가를 지원하기 위한 개념이며, res 폴더에 있는 리소스 폴더에 국가 코드를 추가하여 지역화를 할 수 있습니다.

     

    하기는 간단한 예시 입니다.

    res - values - string.xml 파일 내 localtest 라는 이름으로 안녕하세요를 설정하였습니다.


    다음으로는 res - values - string.xml 을 우클릭 하여 가장하단에 있는  Open Translations Editor 를 클릭해 줍니다.

     

    클릭 하면 다음과 같은 화면이 나오는데 상기에 있는 지구본+ 모양의 아이콘을 눌러줍니다.

     

    누르게 되면 하기와 같이 여러 나라가 나오게 됩니다.

    저의 경우 US 를 선택하였습니다.

    그러면 하기와 같이 해당 나라일 경우 설정되는 값이 나옵니다. Hello~! 를 입력하였습니다.

    만약 설정을 하지 않았을 경우 Default Value 값이 나오게 됩니다.

    이제 모바일 기기에 설정된 언어에 따라서 해당 언어가 표시되도록 만들었습니다.

     

    다음은 해상도 대응에 대한 설명을 해주셨습니다.

    안드로이드는 단말기의 해상도에 따라서 이미지를 선택하여 그림을 그릴 수 있도록 할 수 있습니다.

    단말기 해상도에 따른 분류

    ldpi : ~ 120dpi
    mdpi : ~ 160dpi
    hdpi : ~ 240dpi
    xhdpi : ~ 320dpi
    xxhdpi : ~ 480dpi
    xxxhdpi : ~ 640dpi

     

    단말기 배율

    ldpi : 0.75
    mdpi : 1.0
    hdpi : 1.5
    xhdpi : 2
    xxhdpi : 3
    xxxhdpi : 4

     

    drawable 이미지 적용

    먼저 단말기 해상도에 해당하는 폴더에 이미지가 있을 경우에는 원본 크기 그대로 그립니다.

     

    해상도에 해당하는 디렉토리에 이미지가 없을 경우에는 인접한 해상도 폴더의 이미지를 가져와 적당한 배율로 확대하거나 축소 해서 그립니다.

     

    인접한 해성도 폴더에 이미지가 없을 경우에는 drawable 폴더의 이미지를 확대 혹은 축소해서 그립니다.

    하기와 같은 속성을 가진 이미지 들을 안드로이드 스튜디오에 넣고 해상도가 다른 단말기 별로 켜보면 하기와 같이 나옵니다.

     

     

    다음으로는 화면 회전에 따른 화면 적용방법에 대해서 알려주셨습니다.

     

    layout 폴더에 수식어를 추가하면 회전에 따른 화면을 따로 적용할 수 있습니다.

    수식어
    layout-port : 세로 화면
    layout-land : 가로 화면

     

    안드로이드에서는 화면이 회전이 발생하게 되면 화면을 새롭게 만들게 됩니다. 

    이때 일부 UI요소들은 초기 값으로 설정이 되기 때문에 복원하는 작업이 필요하며, 초기화 되기전 onSaveInstanceState 메서드에서 복원 시 필요한 값을 저장하며, OnCreate 메서드에서 복원 작업을 진행합니다.

    EditText 의 내용의 경우 유지, TextView의 내용 사라짐.

    만약 화면 회전을 막고자 할 경우에는  AndroidManifest.xml의 Activity 태그에 screenOrientation 속성을 portait로 설정하면 됩니다.

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">
    
        <application
            android:allowBackup="true"
            android:dataExtractionRules="@xml/data_extraction_rules"
            android:fullBackupContent="@xml/backup_rules"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.Android83_ScreenRotation"
            tools:targetApi="31">
            <activity
                android:name=".MainActivity"
                android:exported="true"
                android:screenOrientation="portrait">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    class MainActivity : AppCompatActivity() {
    
        lateinit var activityMainBinding: ActivityMainBinding
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
    
            activityMainBinding.run{
    
                // 복원
                if(savedInstanceState != null){
                    val str1 = savedInstanceState.getString("str1")
                    textViewResult.text = str1
                }
    
                buttonLoginSubmit.setOnClickListener {
                    editTextUserId.setText("하하하하")
                    editTextUserPw.setText("aaaaaa")
                }
    
                buttonJoin.setOnClickListener {
                    textViewResult.text = "버튼을 눌렀습니다."
                }
            }
    
            setContentView(activityMainBinding.root)
    
    
        }
    
        // 화면 회전이 발생했을 때 호출되는 메서드(프래그먼트도 동일하다)
        // 매개변수로 들어오는 번들 객체는 화면이 다시 만들어지고 onCreate 메서드를 호출할 때
        // 매개변수로 전달된다.
        // 여기에는 화면 회전 후 값이 유지되지 않는 View들에 대한 복원 작업을 할 수 있도록
        // 데이터를 저장하는 역활을 한다.
        override fun onSaveInstanceState(outState: Bundle) {
            super.onSaveInstanceState(outState)
    
            // 새로운 화면에 복원해야할 뷰의 값을 저장한다.
            outState.putString("str1", activityMainBinding.textViewResult.text.toString())
        }
    }

    1. 상기 코드 상태

    2. ndroidManifest.xml의 Activity 태그에 screenOrientation 속성 과 override fun onSaveInstanceState 메서드를 작성하지 않은 상태

    3. override fun onSaveInstanceState 메서드를 작성한 상태

     

    다음으로는 단말기의 정보를 파악하는 법에 대하여 설명해주셨습니다.

     

    전화번호 : line1Number
    SIM 국가 코드 : simCountryIso)
    모바일 국가코드 + 모바일 네트워크 코드 : simOperator)
    서비스 이름 : simOperatorName)
    SIM 상태(통신 가능 여부, PIN LOCK 여부) : simState)
    음성 메일 번호 : voiceMailNumber)

     

    보드 이름 : Build.BOARD
    소프트웨어를 커스터마이징한 제조사 : Build.BRAND
    제조사 디자인명 : Build.DEVICE
    사용자에게 표시되는 빌드 ID : Build.DISPLAY
    빌드 고유 ID : Build.FINGERPRINT
    ChangeList 번호 : Build.ID                

     

    제품/하드웨어 제조업체 : Build.MANUFACTURER
    제품 모델명 : Build.MODEL
    제품명 :  Build.PRODUCT
    빌드 구분 : Build.TAGS
    빌드 타입 : Build.TYPE
    안드로이드 버전 : Build.VERSION.RELEASE

     

    class MainActivity : AppCompatActivity() {
        lateinit var activityMainBinding: ActivityMainBinding
    
        val permissionList = arrayOf(
            Manifest.permission.READ_PHONE_STATE,
            Manifest.permission.READ_SMS,
            Manifest.permission.READ_PHONE_NUMBERS
        )
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(activityMainBinding.root)
    
            requestPermissions(permissionList, 0)
    
            // TelephoneMannager를 추출한다.
            val telephonyManager = getSystemService(TELEPHONY_SERVICE) as TelephonyManager
    
            activityMainBinding.run{
                // 권한 허용여부를 확인해 거부한 것이 하나라도 있으면 단말기 정보를 출력하지 않는다.
                val chk1 = ActivityCompat.checkSelfPermission(this@MainActivity, Manifest.permission.READ_SMS)
                val chk2 = ActivityCompat.checkSelfPermission(this@MainActivity, Manifest.permission.READ_PHONE_NUMBERS)
                val chk3 = ActivityCompat.checkSelfPermission(this@MainActivity, Manifest.permission.READ_PHONE_STATE)
    
                if(chk1 == PackageManager.PERMISSION_GRANTED &&
                    chk2 == PackageManager.PERMISSION_GRANTED &&
                    chk3 == PackageManager.PERMISSION_GRANTED){
                    // 전화번호
                    textView.text = "전화번호 : ${telephonyManager.line1Number}\n"
                    // SIM 국가 코드
                    textView.append("SIM 국가 코드 : ${telephonyManager.simCountryIso}\n")
                    // 모바일 국가 코드 + 모바일 네트워크 코드
                    textView.append("모바일 국가 코드 + 모바일 네트워크 코드 : ${telephonyManager.simOperator}\n")
                    // 서비스 이름
                    textView.append("서비스 이름 : ${telephonyManager.simOperatorName}\n")
                    // SIM 상태 (통신 가능 여부, PIN LOCK 여부)
                    textView.append("SIM 상태 : ${telephonyManager.simState}\n")
                    // 음성 메일 번호
                    textView.append("음성 메일 번호 :${telephonyManager.voiceMailNumber}")
    
                    textView.append("보드 이름 : ${Build.BOARD}\n")
                    // 소프트웨어를 커스터마이징 한 회사
                    textView.append("소프트웨어를 커스터마이징 한 회사 : ${Build.BRAND}\n")
                    // 제조사 디자인 명
                    textView.append("제조사 디자인 명 : ${Build.DEVICE}\n")
                    // 사용자에게 표시되는 빌드 ID
                    textView.append("사용자에게 표시되는 빌드 ID : ${Build.DISPLAY}\n")
                    // 빌드 고유 ID
                    textView.append("빌드 고유 ID : ${Build.FINGERPRINT}\n")
                    // ChangeList 번호
                    textView.append("ChangeList 번호 : ${Build.ID}\n")
                    // 제품/하드웨어 제조업체
                    textView.append("제품/하드웨어 제조업체 : ${Build.MANUFACTURER}\n")
                    // 제품 모델명
                    textView.append("제품 모델명 : ${Build.MODEL}\n")
                    // 제품명
                    textView.append("제품 명 : ${Build.PRODUCT}\n")
                    // 빌드 구분
                    textView.append("빌드 구분 : ${Build.TAGS}\n")
                    // 빌드 타입
                    textView.append("빌드 타입 : ${Build.TYPE}\n")
                    // 안드로이드 버전
                    textView.append("안드로이드 버전 : ${Build.VERSION.RELEASE}\n")
                    // 안드로이드 코드네임
                    textView.append("안드로이드 코드 네임 : ${Build.VERSION.CODENAME}\n")
                    // 안드로이드 API 레벨
                    textView.append("안드로에드 API 레벨 : ${Build.VERSION.SDK_INT}\n")
    
                    // 단말기 해상도 정보
                    // 해상도 정보를 가지고 있는 객체를 추출한다.
                    val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
    
                    // 안드로이드 버전으로 분기한다.
                    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
                        // 단말기의 가로 해상도를 가져온다.
                        val width = windowManager.currentWindowMetrics.bounds.width()
                        // 단말기의 세로 해상도를 가져온다.
                        val height = windowManager.currentWindowMetrics.bounds.height()
    
                        textView.append("가로 해상도 : ${width}\n")
                        textView.append("세로 해상도 : ${height}\n")
                    } else {
                        // 해상도 정보를 담을 객체
                        val point = Point()
                        // 해상도 정보를 담는다.
                        windowManager.defaultDisplay.getSize(point)
    
                        textView.append("가로 해상도 : ${point.x}\n")
                        textView.append("세로 해상도 : ${point.y}\n")
    
                    }
                }
            }
        }
    }


    다음으로는 안드로이드 센서에 대한 설명을 해주셨습니다.

     

    스마트폰에는 수십가지의 센서가 장착되어 있으며 이를 통해 사용자에 대한 정보를 수집할 수 있습니다.
    스마트폰이 부팅되면 단말기에 장착되어 있는 모든 센서들이 동작하기 시작하고 애플리케이션에서는 이 값을 받아와 사용할 수 있으며, 4차 산업 혁명 시대가 되면서 사용자에 대해 학습한 후 맞춤 서비스를 제공하는 애플리케이션들을 개발하는데도 이용할 수 있습니다.

     

    그리고 간단하게 애뮬레이터에서 센서를 사용하는 방법을 알려주셨습니다.

    정확한 센서의 사용법은 내일 알려주시기로 하셨습니다.

     

     

    마무리

    최근 더위를 먹었는지 복습을 하였지만 정리하여 적지 않았었는데 현재 적응이 되었는지 컨디션이 돌아왔습니다.

    그리고 몰랐는데 제가 복습하는 것을 관심있게 봐주시는 분도 있다는 것을 알게 되었고, 초기 다짐했던 대로 꾸준한 복습을 통하여 발전하는 모습을 다시금 가져야 겠다고 생각을 하게 되었습니다.

     

    오늘의 마음가짐

    푹 쉬었으니 다시 달리자~!

     

Designed by Tistory.