반응형

 

햄버거 버튼 동작이벤트를 넣어보자.

 

1. GNB UI 생성

MainActivity 로 이어질 Home

별도 Activity 로 이어질 버튼을 넣어보겠다

<?xml version="1.0" encoding="utf-8"?>
<layout 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">
    <LinearLayout
        android:id="@+id/ll_menu"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginRight="0dp"
        android:layout_marginLeft="0dp"
        android:orientation="vertical"
        android:background="@color/white">

        <LinearLayout
            android:id="@+id/ll_item_1"
            android:layout_width="match_parent"
            android:layout_height="53dp"
            android:gravity="center_vertical"
            android:paddingLeft="40dp"
            android:paddingRight="20dp"
            android:layout_marginBottom="4dp"
            android:background="@color/home_menu_bg_light_gray"
            android:orientation="horizontal">
            <TextView
                android:id="@+id/tv_item_1"
                android:layout_width="wrap_content"
                android:layout_height="53dp"
                android:layout_gravity="center_vertical"
                android:paddingLeft="0dp"
                android:gravity="center_vertical"
                android:text="Home"
                android:textSize="@dimen/sp_17"
                android:maxLines="1"
                android:textColor="@color/orange"
                android:textStyle="bold" />
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1">
            </View>
            <ImageView
                android:id="@+id/iv_orange_arrow1"
                android:layout_width="7dp"
                android:layout_height="12dp"
                android:layout_marginRight="0dp"
                android:background="@drawable/btn_detail_org"/>
        </LinearLayout>

        <LinearLayout
            android:id="@+id/ll_item_2"
            android:layout_width="match_parent"
            android:layout_height="53dp"
            android:gravity="center_vertical"
            android:paddingLeft="40dp"
            android:paddingRight="20dp"
            android:layout_marginBottom="4dp"
            android:background="@color/home_menu_bg_light_gray"
            android:orientation="horizontal">
            <TextView
                android:id="@+id/tv_item_2"
                android:layout_width="wrap_content"
                android:layout_height="53dp"
                android:layout_gravity="center_vertical"
                android:paddingLeft="0dp"
                android:gravity="center_vertical"
                android:text="기본 Recycler View"
                android:textSize="@dimen/sp_17"
                android:maxLines="1"
                android:textColor="@color/orange"
                android:textStyle="bold" />
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1">
            </View>
            <ImageView
                android:id="@+id/iv_orange_arrow2"
                android:layout_width="7dp"
                android:layout_height="12dp"
                android:layout_marginRight="0dp"
                android:background="@drawable/btn_detail_org"/>
        </LinearLayout>

    </LinearLayout>
</layout>

 

나는 이런식으로 추가해봤다.

메뉴가 몇개안되기때문에 LinearLayout을 사용했음!!

 

2. MainActivity 에 GNB 영역 추가

다른 액티비티에서는 사용하지않고 Main 에 들어왔을때만 보이면 되기때문에 MainActivity 에만 추가하자

<com.example.basic_mobile.ui.TitleBar
        android:id="@+id/inc_titlebar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/title_height"
        app:layout_constraintTop_toTopOf="parent"/>

////추가////
<com.google.android.material.navigation.NavigationView
    android:id="@+id/nav_view"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:orientation="vertical"
    android:visibility="gone"
    tools:ignore="MissingConstraints">

    <com.example.basic_mobile.ui.CustomDrawerMenu
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</com.google.android.material.navigation.NavigationView>

 

 

 

그런데!! 이렇게하면 햄버거버튼 위에 위치하며 버튼이랑 겹쳐진다

각 요소들을 나열해놓기만 하고 줄을 제대로 세우지 않았기때문에 이런식으로 먹혀버린다...

 

 

