반응형

안드로이드 스튜디오에서 코틀린(Kotlin)으로 RecyclerView를 이용하여 연락처 화면을 만들어 스크롤할 수 있고, 터치하면 전화가 걸리도록 하는 앱 만들기 실습을 진행해 볼게요.

 

1. New Project → Empty Views Activity를 선택하고  Title을 정해주세요.  여기서는 RecycleView3라고 하였습니다.

 

2. 그래들(Gradle)과 메인액티비티(Main Activity)에서 뷰 바인딩(ViewBinding) 설정하기.

프로젝트 탐색창에서 "Gradle Scripts" 항목을 펼쳐보면 "build.gradle.kts (Module :app)" 이 보이고 더블클릭해 열어서 아래 이미지처럼 viewBinding.isEnabled = true를  android { ...  } 사이에 넣어주세요.  그리고 변경사항이 적용될 수 있도록 "Sync Now"를 클릭해 주세요.

Main Activity에는 아래처럼 기본 설정 코드가 들어 있을 텐데요, 

이를 아래와 같이 필요 없는 항목은 지우고 ViewBinding을 사용하기 위한 형태로 바꾸어 주세요. 

val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

 

3. 전화번호 프로필(profile) 화면 구성하기. 

프로필의 화면 구성은 아래와 같이 해볼게요.

 

프로젝트 탐색창에서  res 》 layout 에서 마우스 우클릭(이후로는 RMB로 표현)해서 New 》 Layout Resource File을 클릭하세요.

File name : 에  list_item이라 적고 OK를 클릭해 주세요.

그럼, 아래와 같이 layout 폴더에 list_item.xml이라는 파일이 생성된 것을 볼 수 있어요.

 

4. 액티비티 메인(activity_main.xml) 화면 구성하기.

추가한 ConstraintLayout이 클릭되어 있는 상태에서 오른쪽에 있는 Constraint Widget의 ⊕ 표시를 눌러 상위에 있는 부모 Constrait Layout과 연결을 시켜주세요. 

그리고 아래 이미지처럼 각각의 마진(magin)을 8씩 주세요.

우선, 전화번호 목록에 쓰일 사람들의 사진을 대신하여 이미지 아이콘을 다운로드하여 사용해 볼 텐데요,  flaticon.com에 접속하여 " profile " 로 검색해서 다운로드하여 볼게요.

검색된 아이콘들 중에 맘에 드는 것으로 몇 가지를 다운로드하여 보세요.

다운로드 표시 부분을 클릭하지 말고 이미지 가운데를 클릭하면, 가로 세로 원하는 사이즈를 선택해서 받을 수 있어요.

128픽셀을 선택해서 다운로드하세요.  512픽셀로 받아도 되지만, 기본크기가 너무 커지므로 줄여서 받아볼게요.

 

이 사이트의 경우 무료로는 하루 10개 정도 받을 수 있기 때문에 5개는 남자 이미지 5개는 여자 이미지로 총 10개를 다운로드하여서 아래와 같이 man1~5  ,  woman1~5로 네이밍 합니다.

그럼, 윈도 탐색창에서 위의 다운로드한 파일을 모두 선택해서 Ctrl + C(복사) 한 후,
안-스에서 프로젝트 탐색창의  res 》 drawable  폴더를 선택하고  Ctrl + V(붙여 넣기) 하세요.

그리고 OK 버튼을 클릭하면 아래처럼 해당 폴더에 붙여 넣기 됩니다.

물론 RMB 하여 Explorer 창을 열어 윈도 탐색창에 직업 붙여 넣기 해도 동일합니다. (해당 폴더를 바로 찾아 열어 볼 수 있음)

그럼, list_item.xml 파일에서 Widgets(위젯) 항목에 있는 ImageView를 편집화면에다 끌어다 놓기 하세요. 

그리고 바로 나타나는 창에서 man1을 선택 후 OK 버튼을 클릭합니다. 

그리고 Attributes 속성 설정창에 보이는 Constrait Widget에서 화살표로 표시한 부분만 클릭해서 부모 Constraint Layout과 각각 연결시켜 주세요.  이때 연결되는 3곳 각각의 magin은 8 정도로 세팅할게요.

 그리고 삽입한 man 이미지의 가로 폭(layout_width)과 세로 높이(layout_height)를 60dp로 설정해 주세요.

다음,  Component Tree창에서 두 번째 ConstraintLayout을 선택하고  오른쪽 Constraints항목에 있는 부분에서 layout_height 만 wrap_content로 바꾸어 주세요.

다음, 제일 위에 있는 부모 ConstraintLayout을 선택해서 동일하게 layout_height를 wrap_content로 감싸주게 되면 아래 이미지처럼 이미지가 위쪽으로 정렬이 됩니다.

 

그다음, 이미지를 선택한 상태에서 id를 이미지 뷰를 줄인 약자(iv)로 해서 iv_profile로 설정해 볼게요.

id를 변경하고 엔터를 치면 , 아래와 같이 창이 나타나고 OK를 한 번 더 눌러주면 됩니다. 

 

① 텍스트뷰를 끌어다 놓고, id = tv_name   ,   text = 홍길동 입력하고, 아래처럼 Constraint Widget의 연결을 해주세요. 

다음, text 속성에서 textSize = 16sp ,   textColor = #040404  ,  textStyle = Bold로 변경합니다.

② "전화번호"에 대한 TextView항목도 아래처럼 추가해 주세요.

Component Tree 창에서 tv_name을 Ctrl+C  ,  Ctrl+V 하면 동일한 속성으로 복사가 됩니다.   

그럼, id = tv_number로 바꾸고, 전화번호를 입력해 주세요.   색상도 적당한 색으로 바꾸고, Constraint Widget의 연결을 왼쪽 이름에 연결하고 정당한 magin을 입력해 주세요.

③ 동일한 방법으로 이메일에 대한 textView도 추가해서 아래처럼 적당할게 설정해 주세요. 

