Post

[안드로이드] 02. Android UI

안드로이드 UI(View)

Event

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1. anonymous nested class로 event handler 구현
binding.eventBtn.setOnClickListener( object: View.OnClickListener { // SAM
    override fun onClick(v: View) {
        Toast.makeText(this@StartActivity, "Hello World", Toast.LENGTH_SHORT).show()
    }
})

// 2. 람다식 적용하기
binding.eventBtn.setOnClickListener({ v: View? ->
    Toast.makeText(this, "Hello World", Toast.LENGTH_SHORT).show()
})

// 3. 최종 코드
binding.eventBtn.setOnClickListener {
    Toast.makeText(this, "Hello World" + it.javaClass.name, Toast.LENGTH_SHORT).show()
}

TextView / ImageView

  • Button
  • EditText
  • ImageView

ViewGroup

1. AdapterView

  • AdapterView
    • Spinner
    • ListView
    • GridView
  • Adapter

    • ListView

      • ArrayAdapter: 기본 문자열을 표현하는 리스트 뷰

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        
        listview1 = findViewById(R.id.listview1)
        // simple_list_item_1: 하나의 데이터를 보이기 적합한 레이아웃
        val adapter1 = ArrayAdapter(this, android.R.layout.simple_list_item_1, strData)
              
        listview1.adapter = adapter1
              
        // setOnItemClickListener: 아이템 클릭 시 이벤트 처리
        listview1.setOnItemClickListener { parent, view, position, id ->
            selectedTv.text = parent.adapter.getItem(position).toString()
            Log.d(TAG, "onCreate: ${parent.adapter.getItem(position)}")
        }
        
      • CursorAdapter

      • SimpleAdapter: Map 등 여러 속성을 갖는 데이터를 표현하는 리스트 뷰

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        
        listview2 = findViewById(R.id.listview2)
        // simple_list_item_2: 두 개의 데이터를 보이기 적합한 레이아웃
        val adapter2 = SimpleAdapter(this
                                     , mapData
                                     , android.R.layout.simple_list_item_2
                                     , arrayOf("id","name")
                                     , intArrayOf(android.R.id.text1, android.R.id.text2))
              
        listview2.adapter = adapter2
              
        listview2.setOnItemClickListener { parent, view, position, id ->
            selectedTv.text = parent.adapter.getItem(position).toString()
            Log.d(TAG, "onCreate: ${parent.adapter.getItem(position)}")
        }
        
      • CustomAdapter: 사용자 정의 화면을 갖는 리스트 뷰

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        
        listview3 = findViewById(R.id.listview3)
        // R.layout.list_item_layout: 각 아이템을 정의한 레이아웃
        val adapter3 = CustomListAdapter(this,  mapData, R.layout.list_item_layout)
              
        listview3.adapter = adapter3
              
        listview3.setOnItemClickListener { parent, view, position, id ->
            selectedTv.text = parent.adapter.getItem(position).toString()
            Log.d(TAG, "onCreate: ${parent.adapter.getItem(position)}")
        }	
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        
        class CustomListAdapter(val context:Context, var items: List<Map<String, String>>, val layout: Int)
            : BaseAdapter(){
              
            override fun getView(p0: Int, p1: View?, parent: ViewGroup?): View {
                // xml -> java로 inflate
                val inflater= LayoutInflater.from(context)
                val view= inflater.inflate(layout, parent, false)
              
                // view에 값 변경
                view.findViewById<TextView>(R.id.tvId).text= items.get(p0).get("id")
                view.findViewById<TextView>(R.id.tvName).text= items.get(p0).get("name")
              
                return view
            }
              
            override fun getCount(): Int {
                return items.size
            }
              
            override fun getItem(p0: Int): Any {
                return items.get(p0)
            }
              
            override fun getItemId(p0: Int): Long {
                return p0.toLong()
            }
        }
        

