AppBar 사용

상단 앱 바는 현재 화면의 정보와 작업을 표시하기 위해 앱 창 상단을 따라 일관된 위치를 제공합니다.

상단 앱 바의 예
그림 1. 상단 앱 바의 예

앱 바의 소유권은 앱의 요구사항에 따라 다릅니다. 프래그먼트를 사용할 때 앱 바는 호스트 활동에서 소유하는 ActionBar로 또는 프래그먼트 레이아웃 내의 툴바로 구현될 수 있습니다.

모든 화면에서 항상 상단에 있고 화면과 같은 너비의 동일한 앱 바를 사용한다면 활동에서 호스팅하는 테마 제공 작업 모음을 사용합니다. 테마 앱 바를 사용하면 일관된 디자인을 유지할 수 있고 옵션 메뉴와 위로 버튼을 호스팅하는 위치를 제공할 수 있습니다.

여러 화면에서 앱 바의 크기, 배치, 애니메이션을 더 세밀하게 제어하려면 프래그먼트에서 호스팅하는 툴바를 사용하세요. 예를 들어 접기 방식 앱 바 또는 화면 절반 너비에만 걸치고 세로로 중앙에 위치한 앱 바가 필요할 수 있습니다.

상황에 따라 메뉴를 확장하고 사용자 상호작용에 응답하는 등 다른 방식을 사용해야 합니다. 다양한 접근 방식을 이해하여 앱에 가장 적합한 방식을 사용하면 시간을 절약하고 앱이 제대로 작동할 수 있습니다.

이 주제의 예에서는 수정 가능한 프로필이 포함된 ExampleFragment를 참조합니다. 프래그먼트는 앱 바에서 다음 XML 정의 메뉴를 확장합니다.

<!-- sample_menu.xml -->
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/action_settings"
        android:icon="@drawable/ic_settings"
        android:title="@string/settings"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/action_done"
        android:icon="@drawable/ic_done"
        android:title="@string/done"
        app:showAsAction="ifRoom|withText"/>

</menu>

메뉴에는 프로필 화면으로 이동하는 옵션과 프로필 변경사항을 저장하는 옵션 두 가지가 있습니다.

활동 소유 앱 바

앱 바는 호스트 활동에서 소유하는 경우가 거의 대부분입니다. 활동에서 앱 바를 소유하면 프래그먼트가 프래그먼트 생성 중에 호출된 프레임워크 메서드를 재정의하여 앱 바와 상호작용할 수 있습니다.

활동에 등록

앱 바 프래그먼트가 옵션 메뉴를 채우는 데 참여하고 있다고 시스템에 알려야 합니다. 이렇게 하려면 다음 예와 같이 프래그먼트의 onCreate(Bundle) 메서드에서 setHasOptionsMenu(true)를 호출합니다.

Kotlin

class ExampleFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
    }
}

Java

public class ExampleFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }
}

setHasOptionsMenu(true)는 프래그먼트가 메뉴 관련 콜백을 수신하려고 한다고 시스템에 알립니다. 클릭과 같은 메뉴 관련 이벤트가 발생하면 이벤트 처리 메서드는 먼저 활동에서 호출된 후 프래그먼트에서 호출됩니다.

그러나 애플리케이션 로직에서 이 순서에 의존하지 마세요. 동일한 활동이 여러 프래그먼트를 호스팅하는 경우 각 프래그먼트가 메뉴 옵션을 제공할 수 있습니다. 이 경우 콜백 순서는 프래그먼트가 추가된 순서에 따라 달라집니다.

메뉴 확장

메뉴를 앱 바의 옵션 메뉴에 병합하려면 프래그먼트에서 onCreateOptionsMenu()를 재정의합니다. 이 메서드는 현재 앱 바 메뉴와 MenuInflater를 매개변수로 수신합니다. 메뉴 인플레이터를 사용하여 프래그먼트의 메뉴 인스턴스를 만들어 다음 예와 같이 현재 메뉴에 병합합니다.

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        inflater.inflate(R.menu.sample_menu, menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
       inflater.inflate(R.menu.sample_menu, menu);
    }
}