④ 동일하게 생년월일에 대한 tv_birthday texView도 아래처럼 추가해 볼게요. 

정리된 모습

 

5.  list_item.xml에 대한 객체 모듈의 클래스 만들어주기

app 》 kotlin+java 》 com.example.recyclerview3   <RMB> 해서  New 》 Kotlin Class/File을 클릭하세요.

그리고 이름에  Profile이라고 입력하고 엔터 하면

Profile이라는 확장자가 KT인 코틀린 Class 파일이 생성된 것을 볼 수 있어요.

 

클래스 파일 내용은 아래와 같이 입력해서 "사진이미지, 이름, 전화번호, 이메일, 생일" 각각에 대한 인자와 받아들일 속성에 대해 정의해 주세요. 

이때 이미지의 경우 숫자로 처리할 수 있기 때문에 Int형으로 정의합니다.

class Profile ( val picture : Int , val name : String , val telNum : String , val email : String , val birthDay : String )

 

6.  어댑터 만들기 (Adapter)

 어댑터는 뷰 홀더에서 만든 뷰 객체에 적절한 데이터를 대입해서 항목을 완성하는 역할을 하는데요,  이어서 레이아웃 매니저가(LayoutManager) 어댑터가 만든 항목들을 어떻게 배치할지 결정하여 리사이클러 뷰에 출력합니다. 

리사이클러뷰의 전체적인 구조는 아래와 같습니다.

이미지 출처 : s2choco.tistory.com
이미지 출처 : recipes4dev.tistory.com

 

그럼,  뷰홀더를 만들 때처럼,  app 》 kotlin+java 》 com.example.recyclerview3   <RMB> 해서  New 》 Kotlin Class/File을 클릭하세요.  파일 이름은 ProfileAdapter로 할게요.

클래스 파일이 생성되면 아래와 같이 우선 작성하고, 만약 RecyclerView 글자가 빨간색으로 뜬다면, 리사이클러 뷰 위젯을 자동으로 삽입되도록 Alt+Enter 하세요. 

그리고 아래처럼 코드를 이어서 작성하면, class ProfileAdapter 부분이 붉은색 밑줄이 그어지는데 마우스를 올리고 나타나는 팝업창에서 Implement members를 클릭해서 필요한 구성요소들이 자동으로 생성될 수 있도록 해줍니다. 또는 Alt+Shift+Enter 

이어서, CustomViewHolder 부분이 빨간 글자로 되는데, 이 부분도 마우스를 올리고 기다리고 있으면, 클래스를 자동으로 생성할 수 있도록 해줍니다. 

그럼, 뷰홀더(View Holder) 클래스가 만들어지는데요,

아래와 같이 코드를 완성해 보세요. TODO 라고 되어 있는 부분들은 삭제하고, 

list_item.xml에 있는 각각의 객체들을 findViewById로 변수명을 만들어 가져옵니다. 
getItemCount( ) 함수는 Main Activity에 입력되는 데이터의 총개수를 구해오는 함수입니다.

package com.example.recyclerview3
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class ProfileAdapter ( val profileList: ArrayList<Profile> ) : RecyclerView.Adapter<ProfileAdapter.CustomViewHolder> ( )
{
    class CustomViewHolder (itemView: View) : RecyclerView.ViewHolder(itemView){    // : RecyclerView... 를 상속 받아 CustomViewHolder class를 작성함.
        val picture = itemView.findViewById<ImageView>(R.id.iv_profile)    // 인물 사진
        val name = itemView.findViewById<TextView>(R.id.tv_name)           // 이름
        val telNum = itemView.findViewById<TextView>(R.id.tv_number)       // 전화번호
        val email = itemView.findViewById<TextView>(R.id.tv_email)         // 이메일
        val birthday = itemView.findViewById<TextView>(R.id.tv_birthday)   // 생일
    }

    override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): ProfileAdapter.CustomViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)   // parent에 있는 모든 정보(context)를 가져오는 설정 함.
        return CustomViewHolder(view)   // view 형태를 첨부 하여 위에서 정의한 CustomViewHolder( )를 리턴(호출) 함.
    }

    override fun onBindViewHolder(holder: ProfileAdapter.CustomViewHolder, position: Int) {
        holder.picture.setImageResource(profileList.get(position).picture)   // (MainActivity에서 데이터를 받아서) profileList에서 손으로 클릭(터치)되는 위치에 대한 정보를 가져옴.
        holder.name.text = profileList.get(position).name             // position 값은 데이터 0 부터 시작 됨.
        holder.telNum.text = profileList.get(position).telNum         // 만약 유형이 Int 숫자라면, 코드 마지막에 숫자를 문자열로 변환하는 코드를( '.toString()' ) 삽입해주면 에러나지 않음.
        holder.email.text = profileList.get(position).email
        holder.birthday.text = profileList.get(position).birthDay
    }

    override fun getItemCount(): Int {
        return profileList.size    // profileList의 총 데이터 갯수를 반환함.
    }
}

 

7.  메인 레이아웃 구성하기 (activity_main.xml)

MainActivity.kt 파일을 열고서, 기본 주어지는 "HolloWorld!" textView를 지우고,  아래처럼, Common 》 RecyclerView를 끌어다 놓으세요.

그리고, Constrait Wideget의 4곳을 모두 클릭해서 parant에 모두 연결시켜 주세요.

리사이클러 뷰의 id는 rv_profile로 할게요. 

 

8.  메인 액티비티 구성하기  (MainActivity.kt)

먼저, findViewById 대신 바인딩을 사용하기 위해, onCreate ( ) 함수 내에 바인딩 선언을 해주세요. 

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

그리고 profileList라는 변수를 만들고 arrayListOf ( )라는 리스트 함수를 사용하여 앞서 정의한 데이터들의 리스트를 담을 거예요. 

이때 앞서 만든 Profile 클래스의 데이터 형식에 맞추어 입력해 주면 됩니다.  인물 Icon 이미지를 10개 준비했으니, 데이터도 우선 10개를 채워볼게요.  class Profile ( 사진, 이름, 전화번호, 이메일, 생일 )  형태로 작성해 주면 됩니다.