LinearLayout 으로 서열을 제대로 알려주자

 

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.example.basic_mobile.ui.TitleBar
            android:id="@+id/inc_titlebar"
            android:layout_width="match_parent"
            android:layout_height="@dimen/title_height" />

        <com.google.android.material.navigation.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:orientation="vertical"
            android:visibility="gone">

            <com.example.basic_mobile.ui.CustomDrawerMenu
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

        </com.google.android.material.navigation.NavigationView>
        <Button
            android:id="@+id/btn_recycler"
            android:text="기본 Recycler View"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <Button
            android:id="@+id/btn_epoxy"
            android:text="epoxy View"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toBottomOf="@+id/btn_recycler"/>
    </LinearLayout>


</androidx.constraintlayout.widget.ConstraintLayout>

 

 

이런식으로 ConstraintLayout -> LinearLayout -> 요소 배치 의 방식으로 설정하면 된다.

 

자 이제 보여줄 화면을 세팅해놨다면, 햄버거버튼 클릭시에만 NavigationView 가 visible 되도록 해보자

 

3. GNB 클릭 이벤트 설정

MainActivity 에서 햄버거버튼을 클릭했을때,

메뉴영역이 노출되어있으면 visibility = gone, 미노출되어있으면 visible 상태로 만들어보자.

 

하지만 햄버거버튼, 뒤로가기, 타이틀 로고 이미지클릭시 실행되는 동작들이 Activity 별로 달라야할때가 있다.

즉 include 된 페이지의 이벤트를 부모 페이지에게 알려줘야할때 쓰는 방법을 사용해보겠다

 

1) TitleBar 상수 추가

Title bar 에서 어떤 동작을 수행했는지 알려주기 위해서 상수를 추가했다

class TitleBar : FrameLayout {
    companion object {
        const val BUTTON_NAME_BACK = 1 //뒤로가기
        const val BUTTON_NAME_HOME_MENU = 2 //햄버거버튼
        const val BUTTON_NAME_HOME = 3 //타이틀
    }
    ...
}

 

뒤로가기, 햄버거버튼, 타이틀을 클릭했을때 각 상수값을 부모 페이지로 보내도록 하겠다

class TitleBar : FrameLayout {
    object {
        const val BUTTON_NAME_BACK = 1
        const val BUTTON_NAME_HOME_MENU = 2
        const val BUTTON_NAME_HOME = 3
        const val BUTTON_NAME_CLOSE = 4
    }
...
    private fun initView() {

        //햄버거버튼 클릭 이벤트
        binding?.ivMenu?.setOnClickListener() {
            ///이곳에서 상수값을 보내고 싶음
        }
    }
...
}

 

자 어떻게 보내야할까?

 

내가 택한 방법으로는 DataChangeListener를 이용하는것이다.

 

리스너를 하나 만들고 부모에서 해당리스너를 오버라이딩 시켜서 사용하는 방법이다. 차근차근 해보자

 

2) DataChangeListener 추가

TitleBar 하단에 interface로 리스너를 하나 만들어준다.

그리고 그 안에 메소드를 하나 추가해서 파라미터로 상수값을 제공받는다

class TitleBar : FrameLayout {
    object {
        const val BUTTON_NAME_BACK = 1
        const val BUTTON_NAME_HOME_MENU = 2
        const val BUTTON_NAME_HOME = 3
        const val BUTTON_NAME_CLOSE = 4
    }
...

    private var binding: IncTitlebarBinding? = null
    private var dataChangeListener: DataChangeListener? = null
    
    private fun initView() {

        //햄버거버튼 클릭 이벤트
        binding?.ivMenu?.setOnClickListener() {
            if(dataChangeListener != null)
                this.dataChangeListener?.onChanged(BUTTON_NAME_HOME_MENU,"")
        }
    }
...

    interface DataChangeListener {
        fun onChanged(index: Int,  value : String);
    }

}

 

햄버거버튼을 클릭했을 때, 해당 리스너의 onChanged 메소드로 상수값을 전달한다

그렇다면 이것을 부모가 쓰게끔 컨트롤가능한 메소드를 하나 추가해준다

 

