ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TECHIT 앱 스쿨 2기: Android 34일차 (23.06.15)
    [THEC!T] 앱 스쿨2기 : Android 2023. 6. 16. 00:22
    728x90

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

     

    오늘의 시작은 어제 내주신 문제에 대한 설명과 작성으로 시작하셨습니다.

     

    강사님과 제가 작성한 코드의 차이점은 레이아웃에서 뷰의 배치 및 텍스트 내용 등이 있었습니다.

    그 외에는 구현하려는 내용이 동일해서 그런지 거의 비슷 하였습니다.

     

    그리고 또 다른 리사이클러뷰를 이용하여 작성하는 문제를 내주셨습니다.

     

     

    현재 정확히는 기억이 나지 않으나 프로그먼트를 이용하여 화면전환등을 아직 배우지 않았기에 지난번에 배운 visibility 를

    이용하여 해당 문제에서 요구하는 기능을 구현 할 수 있을 거라고 생각하여 바로 작성하였습니다.

     

    해당 문제 작성 후 동작 모습

     

    작성한 코드

     

    package com.test.android37ex02_study
    
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.os.SystemClock
    import android.view.View
    import android.view.View.OnClickListener
    import android.view.ViewGroup
    import android.view.inputmethod.InputMethodManager
    import android.widget.Button
    import android.widget.TextView
    import androidx.core.view.isVisible
    import androidx.recyclerview.widget.GridLayoutManager
    import androidx.recyclerview.widget.LinearLayoutManager
    import androidx.recyclerview.widget.RecyclerView
    import androidx.recyclerview.widget.RecyclerView.ViewHolder
    import com.test.android37ex02_study.databinding.ActivityMainBinding
    import com.test.android37ex02_study.databinding.RowBinding
    import kotlin.concurrent.thread
    
    class MainActivity : AppCompatActivity() {
    
        lateinit var activityMainBinding: ActivityMainBinding
        val dataList = mutableListOf<Student>()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(activityMainBinding.root)
    
    
            activityMainBinding.run{
    
                thread {
                    SystemClock.sleep(500)
    
                    val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
                    imm.showSoftInput(currentFocus,0)
    
                }
    
                editTextName.run{
                    requestFocus()
                    val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
                    imm.showSoftInput(currentFocus,0)
                }
    
                buttonAdd.run{
                    setOnClickListener {
                        intputLayout.isVisible = true
                        recyclerView.isVisible = false
    
                        val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
                        imm.showSoftInput(currentFocus,0)
                        editTextName.requestFocus()
                    }
                }
    
                buttonLook.run {
                    setOnClickListener {
    
                        if(currentFocus != null){
                            val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
                            imm.hideSoftInputFromWindow(currentFocus!!.windowToken,0)
                            clearFocus()
                        }
    
    
    
                        intputLayout.isVisible = false
                        recyclerView.isVisible = true
                    }
                }
    
                editTextKorean.run{
                    setOnEditorActionListener { v, actionId, event ->
    
                        val name = editTextName.text.toString()
                        val age = editTextAge.text.toString()
                        val korean = editTextKorean.text.toString()
    
                        val newData = Student(name, age, korean)
                        dataList.add(newData)
    
                        val adapter = recyclerView.adapter as RecyclerAdapter
    
                        adapter.notifyDataSetChanged()
    
                        editTextName.setText("")
                        editTextAge.setText("")
                        editTextKorean.setText("")
    
                        editTextName.requestFocus()
    
                        false
                    }
                }
    
                recyclerView.run {
                    adapter = RecyclerAdapter()
                    layoutManager = LinearLayoutManager(this@MainActivity)
    
                }
    
            }
        }
    
        inner class RecyclerAdapter : RecyclerView.Adapter<RecyclerAdapter.ViewHolderClass>(){
    
            inner class ViewHolderClass(rowBinding: RowBinding):  RecyclerView.ViewHolder(rowBinding.root){
                var name : TextView
                var age : TextView
                var korean : TextView
                var removeBtn : Button
                init{
                    name = rowBinding.textViewName
                    age = rowBinding.textViewAge
                    korean = rowBinding.textViewKorean
                    removeBtn = rowBinding.buttonRemove
                }
    
    
            }
    
            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderClass {
    
                val rowBinding = RowBinding.inflate(layoutInflater)
                val viewHolderClass = ViewHolderClass(rowBinding)
    
                val params = RecyclerView.LayoutParams(
                    RecyclerView.LayoutParams.MATCH_PARENT,
                    RecyclerView.LayoutParams.WRAP_CONTENT
                )
                rowBinding.root.layoutParams = params
    
    
                return viewHolderClass
            }
    
            override fun getItemCount(): Int {
                return dataList.size
            }
    
            override fun onBindViewHolder(holder: ViewHolderClass, position: Int) {
    
                holder.run{
                    name.text = dataList[position].name
                    age.text = dataList[position].age
                    korean.text = dataList[position].korean
    
                    removeBtn.setOnClickListener {
                        dataList.removeAt(position)
    
                        val adapter = activityMainBinding.recyclerView.adapter as RecyclerAdapter
                        adapter.notifyDataSetChanged()
    
                    }
                }
    
            }
    
        }
    
        data class Student(
            var name: String,
            var age: String,
            var korean: String
        )
    
    }

     

    그리고 강사님과 코드를 비교하였을 때 

    차이점으로는 isVisible 의 사용과 레이아웃이름.visibility = View.VISIBLE 의 사용  및 뷰의 일부 배치 등 의 차이가 있었지만, 그 외에는 구현하려는 기능이 동일하여 큰 차이는 없었습니다.

     

     

    그리고 오늘 안드로이드 UI 관련한 강의 는 마무리가 되었고, 안드로이드의 메뉴와 4대 구성요소 파트로 변경되었습니다.

     

    새로운 파트의 시작은 안드로이드의 권한으로 시작하였습니다.

     

    안드로이드 앱에서는 다양한 종류의 권한이 있으며, 몇 가지 대표적인 권한에 대해서 알려주셨습니다.

    1. 위치 권한: 앱이 사용자의 위치 정보에 접근할 수 있도록 합니다. 이는 지도 기반 서비스나 위치 추적 기능을 사용하는 앱에 필요한 권한입니다.

    2. 연락처 권한: 앱이 사용자의 연락처에 접근하여 연락처 정보를 읽거나 수정할 수 있도록 합니다.

    3. 저장소 권한: 앱이 사용자의 저장소에 접근하여 파일을 읽거나 쓸 수 있도록 합니다.

    4. 인터넷 권한: 앱이 인터넷에 접근하여 네트워크 통신을 할 수 있도록 합니다. 이는 웹 서비스와의 통신이나 데이터 다운로드 등에 필요한 권한입니다.

    manifest 파일 하단에 사용하려는 권한을 명시해야 하며,  이 후 사용자가 사용을 할 때 안드로이드 OS에서 해당 권한에 대해서 사용자의 승인이 있다고 한다면 권한 요청 창을 띄웁니다.

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>

     

    만약 개발자가 사용자의 승인이 필요한 권한이 필요한 기능 등을 작성하려고 한다면, 해당 권한을 승인 받았는지 확인하고 그 결과에 따라 작동하도록 코드를 작성해야 합니다.

     

    모든 권한에 대해서 한번에 승인을 받고자 한다면 equestPermission 메서드를 사용하고 권한 승인 여부에 따라 처리가 필요할 경우 onRequestPermissionsResult 메서드를 overriding하여 권한 별로 분기하여 처리하면 됩니다.

    만약, 권한 요청 후 필요한 처리를 권한 별로 나누어서 구현하고 싶다면, ActivityResultCallback을 사용한다. 라고 말씀해 주셨습니다.

     

    package com.test.android38_permissionstudy
    
    import android.Manifest
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import com.test.android38_permissionstudy.databinding.ActivityMainBinding
    
    class MainActivity : AppCompatActivity() {
    
        lateinit var activityMainBinding: ActivityMainBinding
    
        // 승인 요청을 받을 권한 목록
        // 만약 이미 승인이 되어있거나, 승인이 필요 없는 권한은 requestPermission 에서 넘어간다.
        val permissionList = arrayOf(
            Manifest.permission.INTERNET,
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_CONTACTS
        )
    
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(activityMainBinding.root)
    
            activityMainBinding.run{
                button.run{
                    setOnClickListener {
                        requestPermissions(permissionList,0)
                    }
                }
            }
        }
    
        // requestPermissions 을 통하여 권한을 요청하면 요청 작업이 끝난면 호출되는 메서드
        override fun onRequestPermissionsResult(
            requestCode: Int,
            permissions: Array<out String>,
            grantResults: IntArray
        ) {
    
            super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    
            activityMainBinding.run{
                textView2.text = ""
    
                // permissions 권한 목록
                // grantResults 권한 승인 요청에 대한 결과.
    
                for(idx in 0 .. permissions.size-1){
                    textView2.append("${idx+1}번 권한 : ${permissions[idx]} , \n 승인 결과 : ${grantResults[idx]}\n")
                }
    
    
            }
    
        }
    
    }

     

     

    이 외에도 권한 종류 별로 나누어 처리하는 방법도 알려주셨지만 해당 방법에 대해서는 추후 액티비티에 대한 관련 개념을 수강한 후에 적을 수 있도록 하겠습니다.

     

    권한에 대하여 알려주신 뒤에는 다시 리사이클러 뷰를 사용하여 작성하는 문제를 내주셨습니다.

     

    package com.test.android37ex03
    
    import android.icu.text.IDNA
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.view.ViewGroup
    import android.widget.ArrayAdapter
    import android.widget.ImageView
    import android.widget.Spinner
    import android.widget.SpinnerAdapter
    import androidx.core.view.isVisible
    import androidx.recyclerview.widget.LinearLayoutManager
    import androidx.recyclerview.widget.RecyclerView
    import androidx.recyclerview.widget.RecyclerView.ViewHolder
    import com.test.android37ex03.databinding.ActivityMainBinding
    import com.test.android37ex03.databinding.RowBinding
    
    class MainActivity : AppCompatActivity() {
    
        lateinit var activityMainBinding: ActivityMainBinding
        val nationNameList = arrayOf(
            "토고", "프랑스", "스위스", "스페인", "일본", "독일", "브라질", "대한민국"
        )
        val nationImgList = arrayOf(
            R.drawable.imgflag1,
            R.drawable.imgflag2,
            R.drawable.imgflag3,
            R.drawable.imgflag4,
            R.drawable.imgflag5,
            R.drawable.imgflag6,
            R.drawable.imgflag7,
            R.drawable.imgflag8,
        )
        val InfoList = ArrayList<Info>()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(activityMainBinding.root)
    
            activityMainBinding.run{
                // 일단 둘 다 가린다.
                linearLayout.isVisible = false
                recyclerView.isVisible = false
    
                buttonAdd.run{
                    setOnClickListener {
                        linearLayout.isVisible = true
                        recyclerView.isVisible = false
                    }
                }
    
                buttonLook.run{
                    setOnClickListener {
                        linearLayout.isVisible = false
                        recyclerView.isVisible = true
                    }
                }
    
                spinner.run{
                    val sa = ArrayAdapter<String>(
                        this@MainActivity,
                        android.R.layout.simple_list_item_1,
                        nationNameList
                    )
                    sa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
                    adapter = sa
    
                }
    
    
                buttonSave.run {
                    setOnClickListener {
    
                        val position = when(radioGroup.checkedRadioButtonId){
                            radioButtonAttack.id ->{
                                "공격수"
                            }
                            radioButtonMid.id -> {
                                "미드필더"
                            }
                            radioButtonDenfense.id -> {
                                "수비수"
                            } else ->{
                                "골키퍼"
                            }
                        }
    
                        InfoList.add(
                            Info(nationImgList[spinner.selectedItemPosition],
                                editTextName.text.toString(),
                                position
                            )
                        )
    
                        val adapter = recyclerView.adapter as RecyclerAdapterClass
                        adapter.notifyDataSetChanged()
    
                        editTextName.setText("")
                        radioGroup.clearCheck()
                        if(currentFocus != null){
                            currentFocus!!.clearFocus()
                        }
                    }
                }
    
                recyclerView.run {
                    adapter = RecyclerAdapterClass()
                    layoutManager =  LinearLayoutManager(this@MainActivity)
                }
    
            }
    
        }
    
        data class Info(
            val img:Int,
            val name: String,
            val position: String
        )
    
        inner class RecyclerAdapterClass : RecyclerView.Adapter<RecyclerAdapterClass.ViewHolderClass>(){
    
            inner class ViewHolderClass(rowBinding: RowBinding) : RecyclerView.ViewHolder(rowBinding.root){
                var imageView2 = rowBinding.imageView2
                var textViewName = rowBinding.textViewName
                var textViewPosition = rowBinding.textViewPosition
            }
    
            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderClass {
    
                val rowBinding = RowBinding.inflate(layoutInflater)
                val viewHolderClass = ViewHolderClass(rowBinding)
    
                return viewHolderClass
            }
    
            override fun getItemCount(): Int {
                return InfoList.size
            }
    
            override fun onBindViewHolder(holder: ViewHolderClass, position: Int) {
                holder.run{
                    imageView2.setImageResource(InfoList[position].img)
                    textViewName.text = "이름 : ${InfoList[position].name}"
                    textViewPosition.text = "포지션 : ${InfoList[position].position}"
                }
            }
    
        }
    
    }
    
    
    동작 영상

    문제에는 저장하는 버튼이 없지만 추가하여 작성하였습니다.

    버튼을 추가한 이유는 저의 실력으로는 포지션을 정하는 순간 리사이클 뷰에 데이터를 보내는 리스트에 원활하게 저장하기 어려웠기 때문입니다. 만약 이름 입력이 마지막이였다면 구현 가능하지만 라디오 그룹 내 라디오 버튼을 체크를 클리어 하는 부분에서 제대로 데이터가 전달 및 저장이 되지 않아 버튼을 생성하여 해당 문제를 구현하였습니다.

    내일 강사님께서 코드를 작성하시면서 해당 부분을 작성하실 때 어떻게 작성하시는지 다시 한번 유의 깊게 보며 복습을 할 수 있도록 하겠습니다.

     

     

    마무리

    이제  리사이클러 뷰라는 산을 올라가고 있는데 새로운 높은 산들이 있다는 건 알았지만 직접 보게 된 느낌입니다.

    허허 오늘 강사님께서 권한 처리에 대한 두번째 방법에 대해서 설명해주셨지만 정말 어려웠는데 강사님 말씀으로는 어려운 게 맞고, 또한 관련개념을 설명하지 않아 더욱 어려울 것 이라고 말씀해주셨습니다. 그래서 일단은 스스로 확인 할 수 있는 방법에 대해서 복습을 진행하였습니다. 그래야 추후 지금은 이해가 안가더라도 관련 개념을 배우면서 해당 개념을 배울 때 좀 더 수월하게 이해할 수 있을거라 생각합니다.

     

    오늘의 마음가짐

    일단 이해할 수 있는 것부터 이해해나가면서 쌓아가는 개발자가 되자.

     

     

Designed by Tistory.