val profileList = arrayListOf(
    Profile(R.drawable.woman1, "유관순", "010-0808-1515", "korea@korea.com" , "08.15"),
    Profile(R.drawable.man1, "홍길동", "010-1234-5678", "gildong@chosun.com" , "03.05"),
    Profile(R.drawable.man2, "유재석", "010-7777-8888", "leejs@naver.com" , "07.07"),
    Profile(R.drawable.woman2, "엘사", "010-3333-9999", "elsa@gmail.com" , "12.25"),
    Profile(R.drawable.man3, "모피어스", "010-2222-3434", "morpheus@gmail.com" , "07.12"),
    Profile(R.drawable.man4, "네오", "010-0505-4040", "gildong@chosun.com" , "28.05"),
    Profile(R.drawable.woman3, "지예은", "010-2323-9090", "yeeun@daum.net" , "03.08"),
    Profile(R.drawable.man5, "정상훈", "010-5555-7878", "jeong@snlkorea.com" , "11.30"),
    Profile(R.drawable.woman4, "이수지", "010-3030-7080", "suji@snlkorea.com" , "06.20"),
    Profile(R.drawable.woman5, "김슬기", "010-4747-5656", "seulgi@snlkorea.com" , "28.05"),
)

그리고 레이아웃매니저(layoutManager) 함수를 통해 어댑터가 만든 항목들을 어떻게 배치할지? (가로:HORIZONTAL, 세로:VERTICAL) 정해줍니다. 주로 위아래로 스크롤하는 형태가 되기 때문에, VERTICAL로 해주면 되고요.
이제 제일 중요한, 어댑터를 최초로 사용하게 되는데,  ProfileAdapter( )를 가져와서 위에서 작성한 profileList를 담아 activity_main.xml  레이아웃의 id를 rv_profile이라고 이름 지은 RecyclerView로 바인딩하여 전달(보여주기) 해 주면 됩니다.
이 코드들은 아래와 같아요.

binding.rvProfile.layoutManager = LinearLayoutManager(this, LinearLayoutManager.H VERTICAL, false)
binding.rvProfile.setHasFixedSize(true)  // 리사이클러뷰에 대한 성능개선 관련
binding.rvProfile.adapter = ProfileAdapter(profileList)

그럼,  MainActivity.kt 파일의 완성된 전체 코드를 한 번에 보여드리면 아래와 같아요. 

package com.example.recyclerview3

import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.recyclerview3.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val profileList = arrayListOf(
            Profile(R.drawable.woman1, "유관순", "010-0808-1515", "korea@korea.com" , "08.15"),
            Profile(R.drawable.man1, "홍길동", "010-1234-5678", "gildong@chosun.com" , "03.05"),
            Profile(R.drawable.man2, "유재석", "010-7777-8888", "leejs@naver.com" , "07.07"),
            Profile(R.drawable.woman2, "엘사", "010-3333-9999", "elsa@gmail.com" , "12.25"),
            Profile(R.drawable.man3, "모피어스", "010-2222-3434", "morpheus@gmail.com" , "07.12"),
            Profile(R.drawable.man4, "네오", "010-0505-4040", "gildong@chosun.com" , "28.05"),
            Profile(R.drawable.woman3, "지예은", "010-2323-9090", "yeeun@daum.net" , "03.08"),
            Profile(R.drawable.man5, "정상훈", "010-5555-7878", "jeong@snlkorea.com" , "11.30"),
            Profile(R.drawable.woman4, "이수지", "010-3030-7080", "suji@snlkorea.com" , "06.20"),
            Profile(R.drawable.woman5, "김슬기", "010-4747-5656", "seulgi@snlkorea.com" , "28.05"),
        )
            binding.rvProfile.layoutManager = LinearLayoutManager(this, LinearLayoutManager.H VERTICAL, false)
            binding.rvProfile.setHasFixedSize(true)  // 리사이클러뷰에 대한 성능개선 관련
            binding.rvProfile.adapter = ProfileAdapter(profileList)
    }
}

 

자, 그럼 이 상태로 실행을 해서 확인을 해볼게요. 

에뮬레이터에 뜬 화면을 마우스로 위아래 스크롤 해보면, 준비된 10개의 데이터가 스크롤되면서(VERTICAL) 잘 보이는 걸 알 수 있어요.

네, 대부분 잘 보이는데요,  몇몇 데이터가 Constrait (제약) 연결된 상태에 따라 이름의 길이가 짧거나 하여 이메일 주소와 생일이 겹치는 부분이 보이는데요, 데이터에 따라 크게 겹치지 않도록 Constrait Wedjet에 있는 항목들의 간격을 조정해 보세요. 
 생일의 TextView의 오른쪽이 전화번호의 오른쪽에 연결(연동) 시켜 놓았기 때문에  이름이 짧은 사람은 전화번호가 앞으로 당겨지게 되고 이로인하서 생일도 전화번호의 오른쪽라인으로 기준이 당겨지기 때문에 이메일과 겹치는 부분이 발생하였습니다.

 따라서,  해결방법은 전화번호의 왼쪽이 연결된 이름과의 여백(magin)을 충분히 띄워 주면 됩니다. 

또는 이름의 오른쪽과 연결시킨 부분을 사진의 왼쪽 부분으로 바꾸어 연결시키고 간격을 90sp로 적정하게 벌어지도록 하면,  이름의 길이에 따라 전화번호가 앞뒤는 움직이는 일은 없어서 깔끔하게 정렬된 출력을 볼 수 있어요. 

이렇게 조정 후, 다시 실행시켜 보면, 깔끔하게 정렬된 것을 볼 수 있습니다. 

 

9. 목록 클릭하여 정보 표시하기 

 해당 목록을 클릭하면 Toast기능을 사용하여 간단한 메시지를 출력해 보도록 할게요.