3) 부모페이지 전용 메소드 추가

class TitleBar : FrameLayout {
    object {
        const val BUTTON_NAME_BACK = 1
        const val BUTTON_NAME_HOME_MENU = 2
        const val BUTTON_NAME_HOME = 3
        const val BUTTON_NAME_CLOSE = 4
    }
...

    private var binding: IncTitlebarBinding? = null
    private var dataChangeListener: DataChangeListener? = null
    
    private fun initView() {

        //햄버거버튼 클릭 이벤트
        binding?.ivMenu?.setOnClickListener() {
            if(dataChangeListener != null)
                this.dataChangeListener?.onChanged(BUTTON_NAME_HOME_MENU,"")
        }
    }
...

	//부모 사용 전용 메소드
    fun setOnClickBackBtnListener(listener: (Int, String) -> Unit) {
        this.dataChangeListener = object : DataChangeListener {
            override fun onChanged(index: Int, value: String) {
                listener(index, value)
            }
        }
    }

    interface DataChangeListener {
        fun onChanged(index: Int,  value : String);
    }

}

 

이렇게 메소드를 만들어서 뚫어줘야 부모에서 사욯할 수 있다.

 

전체 코드는 이렇다

class TitleBar : FrameLayout {
    companion object {
        const val BUTTON_NAME_BACK = 1
        const val BUTTON_NAME_HOME_RIGHT_SIDE = 2
        const val BUTTON_NAME_HOME = 3
        const val BUTTON_NAME_CLOSE = 4
    }

    constructor(context: Context) : super(context) {
        inflateView()
        initView()
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        inflateView()
        initView()
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        inflateView()
        initView()
    }

    private var binding: IncTitlebarBinding? = null
    private  var dataChangeListener: DataChangeListener? = null

    private fun inflateView() {
        binding = IncTitlebarBinding.inflate(LayoutInflater.from(context))
        addView(binding?.root)
    }

    private fun initView() {

        //클릭이벤트
        binding?.ivMenu?.setOnClickListener() {
            if(dataChangeListener != null)
                this.dataChangeListener?.onChanged(BUTTON_NAME_HOME_RIGHT_SIDE,"")
        }
    }

    /**
     * set title (TEXT)
     */
    fun setTitle(title:String) {
        if (title.isNullOrEmpty()) {
            binding?.tvTitle?.hide()
        } else {
            binding?.tvTitle?.show()
            binding?.tvTitle?.text = title
        }
    }

    fun showMenuIcon(b: Boolean) {
        if (b) {
            binding?.ivMenu?.show()
        } else {
            binding?.ivMenu?.hide()
        }
    }

    fun setOnClickBackBtnListener(listener: (Int, String) -> Unit) {
        this.dataChangeListener = object : DataChangeListener {
            override fun onChanged(index: Int, value: String) {
                listener(index, value)
            }
        }
    }

    interface DataChangeListener {
        fun onChanged(index: Int,  value : String);
    }

}

 

 

4.  Main Activity 에서 리스너 사용

setTitleBar 부분에서 아래와같이 해당 메소드를 이용할 수 있다

class MainActivity : AppCompatActivity() {

    private var binding: ActivityMainBinding? = null
    lateinit var drawerLayout: DrawerLayout

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding!!.root)
        setTitleBar()
    }

    private fun setTitleBar () {
        binding?.incTitlebar?.setTitle("MAIN")
        binding?.incTitlebar?.showMenuIcon(true)
        binding?.incTitlebar?.setOnClickBackBtnListener { i, s ->
            when (i) {
                TitleBar.BUTTON_NAME_HOME -> {} //로고 클릭 event
                TitleBar.BUTTON_NAME_BACK -> {} //뒤로가기 event
                TitleBar.BUTTON_NAME_HOME_MENU -> { //햄버거버튼 event
                    if (binding?.navView?.isVisible == true) {
                        binding?.navView?.hide()
                    } else {
                        binding?.navView?.show()
                    }

                }
            }
        }
    }

}

 