그림 2는 업데이트된 메뉴를 보여 줍니다.

이제 옵션 메뉴에 메뉴 프래그먼트가 포함됩니다.
그림 2. 이제 메뉴 프래그먼트가 포함되는 옵션 메뉴

클릭 이벤트 처리

옵션 메뉴에 참여하는 모든 활동과 프래그먼트는 터치에 응답할 수 있습니다. 프래그먼트의 onOptionsItemSelected()는 선택된 메뉴 항목을 매개변수로 수신하고 터치가 사용되었는지 나타내는 불리언 값을 반환합니다. 활동이나 프래그먼트가 onOptionsItemSelected()에서 true를 반환하면 참여하는 다른 프래그먼트는 콜백을 수신하지 않습니다.

onOptionsItemSelected() 구현에서 메뉴 항목의 itemId에서 switch 문을 사용합니다. 선택된 항목이 개발자의 항목이라면 터치를 적절하게 처리하고 true를 반환하여 클릭 이벤트가 처리된다고 나타냅니다. 선택된 항목이 개발자의 항목이 아니라면 super 구현을 호출합니다. 기본적으로 super 구현은 false를 반환하여 메뉴 처리를 계속 진행합니다.

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.action_settings -> {
                // Navigate to settings screen.
                true
            }
            R.id.action_done -> {
                // Save profile changes.
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:  {
                // Navigate to settings screen.
                return true;
            }
            case R.id.action_done: {
                // Save profile changes.
                return true;
            }
            default:
                return super.onOptionsItemSelected(item);
        }

    }

}

동적으로 메뉴 수정

버튼을 숨기거나 표시하거나, 아이콘을 변경하는 로직은 onPrepareOptionsMenu()에 배치합니다. 이 메서드는 메뉴가 표시되기 직전에 호출됩니다.

계속해서 이전의 예에서 저장 버튼은 사용자가 수정을 시작할 때까지 표시되지 않아야 하고 사용자가 저장한 후에는 사라져야 합니다. 이 로직을 onPrepareOptionsMenu()에 추가하면 메뉴가 올바르게 표시됩니다.

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onPrepareOptionsMenu(menu: Menu){
        super.onPrepareOptionsMenu(menu)
        val item = menu.findItem(R.id.action_done)
        item.isVisible = isEditing
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onPrepareOptionsMenu(@NonNull Menu menu) {
        super.onPrepareOptionsMenu(menu);
        MenuItem item = menu.findItem(R.id.action_done);
        item.setVisible(isEditing);
    }
}

메뉴를 업데이트해야 하면(예: 사용자가 수정 버튼을 눌러 프로필 정보를 수정하는 경우) 호스트 활동에서 invalidateOptionsMenu()를 호출하여 시스템에서 onCreateOptionsMenu()를 호출하도록 요청합니다. 무효화 시 onCreateOptionsMenu()에서 업데이트를 실행할 수 있습니다. 메뉴가 확장되면 시스템에서는 onPrepareOptionsMenu()를 호출하고 메뉴를 업데이트하여 프래그먼트의 현재 상태를 반영합니다.

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun updateOptionsMenu() {
        isEditing = !isEditing
        requireActivity().invalidateOptionsMenu()
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void updateOptionsMenu() {
        isEditing = !isEditing;
        requireActivity().invalidateOptionsMenu();
    }
}

프래그먼트 소유 앱 바

앱 화면의 대부분에 앱 바가 필요하지 않거나 한 화면에 다른 화면과는 다른 앱 바가 필요하다면 Toolbar를 프래그먼트 레이아웃에 추가하면 됩니다. 프래그먼트의 뷰 계층 구조 내 어디라도 Toolbar를 추가할 수 있지만 일반적으로 화면 상단에 유지합니다. 프래그먼트에서 Toolbar를 사용하려면 다른 뷰에서와 마찬가지로 ID를 제공하고 프래그먼트에서 ID 참조를 가져옵니다. CoordinatorLayout 동작을 사용하여 툴바에 애니메이션을 적용할 수도 있습니다.