예를 들어, itemView 목록에서 엘사를 터치하면, 엘사의 "이름 [전화번호] , 이메일"을 짧은 메시지 형태로 보여주도록 할게요.

ProfileAdapter.kt  파일을 열고, onCreateViewHolder ( )  함수 부분을 아래와 같이 수정(추가) 해 주세요. 

override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): ProfileAdapter.CustomViewHolder {
    val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)   // parent에 있는 모든 정보(context)를 가져오는 설정 함.
    return CustomViewHolder(view).apply {
        itemView.setOnClickListener {
            val cursorPosition : Int = adapterPosition
            val profile : Profile = profileList.get(cursorPosition)
            Toast.makeText(parent.context, "-이름:${profile.name} [전화번호: ${profile.telNum}] \n-E메일: ${profile.email}", Toast.LENGTH_SHORT).show()
        }
    }
}

그리고 실행해서 터치해 보면, 해당하는 정보를 띄워주는 것을 볼 수 있습니다.

 

10. 목록 클릭하여 전화걸기

마지막으로, 해당 목록을 클릭하면 해당 연락처로 전화를 걸 수 있도록 해서 좀 더 효용성을 높여 봤습니다.

동일한 코드에 아랫부분만 수정해 보세요.

override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): ProfileAdapter.CustomViewHolder {
    val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)   // parent에 있는 모든 정보(context)를 가져오는 설정 함.
    return CustomViewHolder(view).apply {
        itemView.setOnClickListener {
            val cursorPosition : Int = adapterPosition
            val profile : Profile = profileList.get(cursorPosition)
            val intent = Intent(Intent.ACTION_DIAL)
            intent.data = Uri.parse("tel:${profile.telNum}")
            itemView.context.startActivity(intent)
        }
    }
}

앱을 실행시켜 보면,  서로 다른 항목을 클릭할 때마다 그 항목에 있는 전화번호로 전화 걸기가 잘 실행 되는 것을 볼 수 있네요.

※ 주의!  임의로 넣은 번호이지만 실제 해당 번호를 소유하고 있는 사람이 있을 수 있으므로, 실제 테스트하실 때는 가족이나 자신의 번호로 테스트해 보시길 바랍니다!

【 앱 실행 결과 】

① 엘사 터치 후 전화걸기가 실행되는 모습

② 홍길동 터치 후 전화걸기가 실행되는 모습

 

감사합니다.

반응형

【AndroidStudio】 gradle 관련 에러 해결하기!

App개발/Android_Studio 2024. 9. 10. 14:49 Posted by 엑소더스팩토리
반응형

[ 에러 증상과 주요 메시지 ]

안드로이드 스튜디오를 사용하면서, 외부 프로젝트를 Import 시켰는데, 갑자기 아래와 같은 에러가 발생하면서 기존 잘 되던 프로젝트도 실행이 안 되는 일이 발생하였는데요,

android studio  Could not read workspace metadata from ...

android studio Multiple build operations failed.

android studio metadata.bin (지정된 파일을 찾을 수 없습니다)

에러 상황

짐작하기로는 Import한 프로젝트와  현재의 SDK 및 Gradle 버전과 달라서 충돌로 인한 오류가 생긴 것으로 추측을 해보는데요, 

이를 해결하기 위해, 기존 알려진 Invalidate Caches 삭제와 Reload All from Disk , Repair IDE 등등을 실행해 보았지만 해결되지 않았는데요.


결국, 이와 같은 증상의 해결방법은 아래와 같았으니, 같은 에러 증상일 경우 시도해 보세요. 

에러 메시지를 보면, " android studio metadata.bin (지정된 파일을 찾을 수 없습니다) "  부분에 있는 matadata.bin 파일에 문제가 생긴것을 알 수 있는데, 보여지는 경로로 탐색창을 열어 찾아갑니다.

그리고 문제가 된 경로의 폴더 transforms-4를 삭제 해보세요.  어차피 캐시 폴더의 내용들이니 삭제를 해도 문제는 없을 것이고 다시 작성 될 거예요.  삭제를 할 때는 모든 안드로이드 스튜디오 프로젝트를 닫아주세요.

그리고 다시 안드로이드 스튜디오를 실행시키면 아래처럼 잘 실행 되는 것을 볼 수 있습니다. 

오늘은 안드로이드 스튜디오를 사용할 때 외부 프로젝트를 Import 시켜 테스트해볼 때 겪을 수 있는 오류와 이를 해결하는 방법에 대해 다루어 보았습니다. 

감사합니다.

반응형
반응형

▶ 최신 버전의 안드로이드 스튜디오로 앱을 제작 시 둥글면서 보라색 배경의 기본 버튼이 제공됩니다. 하지만 이런 기본 제공 되는 버튼의 스타일이 썩 맘에 들지는 않는데요, 그래서 버튼의 모서리 곡률도 조절해서 사각버튼도 만들고 테두리 두께와 색상도 바꾸고 버튼의 배경 색상을 바꾸어 보는 실습을 준비했습니다.

아래 이미지가 오늘 실습의 결과물입니다.

각각을 버튼으로 만들었으니, 각각의 버튼을 클릭할 때마다 각 버튼의 컬러 코드와, 테두리(Radius) 반지름 값과, 두께를 Toast 함수를 이용해서 메시지로 표시하도록 하였습니다.

 

그럼, 코드 작성에 대해 설명드릴게요.

먼저, 안드로이드 스튜디오에서 새프로젝트를 열고,  Empty Views Activity를 선택해 주세요.

- 그래들 항목에서 build.gradle.kts (Module :app)로 들어가서 viewBinding.isEnabled = true를 추가하고 Sync Now를 적용해 주세요.

res 》 drawable 에 마우스 우클릭(RMB) 하여 New 》 Drawable Resource File로 버튼 설정에 관한 새 리소스 파일들을 만들어 줄게요.

파일 이름은 square_btn1.xml  로 할게요. 

연속해서 square_btn4.xml 까지 총 4개의 파일을 추가해 주세요. (아래 참고)