뭐 모든 페이지가 같은 동작을 수행한다면 굳이 이럴필요는 없지만 

아닌경우가 많기때문에.. 해당방법을 사용했다.

 

중심적으로 봐야할점은 부모로 이벤트를 보내는 방법!! 나중에 fragment 에서 리스트클릭하거나 다양한 이벤트를 보낼때 이러한 방식을 사용하게된다.

 

완성본!

 

 

 

 

 

728x90
반응형

1. 타이틀 UI 생성

 

보편적인 안드로이드 타이틀 모양을 제작해보자

위치는 layout 폴더 안에 inc_titlebar 라는 이름의 파일을 만들것이다

inc 의 의미는 include 를 할 파일이라는 의미인데, 액티비티에 타이틀 연결할때 예제를 봐보자

 

타이틀 디자인은 왼쪽에 햄버거 버튼이 있고, 가운데에 타이틀 텍스트가 오도록 한다

 

1) 타이틀 xml 작성

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/layout_title"
            android:layout_width="match_parent"
            android:layout_height="@dimen/title_height"
            android:layout_marginLeft="12dp"
            android:layout_marginRight="12dp">

            <!-- back button -->
            <ImageView
                android:id="@+id/iv_back"
                style="@style/AppTheme.Button.SmallIcon.titleBar"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_gravity="center_vertical|left"
                android:visibility="gone"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/btn_back" />

            <ImageView
                android:id="@+id/iv_menu"
                style="@style/AppTheme.Button.SmallIcon.menu"
                android:layout_width="@dimen/title_height"
                android:layout_height="match_parent"
                android:layout_gravity="center_vertical"
                android:visibility="gone"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/ic_menu" />

            <!-- title -->
            <TextView
                android:id="@+id/tv_title"
                style="@style/AppTheme.Text.Title"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginLeft="60dp"
                android:layout_marginRight="60dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:text="title" />

            <!-- border line -->
            <View
                android:id="@+id/vw_line"
                android:layout_width="match_parent"
                android:layout_height="8dp"
                android:layout_gravity="bottom"
                android:background="@drawable/gradient_stroke_gray"
                app:layout_constraintBottom_toBottomOf="parent" />

        </androidx.constraintlayout.widget.ConstraintLayout>
    </LinearLayout>

</layout>

 

햄버거버튼부분은, 뒤로가기 이미지가 올때도 있어야 하므로 ImageView 두개를 넣어뒀다.

@Style, @drawable, @dimen 부분은 사용성을 좋게하기위해 했지만 안해도되고, 직접 속성을 설정해줘도 되므로 생략!

 

자세한 코드는 아래 깃주소 참고

https://github.com/Daseul727/mobile_sample.git

 

2) 타이틀 소스코드 작성

TitleBar.kt 파일을 만들어주자

 

아래 소스코드 중 FrameLayout 을 import 하면 기본 constructor 세개를 넣어야지 오류가 발생하지않는다.

class TitleBar : FrameLayout {

    constructor(context: Context) : super(context) {
        inflateView()
        initView()
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        inflateView()
        initView()
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        inflateView()
        initView()
    }

    private var binding: IncTitlebarBinding? = null

    private fun inflateView() {
        binding = IncTitlebarBinding.inflate(LayoutInflater.from(context))
        addView(binding?.root)
    }

    private fun initView() {

        //클릭이벤트

    }

    /**
     * set title (TEXT)
     */
    fun setTitle(title:String) {
        if (title.isNullOrEmpty()) {
            binding?.tvTitle?.hide()
        } else {
            binding?.tvTitle?.show()
            binding?.tvTitle?.text = title
        }
    }

    fun showMenuIcon(b: Boolean) {
        if (b) {
            binding?.ivMenu?.show()
        } else {
            binding?.ivMenu?.hide()
        }
    }
}

 