Android Font, dp, sp

  • Font

    font

    • /res/font에 여러 굵기의 폰트를 삽입

    • .xml로 fontFamily 파일 생성

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      
      <?xml version="1.0" encoding="utf-8"?>
      <font-family 
                   xmlns:android="http://schemas.android.com/apk/res/android"
                   xmlns:app="http://schemas.android.com/apk/res-auto">
          <font
              android:font="@font/poppins_black"
              android:fontStyle="normal"
              android:fontWeight="900"
              app:font="@font/poppins_black"
              app:fontStyle="normal"
              app:fontWeight="900" />
          		
        	...
            	
          <font
              android:font="@font/poppins_regular"
              android:fontStyle="normal"
              android:fontWeight="300"
              app:font="@font/poppins_regular"
              app:fontStyle="normal"
              app:fontWeight="300" />
          
      </font-family>
      
    • 텍스트에 font 적용(fontFamily)

      [적용 결과]

      폰트 적용

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      
      <Button
              android:id="@+id/main_btn"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="main 버튼"
              android:fontFamily="@font/poppins_medium"
              app:layout_constraintBottom_toBottomOf="parent"
              app:layout_constraintEnd_toEndOf="parent"
              app:layout_constraintStart_toStartOf="parent"
              app:layout_constraintTop_toTopOf="parent" />
      
  • dp / sp

    • dp: 픽셀에 독립적인 단위, 어떤 화면의 크기에서도 동일한 크기로 나타난다!
    • sp: dp와 비슷하지만, 사용자가 선택한 글꼴 크기에 의해 크기가 조절됨

Android Layout

  • layout_gravity 속성과 gravitiy 속성

    • layout_gravity

      • 부모 layout 내에서 View 위치가 정렬되는 위치를 결정하는 속성

      • 값의 종류

        상수설명
        Bottom / clip_horizontal사이즈를 바꾸지 않고 객체를 바닥으로 내림
        Center / clip_vertical수직 / 수평 방향의 중앙으로 사이즈를 바꾸지 않고 객체를 이동시킴
        center_horizontal / end사이즈를 바꾸지 않고 수평 방향 가운데로 객체를 위치
        center_vertical /fill사이즈를 바꾸지 않고 수직 방향 가운데로 객체를 위치
        fill_horizontal해당 위젯의 가로를 부모 뷰그룹의 사이즈에 맞게 늘려 채워줌
        fill_vertical해당 위젯의 세로를 부모 뷰그룹의 사이즈에 맞게 늘려 채워줌

        [적용 결과]

        layout_gravity

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        
        <Button
                android:id="@+id/center_btn"
                android:layout_width="300dp"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:gravity="right"
                android:text="안녕하세요.는 right, layout_gravity는 center"/>
              
            <Button
                android:id="@+id/center_horizontal_btn"
                android:layout_width="300dp"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:gravity="right"
                android:text="안녕하세요.는 right, layout_gravity는 center_horizontal"/>
        
    • gravity

      • TextView 위젯 안에 표시되는 텍스트가 TextView 내에서 어디에 정렬될 것인가를 지정하기 위한 속성
      • View 내부에서 남은 공간 활용
  • layout_weight

    • 가중치를 이용한 영역 분할

      [적용 결과]

      layout_weight

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      
      <LinearLayout
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="horizontal"
              android:layout_marginTop="30dp">
          
              <TextView
                  android:id="@+id/text1"
                  ...
                  android:layout_weight="1"/>
          
              <TextView
                  android:id="@+id/text2"
                  ...
                  android:layout_weight="1"/>
          
              <TextView
                  android:id="@+id/text3"
                  ...
                  android:layout_weight="1"/>
          
          </LinearLayout>
      

RelativeLayout / FrameLayout / TableLayout / GridLayout

 레이아웃설명속성
RelativeLayout화면에 이미 배치된 뷰를 기준으로 다른 뷰의 위치를 지정하는 레이아웃 상대 위치를 지정하는 속성 / align 속성
FrameLayoutFrameLayout 레이아웃에 포함된 뷰들을 같은 영역에 겹쳐서 배치할 때 사용z축처럼 해당 공간에서 위로 view가 쌓이는 형태 
TableLayout뷰를 테이블 구조로 나열하는 레이아웃<TableRow>로 개행
, 셀의 개수를 예측할 수 없어서 데이터가 가변적일 때는 화면 구성이 복잡함
 