그럼, 각각의 square_btn1...  파일에 아래와 같이 코드를 입력해 주세요.

[ square_btn1 ]

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- solid로 색상 변경 -->
    <solid android:color="#FFEB3B" />
    <!-- 0dp는 모서리를 사각형으로 만듭니다 -->
    <corners android:radius="80dp" />
    <!-- 테두리 굵기와 색상 주기-->
    <stroke android:width="8dp" android:color="#85790C"/>
</shape>

 

[ square_btn2 ]

<?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android">
        <!-- solid로 색상 변경 -->
        <solid android:color="#FF9800" />
        <!-- 0dp는 모서리를 사각형으로 만듭니다 -->
        <corners android:radius="50dp" />
        <!-- 테두리 굵기와 색상 주기-->
        <stroke android:width="5dp" android:color="#FF5722"/>
    </shape>

 

[  square_btn3 ]

<?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android">
        <!-- solid로 색상 변경 -->
        <solid android:color="#2196F3" />
        <!-- 0dp는 모서리를 사각형으로 만듭니다 -->
        <corners android:radius="25dp" />
        <!-- 테두리 굵기와 색상 주기-->
        <stroke android:width="10dp" android:color="#3F51B5"/>
    </shape>

 

[ square_btn4 ]

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- solid로 색상 변경 -->
    <solid android:color="#aa4444" />
    <!-- 0dp는 모서리를 사각형으로 만듭니다 -->
    <corners android:radius="0dp" />
    <!-- 테두리 굵기와 색상 주기-->
    <stroke android:width="10dp" android:color="#ffaa55"/>
</shape>

 

그리고 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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="5"
    android:layout_margin="10dp"
    tools:context=".MainActivity">
    <Button
        android:id="@+id/btn1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:layout_weight="1"
        android:textStyle="bold"
        android:textColor="#FFFFFF"
        android:textSize="20dp"
        android:text="버튼 스타일 1" />
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btn2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:layout_weight="1"
        android:textStyle="bold"
        android:background="@drawable/square_btn1"
        android:textColor="#000000"
        android:textSize="20dp"
        android:text="버튼 스타일 2" />
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btn3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:layout_weight="1"
        android:textStyle="bold"
        android:background="@drawable/square_btn2"
        android:textColor="#FFFFFF"
        android:textSize="20dp"
        android:text="버튼 스타일 3" />
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btn4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:layout_weight="1"
        android:textStyle="bold"
        android:background="@drawable/square_btn3"
        android:textColor="#FFFFFF"
        android:textSize="20dp"
        android:text="버튼 스타일 4" />
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btn5"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:layout_weight="1"
        android:textStyle="bold"
        android:background="@drawable/square_btn4"
        android:textColor="#FFFFFF"
        android:textSize="20dp"
        android:text="버튼 스타일 5" />
</LinearLayout>

위처럼 코딩하면 보이는 미리 보기 모습입니다. 

버튼의 배경색뿐아니라, 둥근 형태가 기본인 버튼을 조금씩 직사각형태로 radius를 조절해 보았으며, 버튼 테두리의 두께와 색상도 변경해 보았습니다. 

그리고, MainActivity.kt 파일에는 아래처럼 코딩해 보세요.  각각의 버튼을 클릭할 때, 각 버튼의 정보를 Toast 함수를 이용해 메시지로 표시하도록 하였습니다. 

package com.example.btnstyle

import android.os.Bundle
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.btnstyle.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.btn1.setOnClickListener {
            Toast.makeText(this, "초기 설정된 기본 스타일입니다", Toast.LENGTH_SHORT).show()
        }
        binding.btn2.setOnClickListener {
            Toast.makeText(this, "-컬러:#FFEB3B \n-테두리(Radius): 80dp -두께: 8dp", Toast.LENGTH_LONG).show()
        }
        binding.btn3.setOnClickListener {
            Toast.makeText(this, "-컬러:#FF9800 \n-테두리(Radius): 50dp -두께: 5dp", Toast.LENGTH_LONG).show()
        }
        binding.btn4.setOnClickListener {
            Toast.makeText(this, "-컬러:#2196F3 \n-테두리(Radius): 25dp -두께: 10dp", Toast.LENGTH_LONG).show()
        }
        binding.btn5.setOnClickListener {
            Toast.makeText(this, "-컬러:#aa4444 \n-테두리(Radius): 0dp -두께: 10dp", Toast.LENGTH_LONG).show()
        }
    }
}

 

그럼, 실행시켜서 각각의 버튼을 클릭해 보면, 아래처럼 실행되는 것을 볼 수 있습니다.

 

그럼, 프로젝트 파일을 Export해서 올려드릴 테니, 에러나시는 분은 다운로드해서 압축을 풀고 Import 해서 확인해 보세요.( File 》 New 》 Import Project )

BtnStyle.zip
0.11MB

반응형
반응형

이번 시간에는 안드로이드 스튜디오에서 인트로 화면을 만들어 볼 텐데요, 

앱을 시작할 때 몇 초간 인트로 화면이 나오고 이후 메인화면으로 넘어가는 것을 실습해 보겠습니다. 

그럼, 안드로이드 스튜디오에서 New Project 》 Empty Views Activity를 선택해 주세요.

프로젝트가 생성되었으면, 
프로젝트 탐색창의 res 》 layout  폴더를  RMB(마우스 우 클릭)하여 New 》 Activity 》 Empty Views Activity를 추가해 주세요. (아래 참고)

타이틀 제목은 intro로 할게요.

그럼, 아래처럼 인트로 xml 파일과 코틀린 kt (자바는 js) 파일이 함께 만들어진 것을 볼 수 있습니다. (activity_intro.xml  & intro.kt )

그럼, 인트로 화면과 메인 화면을 구분하기 위해, 인트로 화면의 배경색을 기본 흰색에서 적당한 색으로 바꾸고,  intro 표시도 해볼게요.
activity_intro.xml의 코드내용을 아래와 같이 작성해 보세요.