initView 쪽에는 햄버거버튼 클릭이벤트를 달아주면 좋을것같고

나머지 하단의 function 들은, 외부에서 햄버거버튼을 보여줄지? 타이틀을 숨질지 동작을 위한 메소드이다.

 

3) Activity 에 타이틀 추가

 

MainActivity 에 타이틀을 추가해보겠다.

<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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

////추가////
    <com.example.basic_mobile.ui.TitleBar
        android:id="@+id/inc_titlebar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/title_height"
        app:layout_constraintTop_toTopOf="parent"/>
////////

    <Button
        android:id="@+id/btn_recycler"
        android:text="기본 Recycler View"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:id="@+id/btn_epoxy"
        android:text="epoxy View"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btn_recycler"/>

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

위와같은 방법으로 TitleBar 를 include 한다

 

그리고 Activity 에서 햄버거버튼 및 title 의 text 를 설정한다

 

class MainActivity : AppCompatActivity() {

    private var binding: ActivityMainBinding? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        ////추가////
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding!!.root)
        setTitleBar()
    }

    private fun setTitleBar () {
        binding?.incTitlebar?.setTitle("MAIN")
        binding?.incTitlebar?.showMenuIcon(true)
    }
}

 

이렇게하면 타이틀 추가 완료

 

728x90
반응형

앱을 실행시키면 Activity가 띄워질때까지 흰 화면이 보일것이다.

이것을 감추기위하여 splsh 화면을 추가해보자.

 

순서는 새로운 splash 전용 새로운 액티비티를 생성하고 해당 액티비티에 theme 로 splash 이미지를 걸어둘것이다.

 

1. 이미지 다운로드 및 추가

난 적당히 로고로 사용할 이미지를 다운받았다

https://pixabay.com

 

이미지를 프로젝트의 res > drawable 폴더에 추가한다

 

2. 로고 이미지 resource 화

이미지를 그대로 사용하기보다는 themes 에 추가하여 사용하는것이 재사용성에 좋다

 

우선 drawable 우클릭 -> New -> Drawable Resource File 선택 후 file name 을 splash.xml 로 설정한다

 

 

아래와 같이 코드를 바꿔준다.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/white" />
        </shape>
    </item>

    <item android:bottom="0dp" android:right="30dp">
        <bitmap
            android:gravity="center_vertical|right"
            android:src="@drawable/logo" />
    </item>

</layer-list>

 

이미지를 xml resource 로 바꿔준것이며 바탕화면을 하얀색으로 바꾸고 이미지를 추가해주었다.

 

3. Themes에 해당 resource 추가

 

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Base.Theme.Basic_mobile" parent="Theme.Material3.DayNight.NoActionBar">
        <!-- Customize your light theme here. -->
        <!-- <item name="colorPrimary">@color/my_light_primary</item> -->
    </style>

    <style name="Theme.Basic_mobile" parent="Base.Theme.Basic_mobile" />

////추가////
    <style name="AppTheme" parent="Theme.MaterialComponents.Light.Bridge">
        <item name="android:textSize">18dp</item>
    </style>
 ////추가////
    <style name="AppTheme.Splash">
        <item name="android:windowBackground">@drawable/splash</item>
    </style>

</resources>

 

4. Entry Activity 추가

empty activity 를 추가해주자. 파일명은 EntryActivity 이며 activity_entry.xml 은 건들지말고 EntryActivity.kt 파일만 수정한다

class EntryActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        goPageIntro()
    }

    fun goPageIntro() {
        Handler(Looper.getMainLooper()).postDelayed({
            val intent = Intent(this, IntroActivity::class.java)
            startActivity(intent)

            finish()
        },1500) //여기숫자로 splash 화면 초 설정 가능
    }
}

 

해당 액티비티에 들어오면바로 IntroActivity 로 이동하게 하는 로직을 추가했다.

 

5. AndroidManifest 수정

 