GridLayout뷰를 테이블 구조로 나열android:coloumnCount로 자동 개행rowCount(최대 행 개수), columnCount(최대 열 개수)

Android Menu

Options Menu

앱 바의 옵션 버튼을 이용하는 메뉴

1) 메뉴 구성을 위한 xml을 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   <menu xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:app="http://schemas.android.com/apk/res-auto">
       <item
           android:id="@+id/example_item"
           android:title="안녕 메뉴아이템"
           android:icon="@drawable/android_icon"
           app:showAsAction="ifRoom"/>
   
       <group android:id="@+id/example_group">
           <item
               android:id="@+id/example_item2"
               android:title="그룹 내부 아이템 1" />
           <item
               android:id="@+id/example_item3"
               android:title="그룹 내부 아이템 2" />
       </group>
       <item android:id="@+id/example_submenu" android:title="상위 메뉴">
           <menu>
               <item android:id="@+id/item_exit" android:title="종료" />
           </menu>
       </item>
   </menu>
   

2) 상단 Toolbar가 보이게끔 Theme 수정

1
2
3
4
   <style name="Base.Theme.Practice" parent="Theme.Material3.DayNight">
       <!-- Customize your dark theme here. -->
       <!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
   </style>

3) onCreateOptionsMenu / onOptionsItemSelected

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   override fun onCreateOptionsMenu(menu: Menu?): Boolean {
           val inflater= menuInflater
           inflater.inflate(R.menu.menu_option, menu)
           return super.onCreateOptionsMenu(menu)
       }
   
   // 전달된 MenuItem의 id로 아이템 구분
   override fun onOptionsItemSelected(item: MenuItem): Boolean {
       if (item.itemId == R.id.item_exit){
           finish()
       } else {
           Toast.makeText(this, "Hello menu, ${item.title}", Toast.LENGTH_SHORT).show()
       }
   
       return super.onOptionsItemSelected(item)
   }

Context Menu

특정 뷰를 이용하는 메뉴, 특정 뷰를 사용자가 길게 눌렀을 때 활성화

  • registerForContextMenu(getListView())
  • onCreateContextMenu()
  • onContextItemSelected()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
override fun onCreate(savedInstanceState: Bundle?) {
        ...

        // long click 시 context 메뉴를 연결할 view 등록
        registerForContextMenu(binding.contextMenuBtn)
}

override fun onCreateContextMenu(menu: ContextMenu?, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) {
        super.onCreateContextMenu(menu, v, menuInfo)

        menuInflater.inflate(R.menu.menu_context, menu)
}

override fun onContextItemSelected(item: MenuItem): Boolean {
        when(item.itemId){
            R.id.context_menu_blue -> binding.contextMenuBtn.setTextColor(Color.BLUE)
            R.id.context_menu_red -> binding.contextMenuBtn.setTextColor(Color.RED)
            R.id.context_menu_green -> binding.contextMenuBtn.setTextColor(Color.GREEN)
        }
        return super.onContextItemSelected(item)
}

특정 뷰를 이용하는 메뉴, 특정 뷰를 눌렀을 때 활성화

  • PopupMenu를 생성, inflate, show()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
binding.popupMenuBtn.setOnClickListener {
    val popupMenu = PopupMenu(applicationContext, it)
    menuInflater.inflate(R.menu.menu_popup, popupMenu.menu)
    popupMenu.setOnMenuItemClickListener { menuItem ->
        when(menuItem.itemId){
            R.id.popup_menu1 -> {
                Toast.makeText(this@MenuActivity, "메뉴 1 클릭", Toast.LENGTH_SHORT).show()
            }
            R.id.popup_menu2 -> {
                Toast.makeText(this@MenuActivity, "메뉴 2 클릭", Toast.LENGTH_SHORT).show()
            }
            else -> {
                Toast.makeText(this@MenuActivity, "메뉴 3 클릭", Toast.LENGTH_SHORT).show()
            }
        }
        false
    }
    popupMenu.show();
}
This post is licensed under CC BY 4.0 by the author.