<?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"
    android:background="#D1DB6E"
    tools:context=".intro">

    <TextView
        android:id="@+id/textView"
        android:layout_width="189dp"
        android:layout_height="107dp"
        android:gravity="center"
        android:singleLine="false"
        android:text="안녕하세요~!\n인트로 화면입니다.\n 5초 뒤에 사라집니다"
        android:textColor="#F44336"
        android:textSize="20dp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

그러면, 아래와 같이 보일 거예요.

그리고, 앱을 실행하면 제일 처음 뜨는 화면이 있는데, 기본적으로는 MainActivity로 지정되어 있어서, activity_main.xml 파일이 로드되어 화면에 보이게 되는데요,  우리는 지금 activity_intro.xml 파일을 먼저 띄워 주는 것으로 바꾸어야 하기 때문에, 아래 이미지를 참고해서 변경해 보세요.  app 》 manifests 》 AndroidMainfest.xml  파일을 열어서 
→ activity name이 .intro로 되어 있는 부분의  android:exported="false" 부분을 true로 변경하고 ,   
→ activity name이 .MainActivity로 되어 있는 부분의  android:exported="true" 부분을 false로 변경하면 됩니다.

그리고  .intro 부분과 .MainActivity 부분의 위치를 바꾸어 주세요.   (단, 복붙해서 바꿀 때,  />  와  > 표시가 잘못되지 않도록 주의하세요)

아래는 변경한 모습입니다. 

여기까지 해놓고 실행시켜 보면 인트로 화면이 먼저 뜨는 것을 볼 수 있습니다. 

이제, 인트로 화면이 5초 뒤에 사라지도록 해볼 텐데요,  아래처럼 intro.kt에 코드를 작성해 주세요.

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

        Handler(Looper.getMainLooper()).postDelayed({
            val intent = Intent(this@intro, MainActivity::class.java)
            startActivity(intent)
            finish()
        }, 5000)
    }
}

위 코드에서 마지막에 보이는 5000 수치를 변경하면 시간을 조절할 수 있습니다. (1000 → 1초)

Handler 클래스의 postDelayed 메서드를 이용해서 MainActivity로 이동하도록 하였는데요,  만약, Handler 글자가 빨간색으로 되어 에러 표시가 난다면,  마우스를 Handler 글자 위에 올려놓은 상태에서 Alt+Enter를 눌러 해당 클래스를 import 하여 추가해 주면 됩니다. 

import android.os.Handler

그리고 실행시켜 보면 아래와 같이 화면 전환 되는 것을 볼 수 있습니다. 

 

아래에 전체 코드를 다시 한번 올려놓을 게요.

[ intro.kt ]

package com.example.intropage

import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat

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

        Handler(Looper.getMainLooper()).postDelayed({
            val intent = Intent(this@intro, MainActivity::class.java)
            startActivity(intent)
            finish()
        }, 5000)
    }
}

 

[ AndroidManifest.xml ]

<?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.IntroPage"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="false" />
        <activity
            android:name=".intro"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

[ activity_intro.xml ]

<?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"
    android:background="#D1DB6E"
    tools:context=".intro">

    <TextView
        android:id="@+id/textView"
        android:layout_width="189dp"
        android:layout_height="107dp"
        android:gravity="center"
        android:singleLine="false"
        android:text="안녕하세요~!\n인트로 화면입니다.\n 5초 뒤에 사라집니다"
        android:textColor="#F44336"
        android:textSize="20dp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

 감사합니다.  좋은 하루 보내세요~ 

반응형
반응형

▶ Fragment(프래그먼트) 정의

 프래그먼트는 태블릿과 같은 대화면에서 효율적으로 화면을 구성하기 위해 생긴 구성요소입니다. 하지만 스마트폰과 같은 작은 화면에서도 효율적인 자원 활용과 메뉴 구성을 위해 종종 사용됩니다.

액티비티를 분할하여 화면의 한 부분을 정의하며 액티비티와 같이 레이아웃, 동작 처리, 생명주기를 가지는 독립적인 모듈인데요, 다른 액티비티에서도 사용할 수 있어 재사용성이 뛰어나며 액티비티 실행 중에 추가, 제거가 가능합니다.

프레그먼트는 액티비티의 일부분에만 배치되는 화면 및 동작을 조작하기 위한 객체입니다.  프레그먼트 매니저를 통해서 여러개의 프레그먼트를 조작할 수 있습니다. 레이아웃 xml 파일에서 다른 뷰들과 함께 배치될 수 있습니다.

▶ 프래그먼트 실습 :  버튼을 클릭하면 해당 프래그먼트 화면을 보여주기!

→  3개의 버튼과 3개의 프래그먼트 화면을 각각 만들고 각 버튼들을 클릭하면 각각의 프래그먼트 화면이 로딩 되도록 합니다. 이때 메인 레이아웃(activity_main.xml) 화면에는 FrameLayout을 이용하여 화면을 fragment를 로딩할 View 영역과 버튼을 클릭할 영역으로 구분해서 만들어 봅니다.

 

→ 각각의 프래그먼트 화면을 아래처럼 단순하게 배경색을 변경하여 구성하고, 추후에 이미지를 넣거나 다양하게 응용하면 됩니다.

 

▶  실행 결과 

→ 1번, 2번, 3번 버튼을 클릭하면 아래와 같이 main_activity.xml 레이아웃 화면에 각각의 프래그먼트 화면이 로딩되도록 합니다.

 

▶ 코드 구성

① 먼저 gradle에 있는 build.gradle.kts(Module :app) 파일에 아래처럼 뷰바인딩 사용 선언을 해주세요.


② 아래 이미지를 참고하여 res 》 layout 》 New 》 Fragment 》 Fragment(Blank) 를 클릭하고 타이틀은 frag1으로 입력하여 만들면 fragment_frag1.xml 파일과 함께 frg1.kt 파일이 세트로 만들어집니다. (layout에서 마우스 우클릭하면 New 메뉴가 나타납니다)