이렇게 만든 EntryActivity 가 앱 첫실행 시 로드되도록 AndroidManifest를 수정해보자

<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.Basic_mobile"
        tools:targetApi="31" >
        <activity
            android:name=".IntroActivity"
            android:exported="false" />
        <activity
            android:name=".MainActivity"
            android:exported="false" />
        <activity
            android:name=".EntryActivity" ////수정////
            android:exported="true"
            android:theme="@style/AppTheme.Splash" > ////수정////
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

 

이렇게 하면 logo 이미지를 resource 화 시키고 theme 에 등록시켜서 android:theme 으로 불러오기까지 완성!

 

 

 

추가적으로 오류사항을 발견했다. splash 화면을 넣었음에도, 상단에 basic_mobile 이라는 app name이 노출된다.

app name을 없애보자

 

6. app name 제거

themes.xml 파일을 수정해보자

 

....

    <style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

    <style name="AppTheme.NoActionBar.Splash">
        <item name="android:windowBackground">@drawable/splash</item>
    </style>
....

 

windowActionBar , windowNotitle 속성을 추가했다.

그리고 겸사겸사 다른이미지로 바꿈...

이렇게하면 더이상 app name이 보이지않는다.

 

 

 

728x90
반응형

 

예제로, 특정 버튼을 클릭하면 홈화면이 뜨도록 해보자.

나는 IntroActivity 를 생성하고 버튼을 클릭하면 MainActivity 가 뜨도록 수정할것이다.

 

1. Intro Activity 생성

지난 포스팅에서와 같이, Activity 는 화면이라고 이해하면 되겠다.

 

MainActivity 의 상위 패키지에서 New > Activity > Empty Views Activity 를 선택한다

 

새로운 Activity 이름은 IntroActivity 로 설정하자

Layout Name 은 자동으로 바뀔텐데 혹시 자동으로 바뀌지않았다면 activity_intro 라고 입력해주자

 

2. Intro 꾸미기

 

아래와 같이 깔끔하게 기본세팅해준다

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

</layout>

 

이곳이 Intro Activity 임을 알수있게 꾸며주자.

 

간단하게 텍스트를 추가해보겠다. 웹에서는 <input> 태그를 사용했다면, 안드로이드에서는 EditText, 혹은 TextView 를 사용한다.

EditText는 수정가능한 <input> 이고, TextVeiw 는 수정불가한 <p> 태그와 비슷하다

 

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

////추가////
    <TextView
        android:text="This is Intro"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</layout>

 

이렇게하면 우측 미리보기화면에 글자가 나와있는것을 볼 수 있다.

만약 layout_width, height 를 설정하지않으면 글자가 안보인다. 해당 설정을 해줘야만 보인다

 

여기에, 버튼을 하나 추가해보자.

<?xml version="1.0" encoding="utf-8"?>
<layout 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">
    <TextView
        android:text="This is Intro"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

///추가/////
    <Button
        android:text="버튼"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</layout>

 

이상하게도 버튼이 보이지않는다.

이럴때 필요한게 웹에서의 <li> 와 같은 기능이다.

 

안드로이드는 여러가지 요소를 세로, 혹은 가로로 나타나고싶을때 부모태그를 추가해줘야한다.

 

ConstraintLayout 을 쓰면 자동완성으로 아래와 같은 태가 보인다.

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

////추가/////
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:text="This is Intro"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

        <Button
            android:text="버튼"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

width, height 를 match_parent로 해준다. 높이 넓이를 부모요소와 같게 하겠다는 뜻이다.

나는 텍스트와 버튼을 세로정렬로 해주고싶어서 orientation = vertical 인데, 세로정렬은 horizontal 이다

 