<androidx.appcompat.widget.Toolbar
    android:id="@+id/myToolbar"
    ... />

프래그먼트 소유 앱 바를 사용하는 경우 Toolbar API를 직접 사용하는 것이 좋습니다. setSupportActionBar()Fragment 메뉴 API를 사용하지 마세요. 활동 소유 앱 바에만 적합합니다.

메뉴 확장

Toolbar 편의 메서드 inflateMenu(int)는 메뉴 리소스의 ID를 매개변수로 사용합니다. XML 메뉴 리소스를 툴바로 확장하려면 다음 예와 같이 resId를 이 메서드에 전달합니다.

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu);
    }

}

또 다른 XML 메뉴 리소스를 확장하려면 새 메뉴의 resId를 사용하여 메서드를 다시 호출합니다. 새 메뉴 항목이 메뉴에 추가되고 기존 메뉴 항목은 수정되거나 삭제되지 않습니다.

기존 메뉴 세트를 교체하려면 다음 예와 같이 새 메뉴 ID로 inflateMenu(int)를 호출하기 전에 메뉴를 삭제합니다.

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun clearToolbarMenu() {
        viewBinding.myToolbar.menu.clear()
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void clearToolbarMenu() {

        viewBinding.myToolbar.getMenu().clear()

    }

}

클릭 이벤트 처리

setOnMenuItemClickListener() 메서드를 사용하여 툴바에 직접 OnMenuItemClickListener를 전달할 수 있습니다. 이 리스너는 사용자가 툴바의 끝에 표시된 작업 버튼이나 관련 오버플로에서 메뉴 항목을 선택할 때 호출됩니다. 선택된 MenuItem은 리스너의 onMenuItemClick() 메서드에 전달되고 다음 예와 같이 작업을 소비하는 데 사용될 수 있습니다.

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        viewBinding.myToolbar.setOnMenuItemClickListener {
            when (it.itemId) {
                R.id.action_settings -> {
                    // Navigate to settings screen.
                    true
                }
                R.id.action_done -> {
                    // Save profile changes.
                    true
                }
                else -> false
            }
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.setOnMenuItemClickListener(item -> {
            switch (item.getItemId()) {
                case R.id.action_settings:
                    // Navigate to settings screen.
                    return true;
                case R.id.action_done:
                    // Save profile changes.
                    return true;
                default:
                    return false;
            }
        });
    }
}

동적으로 메뉴 수정

프래그먼트가 앱 바를 소유하면 다른 뷰와 마찬가지로 런타임에 Toolbar를 수정할 수 있습니다.

이전의 예에 이어서 설명하자면 저장 메뉴 옵션은 사용자가 수정을 시작할 때까지 표시되지 않아야 하고 옵션을 탭했을 때 다시 사라져야 합니다.

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun updateToolbar() {
        isEditing = !isEditing

        val saveItem = viewBinding.myToolbar.menu.findItem(R.id.action_done)
        saveItem.isVisible = isEditing

    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void updateToolbar() {
        isEditing = !isEditing;

        MenuItem saveItem = viewBinding.myToolbar.getMenu().findItem(R.id.action_done);
        saveItem.setVisible(isEditing);
    }

}

탐색 버튼이 있다면 툴바의 시작 부분에 표시됩니다. 툴바에서 탐색 아이콘을 설정하면 아이콘이 표시됩니다. 다음 예와 같이 사용자가 탐색 버튼을 클릭할 때마다 호출되는 탐색별 onClickListener()를 설정할 수도 있습니다.

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        myToolbar.setNavigationIcon(R.drawable.ic_back)

        myToolbar.setNavigationOnClickListener { view ->
            // Navigate somewhere.
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.setNavigationIcon(R.drawable.ic_back);
        viewBinding.myToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Navigate somewhere.
            }
        });
    }
}