같은 방법으로 아래 이미지처럼  fragment_frag2.xml , fragment_frag3.xml ( frg2.kt , frag3.kt)을 만들어 주세요. 

 

→ frag 1의 xml 코드는 아래와 같습니다. (단순하게 배경색 지정하고 TextView를 하나 넣었습니다)

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:background="#2233aa"
    tools:context=".frag1">

    <TextView
        android:id="@+id/txtview_frag1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text= "Frag 1 page"
        android:textSize="20dp"
        android:textStyle="bold"
        android:textColor="@color/white"/>

</FrameLayout>

 

frag1.xml의 뷰화면

→ frag2의 xml 코드 또한 동일하며 배경색과 id 그리고 text 내용만 각각 바꾸어 주세요.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:background="#A51EB4"
    tools:context=".frag2">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text= "Frag 2 page"
        android:textSize="20dp"
        android:textStyle="bold"
        android:textColor="@color/white"/>

</FrameLayout>

frag2.xml의 뷰화면

 

→  frag3의 xml 코드 또한 동일하며 배경색과 id 그리고 text 내용만 각각 바꾸어 주세요.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:background="#FF9800"
    tools:context=".frag3">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text= "Frag 3 page"
        android:textSize="20dp"
        android:textStyle="bold"
        android:textColor="@color/white"/>

</FrameLayout>

frag3.xml의 뷰화면

④ 자동으로 생성된 frag1.kt 프래그먼트의 클래스 함수 코드는 아래와 같습니다.

package com.example.fragment

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

/**
 * A simple [Fragment] subclass.
 * Use the [frag1.newInstance] factory method to
 * create an instance of this fragment.
 */
class frag1 : Fragment() {
    // TODO: Rename and change types of parameters
    private var param1: String? = null
    private var param2: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_frag1, container, false)
    }

    companion object {
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         *
         * @param param1 Parameter 1.
         * @param param2 Parameter 2.
         * @return A new instance of fragment frag1.
         */
        // TODO: Rename and change types and number of parameters
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            frag1().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }
}

위 코드처럼 기본 생성된 코드 그대로 사용해도 실행에 문제는 없는데요,  필요 없는 내용 다 지우고 아래만 남겨 놓으면  됩니다.

package com.example.fragment

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

class frag1 : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_frag1, container, false)
    }
}

frag2.kt  와 frag3.kt 코드도 마찬가지로 정리해 보세요.

 

⑤ activity_main.xml  코드는 아래처럼 작성해 주세요.

<?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"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/main_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/linearLayout"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"></FrameLayout>

    <LinearLayout
        android:id="@+id/btn_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:weightSum="3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/main_view">

        <Button
            android:id="@+id/btn1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_weight="1"
            android:text="1번"
            android:textSize="20dp"
            android:textStyle="bold" />

        <Button
            android:id="@+id/btn2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_weight="1"
            android:text="2번"
            android:textSize="20dp"
            android:textStyle="bold" />

        <Button
            android:id="@+id/btn3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_weight="1"
            android:text="3번"
            android:textSize="20dp"
            android:textStyle="bold" />
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

 

⑥  뷰 바인딩을 적용한  MainActivity.kt의 코드는 아래처럼 해주세요. 

package com.example.fragment

import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.fragment.databinding.ActivityMainBinding
import com.example.fragment.databinding.FragmentFrag1Binding

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)        
        setContentView(binding.root)
        binding.btn1.setOnClickListener {
            supportFragmentManager.beginTransaction().replace(R.id.main_view, frag1()).commit()
        }
        binding.btn2.setOnClickListener {
            supportFragmentManager.beginTransaction().replace(R.id.main_view, frag2()).commit()
        }
        binding.btn3.setOnClickListener {
            supportFragmentManager.beginTransaction().replace(R.id.main_view, frag3()).commit()
        }
    }
}

 

이렇게 까지만 하면 작성이 완료되고 실행해 보면 아래와 같이 실행이 되는 것을 볼 수 있습니다.

 

마지막으로, 살짝 응용해서 TextView 대신 이미지 뷰(ImageView)를 넣어 사진을 보여주는 것으로 바꾸어 볼게요.

그럼, 변경할 곳은 fragment_frag1.xml  파일에서 TextView 대신 아래와 같이 ImageView 를 대체해서 넣어 주면 됩니다.   나머지 fragment_frag2.xml 과 fragment_frag3.xml 파일도 동일하게 바꾸어주세요.

<fragment_frag1.xml 파일 코드>

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:background="#ffaaff"
    tools:context=".frag1">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:src="@drawable/lake1"
        android:background="#22aabb" />

</FrameLayout>

이때, 보여줄 사진으로 호수(lake)사진을 넣었는데요, 첨부해 드릴테니 연습으로 해보세요.

호수(jpg).zip
1.53MB


사진 파일들은  안드로이드 스튜디오의 프로젝트 탐색창에서 ,  res 》 drawable 폴더로 복사해 넣어주세요. 
(아래 참고)  직접 끌어다 넣기하거나 , 복붙해도 됩니다.

아래는, 이미지로 대체해서 실행한 모습입니다. 
사진이 없는 배경의 공간은 기본값인 흰색인데, background 배경의 색을 사진과 잘 어울리게 넣어서 채워봤습니다.

 

그럼, 지금까지 안드로이드 스튜디오에서  kotlin으로 프래그먼트(fragment)를 사용하는 방법과 실제 예시를 설명해 드렸습니다.  몇 번 반복적으로 연습해 보면 금방 응용할 수 있을 거예요. 

감사합니다.

반응형
반응형