하지만 이렇게하면 버튼이 TextView를 덮어버리므로 버튼을 맨 아래로 보내고 TextView 를 정가운데로 보내보자.

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:text="This is Intro"
            android:layout_width="wrap_content" ////수정////
            android:layout_height="wrap_content" ////수정////
            app:layout_constraintTop_toTopOf="parent" ////추가////
            app:layout_constraintBottom_toBottomOf="parent" ////추가////
            app:layout_constraintStart_toStartOf="parent" ////추가////
            app:layout_constraintEnd_toEndOf="parent"/> ////추가////

        <Button
            android:text="버튼"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"/> ////추가////
    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

 

이렇게 인트로 화면을 만들었다.

TextView 를 wrap_content 로 바꾼이유는, 상위요소 기준으로 가운데로 보내야되는데 넓이가 부모만큼 크면 어디로 보내야할지 모르게 된다.

 

그래서 TextView 범위는 content 를 감쌀정도, 작게 만들고 상위요소를 기준으로 해당 컨텐츠 위치를 조정하는 의미이다

 

 

3. Main Activity 로 이동하기

버튼을 누르면 MainActivity 로 이동시켜주려고 한다.

그러려면 버튼에 클릭이벤트를 주어야한다.

 

우선 버튼에 id를 할당하자.

//activity_intro.xml

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:text="This is Intro"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>

        <Button
            android:id="@+id/btn_go_main" //// 추가 ////
            android:text="버튼"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"/>
    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

 

해당 xml 의 이벤트등록 및 데이터바인딩은 모두 activity_intro.xml 의 짝꿍 IntroActivity.kt 에서 할수있다.

//introActivity

class IntroActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_intro)
        
        //버튼 정의
        val goMainBtn = findViewById<Button>(R.id.btn_go_main)
        
        //버튼 클릭 이벤트
        goMainBtn.setOnClickListener {
            val intent = Intent(this, MainActivity::class.java)
            startActivity(intent)
        }
        
    }
}

 

버튼 정의 부분을 보면

findViewById<Button>(R.id.btn_go_main)

 

findViewById  : id를 기준으로 찾을건데

<Button> : 버튼 요소이며

R.id.btn_go_main : id 는 btn_go_main 

 

버튼을 goMainBtn 이라는 변수에 넣고 클릭이벤트를 준것이다.

 

Activity 간의 이동을 할때는 intent 정의 후 startActivity 로 이동한다. 이것은 공식!

 

외전) 버튼 정의 우아하게 하기

버튼정의할때 위의 방법은 기초적인 방법이고...실무에서 자주 쓰이는 binding 방법으로 수정하

 

1) build.gradle 수정

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}
android {
    namespace 'com.example.basic_mobile'
    compileSdk 34
...
..
.

    kotlinOptions {
        jvmTarget = '1.8'
    }
    
    buildFeatures { ////추가////
        viewBinding = true
        dataBinding true
    }
}

 

buildfeatrures 를 추가해준다. 이것은 데이터바인딩을 하겠다는 의미.

build.gradle 수정하고 Sync now 를 클릭해준다.

 

2) IntroActivity 수정

class IntroActivity : AppCompatActivity() {

    private lateinit var binding : ActivityIntroBinding ////추가////
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

         ////수정////
        //binding 초기화 
        binding = ActivityIntroBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        //버튼 정의
        val goMainBtn = binding.btnGoMain ////수정////

        //버튼 클릭 이벤트
        goMainBtn.setOnClickListener {
            val intent = Intent(this, MainActivity::class.java)
            startActivity(intent)
        }
    }

}

 

binding 이라는 변수를 추가해주고, activity_intro.xml과 연결시킨다.

 

본인의 나는 activity_intro.xml 이라서 ActivityIntroBinding 이지만

만약 activity_sample,xml 이라면 ActivitySampleBinding 일것이다.

 

어짜피 자동완성되므로 괜춘.

 

4. 앱 실행 시 Intro Activity 실행

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.Basic_mobile"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity" //// 수정 ////
            android:exported="false" />
        <activity
            android:name=".IntroActivity" //// 수정 ////
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

 

 

이렇게하면, 앱 첫진입은 intro에서하고 버튼누르면 MainActivity 로 페이지 이동하는것을볼 수 있다.

 

 

 

만약 에러가 발생한다면 Logcat 을 확인하자

좌측 상단에 현재 내 에뮬레이터 기기가 맞는지 유의할것!!

 

디버깅이 필요하다면 벌레모양 클릭~

 

원하는 코드에 breack point 생성~

 

 

최종적으로 아래와 같은 동작을 확인할 수 있다.

728x90
반응형

1.  프로젝트 생성

안드로이드 스튜디오 -> New Project -> Empty Views Activity 

 

2. 화면 생성

Empty View Activity 로 프로젝트를 생성하면 아래와 같은 프로젝트를 확인할 수 있다.

 

 

자 이제 그러면 이 괴상하게 생긴 프로젝트를 뜯어보자.

 

애뮬레이터관련 세팅은 모두 완료했다고 가정하고, 프로젝트를 시작시키면 hello world 페이지가 나온다.

이 페이지는 어떻게 나온걸까?

 

 

1) activity_main.xml

바로 이곳을 통해서 나왓다. 이곳은 프로젝트를 생성하면 기본적으로 주어지는 페이지이다.

 

아래와 같은 파일이 보일텐데 겁먹지말고 우측의 Split 을 눌러주자.

 

 

그러면 아래와 같이 코딩을 할 수 있는 화면과 우측의 미리보기 화면이 제공된다

 

앞으로 개발할때는 이 화면을 기본으로 두고 개발하면 된다.

 

왼쪽 코드영역에서 TextView 태그를 자세히 보면 android:text = "Hello World!" 가 보일텐데, 이것이 앱에 보이는 글씨다.

 

그렇다면 어떤 원리로 이 화면이 나오게 되는걸까?

 

2) MainActivity.kt

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

 

onCreate 라는 함수를 통해서 View 즉 화면을 생성한다.

 

create 를 할건데, 어떤 view를 보여줄것이냐? setContentView( ) 괄호를 보여줄것이다. 

괄호에 들어가는 파라미터는 R.layout. ~~ 이런 형식인데 R은 root 라고 생각하자.

 

프로젝트 root 에서 layout 폴더에 들어있는 xml들을 해당 파라미터에 넣을 수 있다.

현재는 activity_main 밖에없으므로 R.layout. 을 입력하면 자동완성으로 acitivity_main 이 보여질것이다.

(activity_main 을 Ctrl +우클릭 혹은 Ctrl + Alt 해보면 activity_main.xml로 이동됨)

 

이렇게 activity 를 통해서 view 화면이 앱에 보여지는데 정작 MainActivity는 어디서 실행되는걸까?

 

3) AndroidManifest.xml

 

이곳은 안드로이드 앱권한, intro 화면 설정, Activity 종류를 나열하는 곳이다.

새로운 Activity 를 생성하면 이곳에도 추가된다.

 

<activity> 태그를 보면 name 으로 MainActivity 가 적혀있는데, 이 태그는 MainActivity 에 대한 설정이라는 뜻

그리고 intent-filter 로

<action android:name="android.intent.action.MAIN" />

 

이것은 앱을 실행 시켰을 때 처음으로 진입하는 진입점이라는 표시다.

 

즉 해석하자면, MainActivity 가 있으며 이것은 앱을 실행했을때 가장 처음으로 노출되는 화면이라는 뜻이다.

 

 

3. 구조 정리

 

이렇게 프로젝트 생성과 어떻게 앱 화면이 노출되는지 순서대로 살펴보았다.

 

주요 개념은 아래와 같다.

 

build.gradle

dependencies 추가

 

manifests

앱권한, intro, mainActivity, splash 화면 설정

 

java

Activity 파일 위치하며, 로직을 작성하는 곳

 

res

이미지, 레이아웃, 화면을 그리는 xml 파일 등 resources가 위치함

 

728x90

+ Recent posts