▶ 기존 방식 →  findViewById

 안드로이드 스튜디오를 이용한 안드로이드 네이티브 앱개발에서 findViewById 메서드는 리소스 id를 통해서 레이아웃에 있는 뷰 객체들 중 일치하는 뷰를 가져오는 메서드입니다.  그리고 setContentView와 같은 메서드로 xml에 있는 리소스들을 지정한 속성에 맞게 인스턴스를 생성하여 메모리에 로드하는 인플레이션 과정이 필요합니다.  이때 사용하는 메서드가 findViewById인데요, 문제는 가져와야 할 요소가 적을 때는 별문제가 없지만 아무리 간단한 앱이라고 하더라도 페이지 화면과 구성요소들이 증가하게 되면, 이 메서드로 일일이 뷰 객체를 하나씩 지정해서 가져오는 작업이 아주 번거로워집니다.  이에 따라, 성능상으로도 좋지 않기에 kotlin extension, Data Binding, View Binding과 같은 대체 방법들이 추가되었고, 이를 이용해서 앱을 개발하는 것이 좋습니다.

 

▶ viewBinding의 장점

1. findViewById 보다 속도가 상대적으로 빠르다.
2. Binding 지정만 해주면 정확한 view의 타입을 찾아 알아서 맵핑해준다.
3. NullPointerException을 방지 해준다.

 

viewBinding 사용법

① build.gradle.kts (Module:app) 파일을 클릭하여 android {  ...  }  항목 속에 아래코드를 추가해 주세요.

viewBinding.isEnabled = true

android {
    namespace = "com.example.resourcetest"
    compileSdk = 34
    viewBinding.isEnabled = true
    defaultConfig {
        applicationId = "com.example.resourcetest"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

코드 추가 후 상단에 보이는 Sync Now를 클릭하여 적용시켜 주세요.(아래)

 

그리고 바인딩을 참조하려는 layout의 이름 규칙에 따라 binding 객체를 생성해 주세요.  

이름 생성 규칙  ↓ ↓
예시 1) activity_main.xml 을 참조한다면(바인딩)  ActivityMainBinding 으로 이름 지어집니다.
(언더바는 생략하고 단어 첫 글자를 대문자로 한 다음 Binding글자와 함께 모두 이어 붙입니다.

예시 2) page_one.xml 을 참조한다면(바인딩)  PageOneBinding 으로 이름 지어집니다.

따라서 예시 1과 같이 activity_main.xml을 바인딩한다면  MainActivity.kt 파일의 코드를  아래와 같이 구성해 주면 됩니다.

val binding = ActivityMainBinding.inflate(layoutInflater)

그리고 변경하고자 하는 xml 파일의 text 값이 있다면 아래처럼 구성합니다.
만약, activity_main.xml의 textView 객체의 id가 txtview 라면,

binding.txtview.setText("클릭했음")

그러면 textView의 내용이 "클릭했음"으로 변경 되게 됩니다.

아래는 변경한 코드 포함 전체 MainActivity.kt의 코드입니다.  (버튼 1을 눌렀을 때 textView의 내용이 변하는 코드)

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)        
        setContentView(binding.root)
        binding.btn1.setOnClickListener {
        	binding.txtview.setText("클릭했음")
        }

 

반응형
반응형

- 안드로이드 스튜디오와 같은 개발프로그램에서 스마트폰과 연결하는 방법과
  (개발자 모드 활성화 방법)
- USB디버깅 모드가 비활성화 되어 있는 이유와 활성화 방법에 대해 다루어 볼게요

기본적으로 스마트폰에서는 보안관련 안전을 위해 케이블 연결로 안드로이드 스튜디오와 같은 프로그램에서 앱 직접 설치와 같은 접근을 제한하고 있는데요,  이를 해결하기 위해서는 스마트폰에 숨김처리되어 있는 '개발자 옵션'이라는 것을 활성화해주어야 합니다.   

우선 아래 안드로이드 프로그램에서 가상 디바이스로 먼저 기본화면을 동작시켜 볼게요.

 

픽셀이라는 가상 디바이스로 실행이 잘 되었네요.

 

현재 스마트폰이 PC에 연결되어 있으나 장치 목록에는 인식되지 않은 것으로 보이네요.

 

그럼, 스마트폰에서 설정으로 들어가세요.

그런 다음 제일 아래에 있는 '휴대전화 정보'를 터치해 들어가세요.

다음, '소프트웨어 정보'를 터치해주세요.

다음, '빌드번호'를 7회 이상 터치해 주면, 개발자 메뉴가 활성화됩니다.

그럼, 뒤로 가기를 한 번 눌러 제일 아래로 스크롤해 보면, 아래처럼 '개발자 옵션'이 활성화되어 있는 것을 볼 수 있어요.

USB 디버깅 메뉴를 터치해서 USB디버깅 허용 팝업창의 확인 버튼을 터치하고,

곧이어 뜨는 팝업창에서 '이 컴퓨터에서 항상 허용'을 체크하고 허용을 눌러주세요.

이제 안드로이드 스튜디오에서 PC와 연결된 스마트폰 디바이스가 잡히는 것을 볼 수 있습니다.

 

아래는 연결한 갤럭시 스마트폰에 앱설치와 동시에 바로 실행된 모습입니다.

 

※ USB 디버깅 모드가 활성화 자체가 안 되는 경우가 생기는데요, 즉 ON/OFF 자체를 할 수 없는 상황이 생기는데, 그 이유는 기존 연결해서 사용하던 PC가 아닌 노트북이나 다른 PC로 연결해서 사용할 때 이렇게 됩니다. 
이럴 때는 기존 PC에 연결 승인된 권한을 취소해 주어야 새로 연결하는 PC에 다시 권한을 주면서 USB 디버깅 모드를 활성화시킬 수 있게 됩니다. 

 

그럼 다시 "이 컴퓨터에서 항상 허용"에 체크해 주고 확인을 눌러 주면   USB 디버깅이 활성화된 것을 볼 수 있습니다.

 

비록 크게 어려운 부분은 아니지만, 간혹 "USB디버깅" 메뉴 자체가 활성화 안 되어 당황할 수 있기에 이쪽 관련으로 처음 시작하려는 누군가에게는 도움이 되리라 생각하여 게시해 봅니다. 

그럼, 오늘도 좋은 하루 보내세요~ 

https://youtu.be/AWZE7UQLb3Q

 

- YouTube

 

www.youtube.com

 

 

반응형