활동 삽입은 애플리케이션의 작업 창을 두 개의 활동 또는 동일한 활동의 두 개의 인스턴스로 분할하여 대형 화면 기기에서 앱을 최적화합니다.
앱이 여러 활동으로 구성된 경우 활동 삽입을 사용하면 태블릿, 폴더블, ChromeOS 기기에서 향상된 사용자 환경을 제공할 수 있습니다.
활동 삽입에는 코드 리팩터링이 필요하지 않습니다. XML 구성 파일을 만들거나 Jetpack WindowManager API를 호출하여 앱이 활동을 표시하는 방법, 즉 나란히 표시할지 스택으로 표시할지를 결정합니다.
작은 화면 지원은 자동으로 유지됩니다. 화면이 작은 기기에 앱이 있는 경우 활동이 서로 스택됩니다. 대형 화면에서는 활동이 나란히 표시됩니다. 시스템에서 만들어 놓은 구성을 기반으로 하는 프레젠테이션(브랜치 로직 없음) 필요합니다.
활동 삽입은 기기 방향 변경을 지원하고 폴더블 기기에서 원활하게 작동하여 기기를 접거나 펼칠 때 활동을 스택하고 스택 해제합니다.
활동 삽입은 Android 12L을 실행하는 대부분의 대형 화면 기기에서 지원됩니다. (API 수준 32) 이상
작업 창 분할
활동 삽입은 앱 작업 창을 기본 컨테이너와 보조 컨테이너로 분할합니다. 컨테이너는 기본 활동에서 실행된 활동, 또는 이미 컨테이너에 있는 다른 활동에서 실행된 활동을 보유합니다.
활동이 실행되면 보조 컨테이너에 스택되고, 작은 화면에서 보조 컨테이너는 기본 컨테이너 위에 스택됩니다. 따라서 활동 스택 및 뒤로 탐색이 앱에 이미 내장된 활동의 순서와 일치합니다.
활동 삽입을 사용하면 다양한 방식으로 활동을 표시할 수 있습니다. 내 앱이 두 활동을 나란히 실행하여 작업 창을 분할할 수 있음 동시에 사용할 수 있습니다.
또는 전체 작업 창을 차지하고 있는 활동은 새 활동을 나란히 실행하여 분할을 만들 수 있습니다.
이미 분할에 표시되며 작업 창을 공유하는 활동은 다음과 같은 방법으로 다른 활동을 실행할 수 있습니다.
측면으로, 다른 활동 위:
측면으로, 분할의 좌우를 전환하여 이전 기본 활동을 숨김:
기존 위치의 위에(동일한 활동 스택) 활동 실행:
<ph type="x-smartling-placeholder">동일한 작업에서 활동 전체 크기 창 실행:
뒤로 탐색
다양한 유형의 애플리케이션은 분할 작업 창 상태에서 활동 간의 종속성이나 사용자가 뒤로 이벤트를 트리거하는 방식(아래의 예)에 따라 서로 다른 뒤로 탐색 규칙을 가질 수 있습니다.
- 함께 모이기: 활동이 관련이 있고 표시되지 않아야 하는 경우 둘 다 종료하도록 뒤로 탐색을 구성할 수 있습니다.
- 단독: 활동이 완전히 독립적인 경우 한 활동의 뒤로 탐색이 작업 창에 있는 다른 활동의 상태에 영향을 주지 않습니다.
버튼 탐색을 사용할 때 마지막으로 포커스가 맞춰진 활동으로 뒤로 이벤트가 전송됩니다.
동작 기반 탐색:
Android 14(API 수준 34) 이하: 동작이 발생한 활동으로 뒤로 이벤트가 전송됩니다. 사용자가 화면 왼쪽에서 스와이프하면 뒤로 이벤트가 분할 창의 왼쪽 창에 있는 활동으로 전송됩니다. 사용자가 앱의 오른쪽에서부터 스와이프하면 화면에서 뒤로 이벤트가 오른쪽 창의 활동으로 전송됩니다.
Android 15 (API 수준 35) 및 이후 버전
동일한 앱의 여러 활동을 처리할 때 이 동작은 스와이프 방향과 관계없이 상단 활동을 종료하여 더 통합된 환경을 제공합니다.
서로 다른 앱 (오버레이)의 두 활동이 관련된 시나리오에서는 뒤로 이벤트가 포커스된 마지막 활동으로 전달되며, 버튼 탐색 동작입니다.
다중 창 레이아웃
Jetpack WindowManager를 사용하면 활동 삽입 다중 창을 빌드할 수 있습니다.
Android 12L (API 수준 32) 이상을 사용하는 대형 화면 기기의 레이아웃
이전 플랫폼 버전이 설치된 일부 기기 앱을 기반으로 한
프래그먼트나 뷰 기반 레이아웃(예:
SlidingPaneLayout
는 개선된 대형 화면 사용자 환경을 제공할 수 있습니다.
코드를 리팩토링할 수 있습니다
한 가지 일반적인 예는 목록-세부정보 분할입니다. 높은 품질의 표현을 위해 시스템에서 목록 활동을 시작한 후에 애플리케이션이 즉시 세부정보 활동을 시작합니다. 전환 시스템은 두 활동이 모두 그려질 때까지 대기했다가 두 활동을 함께 표시합니다. 두 활동은 사용자의 관점에서 하나로 실행됩니다.
속성 분할
분할 컨테이너 간 작업 창의 비율과 컨테이너가 다른 컨테이너에 대해 배치되는 방식을 지정할 수 있습니다.
XML 구성 파일에 정의된 규칙의 경우 다음 속성을 설정해야 합니다.
splitRatio
: 컨테이너 비율을 설정합니다. 값은 개구간 (0.0, 1.0) 내의 부동 소수점 숫자입니다.splitLayoutDirection
: 분할 컨테이너가 다른 컨테이너에 대해 배치되는 방식을 지정합니다. 이 속성에는 다음과 같은 값이 있습니다.ltr
: 왼쪽에서 오른쪽rtl
: 오른쪽에서 왼쪽locale
: 언어 설정에 따라ltr
또는rtl
로 결정됨
예시는 XML 구성 섹션을 참고하세요.
WindowManager API를 사용하여 만든 규칙의 경우 SplitAttributes
를 만듭니다.
SplitAttributes.Builder
로 객체를 설정하고 다음 빌더를 호출합니다.
메서드:
setSplitType()
: 분할 컨테이너의 비율을 설정합니다. 자세한 내용은 다음을 포함한 유효한 인수의 경우SplitAttributes.SplitType
SplitAttributes.SplitType.ratio()
메서드를 사용하여 지도 가장자리에 패딩을 추가할 수 있습니다.setLayoutDirection()
: 컨테이너의 레이아웃을 설정합니다. 가능한 값은SplitAttributes.LayoutDirection
을 참고하세요.
예시는 WindowManager API 섹션을 참고하세요.
자리표시자
자리표시자 활동은 활동 분할. 궁극적으로 콘텐츠가 포함된 다른 활동으로 대체됩니다. 예를 들어 자리표시자 활동이 목록-세부정보 레이아웃에서 활동 분할의 보조 측면은 다음 항목이 목록이 선택됩니다. 이때 세부정보가 포함된 활동이 자리표시자가 선택된 목록 항목의 정보로 대체됩니다.
기본적으로 시스템은 위치를 표시할 공간이 충분한 경우에만 활동 분할을 실행합니다 자리표시자는 디스플레이 크기가 너무 작아서 분할을 표시할 수 없습니다. 공간이 허락하는 경우 시스템은 다시 초기화된 상태로 자리표시자를 다시 실행합니다.
하지만 SplitPlaceholderRule
또는stickyPlaceholder
SplitPlaceholder.Builder
의 setSticky()
메서드는
기본 동작입니다. 속성 또는 메서드가 true
값을 지정하는 경우,
작업이 수행될 때 시스템에서 자리표시자를 작업 창의 최상위 활동으로 표시합니다.
디스플레이가 두 개의 창 디스플레이에서 단일 창 디스플레이로 크기가 조정됩니다.
예시는 분할 구성을 참고하세요.
창 크기 변경
기기 설정이 변경되면 작업 창 너비가 줄어들어 다중 창 레이아웃에 맞게 충분히 커야 합니다 (예: 대형 화면 폴더블 기기가 태블릿 크기에서 휴대전화 크기로 접히거나 앱 창 크기가 조절됨 멀티 윈도우 모드)의 보조 창에 있는 자리표시자가 아닌 활동이 작업 창은 기본 창의 활동 위에 스택됩니다.
자리표시자 활동은 분할에 맞게 디스플레이 너비가 충분한 경우에만 표시됩니다. 작은 화면에서는 자리표시자가 자동으로 닫힙니다. 이 디스플레이 영역이 다시 충분히 커지면 자리표시자가 다시 생성됩니다. (자리표시자 섹션을 참고하세요.)
WindowManager가 기본 창의 활동 위에 보조 창의 활동을 z 순서로 지정하므로 활동 스택이 가능합니다.
보조 창의 여러 활동
활동 B가 추가 인텐트 플래그 없이 제자리에서 활동 C를 시작합니다.
동일한 작업에서 다음과 같이 활동이 z 순서로 지정됩니다.
따라서 작은 작업 창에서는 애플리케이션이 C를 실행합니다.
작은 창에서 뒤로 이동하면 활동 위에 스택된 활동을 탐색하게 됩니다.
작업 창 구성이 여러 창을 수용할 수 있는 큰 크기로 복원되면 활동이 다시 나란히 표시됩니다.
스택된 분할
활동 B가 측면에 활동 C를 시작하고 분할의 좌우를 전환합니다.
동일한 작업에서 다음과 같이 활동이 z 순서로 지정됩니다.
작은 작업 창에서는 애플리케이션이 맨 위에 있는 C를 포함한 단일 활동으로 축소됩니다.
고정 세로 방향
android:screenOrientation 매니페스트 설정을 사용하면 앱에서 가로 또는 세로 방향으로 전환할 수 있습니다. 태블릿 및 폴더블과 같은 대형 화면 기기에서 사용자 환경을 개선하기 위해 기기 제조업체(OEM)는 화면 방향 요청을 무시하고 가로 모드 디스플레이에서 세로 방향으로 앱을 레터박스 처리하거나 세로 모드 디스플레이에서 가로 방향으로 앱을 레터박스 처리할 수 있습니다.
마찬가지로 활동 삽입이 사용 설정되면 OEM은 대형 화면에서 가로 모드 방향으로 고정된 세로 모드 레터박스 활동 (너비 ≥ 600dp) 고정 세로 모드 활동이 두 번째 활동을 실행하면 기기는 두 개의 창이 있는 디스플레이에 두 활동을 나란히 표시할 수 있습니다.
항상 android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
추가
속성을 앱 매니페스트 파일에 추가하여 앱이 지원함을 기기에 알립니다.
활동 삽입 (분할 구성 참고)
섹션). 그러면 OEM 맞춤 기기에서 레터박스 처리 여부를 결정할 수 있습니다.
사용할 수 있습니다.
분할 구성
분할 규칙은 활동 분할을 구성합니다. XML에서 분할 규칙 정의 구성 파일을 만들거나 Jetpack WindowManager API를 만들어 있습니다.
어느 경우든 앱은 WindowManager 라이브러리에 액세스해야 하고 앱에서 활동 삽입을 구현했음을 시스템에 알려야 합니다.
다음 단계를 따르세요.
최신 WindowManager 라이브러리 종속 항목을 앱의 모듈 수준
build.gradle
파일에 추가합니다. 예를 들면 다음과 같습니다.implementation 'androidx.window:window:1.1.0-beta02'
WindowManager 라이브러리는 활동에 필요한 모든 구성요소를 제공 있습니다.
앱이 활동 삽입을 구현했음을 시스템에 알립니다.
앱 매니페스트 파일의 <application> 요소에
android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
속성을 추가하고 값을 true로 설정합니다. 예를 들면 다음과 같습니다.<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <property android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED" android:value="true" /> </application> </manifest>
WindowManager 버전 1.1.0-alpha06 이상에서는 활동 삽입 분할이 사용 중지됩니다. 단, 속성이 매니페스트에 추가되고 true로 설정되는 경우는 예외입니다.
또한 기기 제조업체는 이 설정을 사용하여 활동 삽입을 지원하는 앱의 맞춤 기능을 사용 설정합니다. 예를 들어 기기는 가로 모드 디스플레이에서 세로 모드 전용 활동을 레터박스 처리하여, 두 번째 활동이 시작될 때 창 두 개 레이아웃으로 전환되도록 활동의 방향을 설정할 수 있습니다(세로 모드로 고정된 방향 참고).
XML 구성
활동 삽입의 XML 기반 구현을 만들려면 다음을 완료하세요. 다음 단계를 따르세요.
다음을 실행하는 XML 리소스 파일을 만듭니다.
- 분할을 공유하는 활동 정의
- 분할 옵션 구성
- 콘텐츠를 사용할 수 없는 경우 분할의 보조 컨테이너에 맞는 자리표시자를 만듭니다.
- 분할에 포함되면 안 되는 활동을 지정합니다.
예를 들면 다음과 같습니다.
<!-- main_split_config.xml --> <resources xmlns:window="http://schemas.android.com/apk/res-auto"> <!-- Define a split for the named activities. --> <SplitPairRule window:splitRatio="0.33" window:splitLayoutDirection="locale" window:splitMinWidthDp="840" window:splitMaxAspectRatioInPortrait="alwaysAllow" window:finishPrimaryWithSecondary="never" window:finishSecondaryWithPrimary="always" window:clearTop="false"> <SplitPairFilter window:primaryActivityName=".ListActivity" window:secondaryActivityName=".DetailActivity"/> </SplitPairRule> <!-- Specify a placeholder for the secondary container when content is not available. --> <SplitPlaceholderRule window:placeholderActivityName=".PlaceholderActivity" window:splitRatio="0.33" window:splitLayoutDirection="locale" window:splitMinWidthDp="840" window:splitMaxAspectRatioInPortrait="alwaysAllow" window:stickyPlaceholder="false"> <ActivityFilter window:activityName=".ListActivity"/> </SplitPlaceholderRule> <!-- Define activities that should never be part of a split. Note: Takes precedence over other split rules for the activity named in the rule. --> <ActivityRule window:alwaysExpand="true"> <ActivityFilter window:activityName=".ExpandedActivity"/> </ActivityRule> </resources>
이니셜라이저를 만듭니다.
WindowManager
RuleController
구성요소가 XML을 파싱함 구성 파일을 생성하고 시스템에 규칙을 사용할 수 있게 합니다. Jetpack Startup 라이브러리Initializer
는 XML 파일을 앱 시작 시RuleController
를 표시하여 규칙 실행 시 규칙이 적용되도록 합니다. 시작됩니다.이니셜라이저를 만들려면 다음 단계를 따르세요.
최신 Jetpack Startup 라이브러리 종속 항목을 모듈 수준에 추가
build.gradle
파일. 예:implementation 'androidx.startup:startup-runtime:1.1.1'
Initializer
인터페이스를 구현하는 클래스를 만듭니다.이니셜라이저는
RuleController
XML 구성 파일의 ID 전달 (main_split_config.xml
)RuleController.parseRules()
메서드에 전달합니다.Kotlin
class SplitInitializer : Initializer<RuleController> { override fun create(context: Context): RuleController { return RuleController.getInstance(context).apply { setRules(RuleController.parseRules(context, R.xml.main_split_config)) } } override fun dependencies(): List<Class<out Initializer<*>>> { return emptyList() } }
Java
public class SplitInitializer implements Initializer<RuleController> { @NonNull @Override public RuleController create(@NonNull Context context) { RuleController ruleController = RuleController.getInstance(context); ruleController.setRules( RuleController.parseRules(context, R.xml.main_split_config) ); return ruleController; } @NonNull @Override public List<Class<? extends Initializer<?>>> dependencies() { return Collections.emptyList(); } }
규칙 정의에 맞는 콘텐츠 제공자 만들기
androidx.startup.InitializationProvider
를 앱 매니페스트 파일에<provider>
로 추가합니다.RuleController
이니셜라이저,SplitInitializer
:<!-- AndroidManifest.xml --> <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <!-- Make SplitInitializer discoverable by InitializationProvider. --> <meta-data android:name="${applicationId}.SplitInitializer" android:value="androidx.startup" /> </provider>
InitializationProvider
는 다음 날짜 이전에SplitInitializer
를 탐색하고 초기화합니다. 앱의onCreate()
메서드가 호출됩니다. 따라서 분할 규칙은 앱의 기본 활동이 시작할 때 적용됩니다.
WindowManager API
몇 개의 API 호출을 사용하여 활동 삽입을 프로그래매틱 방식으로 구현할 수 있습니다. 활동이 시작되기 전에 규칙이 적용되도록 Application
의 서브클래스에 있는 onCreate()
메서드에서 API를 호출합니다.
프로그래매틱 방식으로 활동 분할을 만들려면 다음 단계를 따르세요.
분할 규칙 만들기:
SplitPairFilter
만들기 는 분할을 공유하는 활동을 식별하는 다음과 같습니다.Kotlin
val splitPairFilter = SplitPairFilter( ComponentName(this, ListActivity::class.java), ComponentName(this, DetailActivity::class.java), null )
Java
SplitPairFilter splitPairFilter = new SplitPairFilter( new ComponentName(this, ListActivity.class), new ComponentName(this, DetailActivity.class), null );
필터 세트에 필터를 추가합니다.
Kotlin
val filterSet = setOf(splitPairFilter)
Java
Set<SplitPairFilter> filterSet = new HashSet<>(); filterSet.add(splitPairFilter);
분할의 레이아웃 속성을 만듭니다.
Kotlin
val splitAttributes: SplitAttributes = SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build()
자바
final SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build();
SplitAttributes.Builder
는 레이아웃이 포함된 객체를 만듭니다. 속성:setSplitType()
: 사용 가능한 디스플레이 영역이 각 활동 컨테이너에 어떻게 할당되는지를 정의합니다. 비율 분할 유형은 기본 컨테이너에 할당된 가용 디스플레이 영역의 비율을 지정합니다. 보조 컨테이너는 가용 디스플레이 영역의 나머지 부분을 차지합니다.setLayoutDirection()
: 활동 컨테이너가 실행되는 방식을 지정합니다. 서로 상대적으로 배치되며 기본 컨테이너가 먼저 배치됩니다
SplitPairRule
을 빌드합니다.Kotlin
val splitPairRule = SplitPairRule.Builder(filterSet) .setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER) .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS) .setClearTop(false) .build()
Java
SplitPairRule splitPairRule = new SplitPairRule.Builder(filterSet) .setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER) .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS) .setClearTop(false) .build();
SplitPairRule.Builder
는 다음과 같은 규칙을 만들고 구성합니다.filterSet
: 분할을 공유하는 활동을 식별하여 규칙을 적용할 시기를 결정하는 분할 쌍 필터를 포함합니다.setDefaultSplitAttributes()
: 레이아웃 속성을 있습니다.setMinWidthDp()
: 분할을 사용 설정하는 최소 디스플레이 너비를 밀도 독립형 픽셀(dp) 단위로 설정합니다.setMinSmallestWidthDp()
: 기기 방향과 관계없이 두 디스플레이 크기 중 더 작은 값이 분할을 사용할 수 있는 최솟값(dp)을 설정합니다.setMaxAspectRatioInPortrait()
: 세로 모드 방향에서 활동 분할이 표시되는 최대 디스플레이 가로세로 비율(높이:너비)을 설정합니다. 세로 모드 디스플레이의 가로세로 비율이 최대 가로세로 비율을 초과하면 디스플레이 너비와 관계없이 분할이 사용 중지됩니다. 참고: 기본값은 1.4이며, 이 경우 활동이 대부분의 태블릿에서 세로 모드 방향의 전체 작업 창을 차지합니다.SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
및setMaxAspectRatioInLandscape()
도 참고하세요. 기본값 가로 모드:ALWAYS_ALLOW
setFinishPrimaryWithSecondary()
: 보조 컨테이너의 모든 활동이 종료될 때 기본 컨테이너의 활동에 어떠한 영향을 미치는지 설정합니다.NEVER
는 보조 컨테이너의 모든 활동이 종료될 때 시스템에서 기본 활동을 종료하면 안 된다는 것을 나타냅니다(활동 종료 참고).setFinishSecondaryWithPrimary()
: 모든 항목을 마무리하는 방법을 설정합니다. 기본 컨테이너의 활동이 기본 컨테이너의 활동에 영향을 하위 컨테이너일 수 있습니다ALWAYS
는 시스템이 항상 보조 컨테이너의 활동이 종료될 때 종료( 활동 종료).setClearTop()
: 새 활동이 실행될 때 보조 컨테이너가 종료되고 kube-APIserver입니다false
값은 새 활동이 보조 컨테이너에 이미 있는 활동 위에 스택됨을 지정합니다.
WindowManager
RuleController
의 싱글톤 인스턴스를 가져오고 규칙을 추가합니다.Kotlin
val ruleController = RuleController.getInstance(this) ruleController.addRule(splitPairRule)
자바
RuleController ruleController = RuleController.getInstance(this); ruleController.addRule(splitPairRule);
다음 경우에 보조 컨테이너의 자리표시자를 만듭니다. 콘텐츠를 사용할 수 없음:
사용하는 활동을 식별하는
ActivityFilter
를 만듭니다. 자리표시자는 작업 창 분할을 공유합니다.Kotlin
val placeholderActivityFilter = ActivityFilter( ComponentName(this, ListActivity::class.java), null )
Java
ActivityFilter placeholderActivityFilter = new ActivityFilter( new ComponentName(this, ListActivity.class), null );
필터 세트에 필터를 추가합니다.
Kotlin
val placeholderActivityFilterSet = setOf(placeholderActivityFilter)
자바
Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>(); placeholderActivityFilterSet.add(placeholderActivityFilter);
만들기
SplitPlaceholderRule
:Kotlin
val splitPlaceholderRule = SplitPlaceholderRule.Builder( placeholderActivityFilterSet, Intent(context, PlaceholderActivity::class.java) ).setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS) .setSticky(false) .build()
Java
SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder( placeholderActivityFilterSet, new Intent(context, PlaceholderActivity.class) ).setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS) .setSticky(false) .build();
SplitPlaceholderRule.Builder
는 다음과 같은 규칙을 만들고 구성합니다.placeholderActivityFilterSet
: 규칙 적용 시기를 결정하려면 연결되어 있는 것을 볼 수 있습니다.Intent
: 자리표시자 활동의 실행을 지정합니다.setDefaultSplitAttributes()
: 규칙에 레이아웃 속성을 적용합니다.setMinWidthDp()
: 최소 디스플레이 너비를 설정합니다(밀도 독립형 픽셀(dp)). 사용할 수 있습니다setMinSmallestWidthDp()
: 두 디스플레이 중 더 작은 값의 최솟값 (dp)을 설정합니다. 기기에 관계없이 분할을 허용해야 함 방향을 설정할 수 있습니다.setMaxAspectRatioInPortrait()
: 세로 모드에서 최대 디스플레이 가로세로 비율 (높이:너비)을 설정합니다. 활동 분할이 표시되는 방향입니다. 참고: 기본값은 1.4이며, 이 경우 활동이 작업을 채웁니다. 가로 방향 창을 표시합니다. 참고 항목SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
및setMaxAspectRatioInLandscape()
가로 모드의 기본값은ALWAYS_ALLOW
입니다.setFinishPrimaryWithPlaceholder()
: 자리표시자 활동이 종료될 때 활동에 미치는 영향을 설정합니다. 기본 컨테이너에서 실행됩니다 ALWAYS는 시스템이 항상 자리표시자가 기본 컨테이너의 활동을 종료한 후 종료됩니다 (활동 종료 참고).setSticky()
: 자리표시자가 충분한 최소 너비로 분할에 처음 표시된 후에 자리표시자 활동이 작은 디스플레이의 활동 스택 상단에 표시될지를 결정합니다.
WindowManager
RuleController
에 규칙 추가:Kotlin
ruleController.addRule(splitPlaceholderRule)
Java
ruleController.addRule(splitPlaceholderRule);
분할에 포함되면 안 되는 활동을 지정합니다.
항상 전체 작업 디스플레이 영역을 차지해야 하는 활동을 식별하는
ActivityFilter
를 만듭니다.Kotlin
val expandedActivityFilter = ActivityFilter( ComponentName(this, ExpandedActivity::class.java), null )
Java
ActivityFilter expandedActivityFilter = new ActivityFilter( new ComponentName(this, ExpandedActivity.class), null );
필터 세트에 필터를 추가합니다.
Kotlin
val expandedActivityFilterSet = setOf(expandedActivityFilter)
Java
Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>(); expandedActivityFilterSet.add(expandedActivityFilter);
ActivityRule
을 만듭니다.Kotlin
val activityRule = ActivityRule.Builder(expandedActivityFilterSet) .setAlwaysExpand(true) .build()
Java
ActivityRule activityRule = new ActivityRule.Builder( expandedActivityFilterSet ).setAlwaysExpand(true) .build();
ActivityRule.Builder
는 다음과 같은 규칙을 만들고 구성합니다.expandedActivityFilterSet
: 실행할 활동을 식별하여 규칙을 적용할 시기를 결정합니다. 선택할 수 있습니다.setAlwaysExpand()
: 활동이 전체 작업 창을 채워야 하는지 지정합니다.
WindowManager
RuleController
에 규칙 추가:Kotlin
ruleController.addRule(activityRule)
Java
ruleController.addRule(activityRule);
교차 애플리케이션 삽입
Android 13(API 수준 33) 이상에서는 앱이 다른 앱의 활동을 삽입할 수 있습니다. 교차 애플리케이션 또는 교차 UID 활동 삽입 여러 Android 애플리케이션의 활동을 시각적으로 통합할 수 있습니다. 이 시스템은 호스트 앱의 활동과 하나의 앱에서와 마찬가지로 화면의 위아래에 나란히 있는 다른 앱을 사용할 수 있습니다. 활동 삽입에 대해 알아봅니다.
예를 들어 설정 앱에서 WallpaperPicker 앱의 배경화면 선택기 활동을 삽입할 수 있습니다.
신뢰 모델
다른 앱의 활동을 삽입하는 호스트 프로세스는 크기, 위치, 자르기 및 있습니다. 악성 호스트는 이 기능을 이용하여 사용자를 오도하고 클릭재킹 또는 기타 UI 수정 공격 생성
교차 앱 활동 삽입의 오용을 방지하기 위해 Android는 앱에서 활동을 삽입할 수 있습니다. 앱은 호스트를 신뢰할 수 있는 호스트 또는 신뢰할 수 없는 호스트로 지정할 수 있습니다.
신뢰할 수 있는 호스트
다른 애플리케이션이
활동에서 호스트의 SHA-256 인증서를
android:knownActivityEmbeddingCerts
속성에
앱 매니페스트 파일의 <activity>
또는 <application>
요소.
android:knownActivityEmbeddingCerts
의 값을 다음과 같은 문자열로 설정하거나
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
... />
또는 (여러 인증서를 지정하려면) 문자열로 구성된 배열로 설정합니다.
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
... />
다음과 같이 리소스를 참조합니다.
<resources>
<string-array name="known_host_certificate_digests">
<item>cert1</item>
<item>cert2</item>
...
</string-array>
</resources>
앱 소유자는 Gradle signingReport
작업을 실행하여 SHA 인증서 다이제스트를 가져올 수 있습니다. 인증서 다이제스트는 구분용 콜론이 포함되지 않은 SHA-256 지문입니다. 자세한 내용은 서명 보고서 실행 및 클라이언트 인증을 참고하세요.
신뢰할 수 없는 호스트
모든 앱이 앱의 활동을 삽입하고 이러한 앱 활동 표현을 제어할 수 있게 하려면 앱 매니페스트의 <activity>
요소나 <application>
요소에 android:allowUntrustedActivityEmbedding
속성을 지정합니다. 예를 들면 다음과 같습니다.
<activity
android:name=".MyEmbeddableActivity"
android:allowUntrustedActivityEmbedding="true"
... />
속성의 기본값은 false이며, 이 경우 교차 앱 활동 삽입이 방지됩니다.
맞춤 인증
신뢰할 수 없는 활동 삽입의 위험을 완화하려면 호스트 ID를 확인하는 맞춤 인증 메커니즘을 만듭니다. 호스트를 아는 경우
androidx.security.app.authenticator
라이브러리를 사용하여
인증할 수 있습니다 활동이 삽입된 후 호스트가 인증하는 경우
실제 콘텐츠를 표시합니다. 그렇지 않은 경우 사용자에게 작업이 허용되지 않았음을 알리고 콘텐츠를 차단할 수 있습니다.
ActivityEmbeddingController#isActivityEmbedded()
메서드를 사용합니다.
Jetpack WindowManager 라이브러리를 사용하여 호스트가
예를 들면 다음과 같습니다.
Kotlin
fun isActivityEmbedded(activity: Activity): Boolean { return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity) }
Java
boolean isActivityEmbedded(Activity activity) { return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity); }
최소 크기 제한
Android 시스템이 앱에 지정된 최소 높이와 너비를 적용합니다.
매니페스트 <layout>
요소를 삽입된 활동에 추가합니다. 애플리케이션이
최소 높이와 너비를 지정하지 않으면 시스템 기본값이 적용됩니다.
(sw220dp
)
호스트가 삽입된 컨테이너의 크기를 최솟값보다 작게 조정하려고 하면 삽입된 컨테이너가 전체 작업 경계를 차지하도록 확장됩니다.
<activity-alias>
신뢰할 수 있거나 신뢰할 수 없는 활동 삽입이 <activity-alias>
요소와 함께 작동하려면 별칭이 아닌 타겟 활동에 android:knownActivityEmbeddingCerts
또는 android:allowUntrustedActivityEmbedding
을 적용해야 합니다. 시스템 서버에서 보안을 확인하는 정책은 별칭이 아닌 타겟에 설정된 플래그를 기반으로 합니다.
호스트 애플리케이션
호스트 애플리케이션은 애플리케이션과 동일한 방식으로 교차 앱 활동 삽입을 구현합니다.
단일 앱 활동 삽입을 구현할 수 있습니다. SplitPairRule
및
SplitPairFilter
또는 ActivityRule
및 ActivityFilter
객체
삽입된 활동 및 작업 창 분할을 지정할 수 있습니다. 분할 규칙 정의됨
XML에서 정적으로 또는 Jetpack을 사용하여 런타임에
WindowManager API 호출
호스트 애플리케이션이 교차 앱 삽입을 선택하지 않은 활동을 삽입하려고 하면 활동이 전체 작업 경계를 차지합니다. 따라서 호스트 애플리케이션은 타겟 활동이 교차 앱 삽입을 허용하는지 여부를 알아야 합니다.
삽입된 활동이 같은 작업에서 새 활동을 시작하고 새 활동이 활동이 교차 앱 삽입을 선택하지 않은 경우 삽입된 컨테이너에서 활동을 오버레이하는 대신 전체 작업 경계를 수행합니다.
호스트 애플리케이션은 활동이 동일한 작업에서 실행되는 경우 제한 없이 자체 활동을 삽입할 수 있습니다.
분할 예
전체 크기 창에서 분할
리팩터링할 필요가 없습니다. 분할 구성을 정의할 수 있습니다.
런타임에서 호출한 다음 아무것도 없이 Context#startActivity()
를
추가할 수 있습니다.
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
기본적으로 분할
애플리케이션의 방문 페이지가 대형 화면에서 두 컨테이너로 분할되도록 설계된 경우 두 활동이 모두 생성되고 동시에 표현될 때 사용자 환경이 가장 좋습니다. 하지만 사용자가 기본 컨테이너의 활동과 상호작용(예: 사용자가 탐색 메뉴에서 항목 선택)할 때까지 분할의 보조 컨테이너에 콘텐츠를 사용하지 못할 수도 있습니다. 자리표시자 활동은 분할의 보조 컨테이너에 콘텐츠가 표시될 수 있을 때까지 공백을 채울 수 있습니다(자리표시자 섹션 참고).
자리표시자로 분할을 만들려면 자리표시자를 만들어 기본 활동:
<SplitPlaceholderRule
window:placeholderActivityName=".PlaceholderActivity">
<ActivityFilter
window:activityName=".MainActivity"/>
</SplitPlaceholderRule>
딥 링크 분할
앱이 인텐트를 수신하면 대상 활동이 활동 분할의 보조 부분으로 표시될 수 있습니다. 예를 들어 목록의 항목에 관한 정보가 포함된 세부정보 화면을 표시하는 요청이 있습니다. 작은 디스플레이에서는 전체 크기 작업 창에, 큰 기기에서는 목록 옆에 세부정보가 표시됩니다.
실행 요청은 기본 활동으로 라우팅되고 대상 세부정보 활동은 분할에서 실행됩니다. 시스템은 사용할 수 있는 디스플레이 너비에 따라 올바른 표시 방법(스택 또는 나란히 표시)을 자동으로 선택합니다.
Kotlin
override fun onCreate(savedInstanceState Bundle?) { . . . RuleController.getInstance(this) .addRule(SplitPairRule.Builder(filterSet).build()) startActivity(Intent(this, DetailActivity::class.java)) }
자바
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { . . . RuleController.getInstance(this) .addRule(new SplitPairRule.Builder(filterSet).build()); startActivity(new Intent(this, DetailActivity.class)); }
딥 링크 대상은 사용자에게 표시되는 것을 방지할 수 있습니다. 따라서 개발자는 세부정보 활동을 종료하고 기본 활동만 남깁니다.
대신 finishPrimaryWithSecondary
속성을 사용하여 두 활동을 동시에 종료할 수 있습니다.
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".ListActivity"
window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>
구성 속성 섹션을 참고하세요.
분할 컨테이너의 여러 활동
분할 컨테이너에 여러 활동을 스택하면 사용자가 딥 콘텐츠에 액세스할 수 있습니다. 예를 들어 목록-세부정보 분할에서 사용자가 하위 세부정보 섹션으로 이동하되 기본 활동은 제자리에 유지해야 할 수도 있습니다.
Kotlin
class DetailActivity { . . . fun onOpenSubDetail() { startActivity(Intent(this, SubDetailActivity::class.java)) } }
Java
public class DetailActivity { . . . void onOpenSubDetail() { startActivity(new Intent(this, SubDetailActivity.class)); } }
하위 세부정보 활동이 세부정보 활동 위에 놓여 세부정보 활동이 숨겨집니다.
그러면 사용자는 스택에서 뒤로 이동하여 이전의 세부정보 수준으로 돌아갈 수 있습니다.
여러 활동을 서로 위에 스택하는 것은 이러한 활동이 동일한 보조 컨테이너의 한 활동에서 실행되는 경우 기본 동작입니다. 활성 분할 내의 기본 컨테이너에서 실행된 활동도 활동 스택 맨 위에 있는 보조 컨테이너에 배치됩니다.
새 작업의 활동
분할 작업 창의 활동이 새 작업에서 활동을 시작하는 경우 이 새 작업은 분할을 포함하는 작업에서 분리되어 전체 크기 창으로 표시됩니다. 최근 항목 화면에는 분할에서의 작업과 새 작업, 두 가지 작업이 표시됩니다.
활동 대체
활동이 보조 컨테이너 스택에서 대체될 수 있습니다. 예를 들어 기본 활동이 최상위 탐색에 사용되고 보조 활동이 선택된 대상인 경우가 있습니다. 최상위 탐색에서 선택한 각 항목은 보조 컨테이너에서 새 활동을 시작하고 이전에 보조 컨테이너에 존재하던 하나 이상의 활동을 삭제해야 합니다.
다음 시점에 앱이 보조 컨테이너의 활동을 종료하지 않으면 탐색 선택이 변경되면 분할할 때 뒤로 탐색이 혼동될 수 있음 접혀 있을 때 (기기가 접혀 있을 때) 예를 들어 기본 창과 화면 A와 B가 보조 창에 쌓여 있을 때, 휴대전화를 접으면 B가 A 위에 있고 A가 메뉴 위에 있습니다. 사용자가 B에서 뒤로 이동하면 메뉴 대신 A가 표시됩니다.
이 경우 화면 A를 백 스택에서 삭제해야 합니다.
기존 분할에서 측면으로 새 컨테이너에 실행할 때의 기본 동작은 새 보조 컨테이너를 맨 위에 두고 이전 컨테이너를 백 스택에 유지하는 것입니다. clearTop
을 사용하여 이전 보조 컨테이너를 지우고 정상적으로 새 활동을 실행하도록 분할을 구성할 수 있습니다.
<SplitPairRule
window:clearTop="true">
<SplitPairFilter
window:primaryActivityName=".Menu"
window:secondaryActivityName=".ScreenA"/>
<SplitPairFilter
window:primaryActivityName=".Menu"
window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>
Kotlin
class MenuActivity { . . . fun onMenuItemSelected(selectedMenuItem: Int) { startActivity(Intent(this, classForItem(selectedMenuItem))) } }
자바
public class MenuActivity { . . . void onMenuItemSelected(int selectedMenuItem) { startActivity(new Intent(this, classForItem(selectedMenuItem))); } }
또는 동일한 보조 활동을 사용하고 기본(메뉴) 활동에서는 동일한 인스턴스로 확인되지만 상태 또는 UI 업데이트를 보조 컨테이너에 트리거하는 새 인텐트를 전송합니다.
여러 분할
앱은 추가 활동을 측면에 실행하여 여러 수준의 딥 탐색을 제공할 수 있습니다.
보조 컨테이너의 활동이 측면에 새 활동을 실행하면 새 분할이 만들어지는 기존 분할 위에 생성됩니다.
백 스택에는 이전에 열린 모든 활동이 포함되어 있으므로 사용자는 C를 종료한 후 A/B 분할로 이동할 수 있습니다.
새 분할을 만들려면 기존의 보조 컨테이너에서 측면으로 새 활동을 실행합니다. A/B 분할과 B/C 분할 모두의 구성 선언 그런 다음 아래와 같이 B에서 정상적으로 활동 C를 실행합니다.
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
<SplitPairFilter
window:primaryActivityName=".B"
window:secondaryActivityName=".C"/>
</SplitPairRule>
Kotlin
class B { . . . fun onOpenC() { startActivity(Intent(this, C::class.java)) } }
자바
public class B { . . . void onOpenC() { startActivity(new Intent(this, C.class)); } }
분할 상태 변경에 관한 반응
앱의 다양한 활동에는 동일한 기능을 실행하는 UI 요소가 있을 수 있습니다. 예를 들어 계정 설정이 포함된 창을 여는 컨트롤이 있습니다.
공통된 UI 요소를 가진 두 활동이 분할되는 경우 중복되어 이 요소를 두 활동 모두에서 표시하면 혼란스러울 수 있습니다.
활동이 분할에 포함되는 시점을 알아보려면 SplitController.splitInfoList
흐름을 확인하거나 SplitControllerCallbackAdapter
에 리스너를 등록하여 분할 상태의 변경사항을 확인하세요. 그런 다음 UI를 다음과 같이 조정합니다.
Kotlin
val layout = layoutInflater.inflate(R.layout.activity_main, null) val view = layout.findViewById<View>(R.id.infoButton) lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { splitController.splitInfoList(this@SplitDeviceActivity) // The activity instance. .collect { list -> view.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE } } }
자바
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { . . . new SplitControllerCallbackAdapter(SplitController.getInstance(this)) .addSplitListener( this, Runnable::run, splitInfoList -> { View layout = getLayoutInflater().inflate(R.layout.activity_main, null); layout.findViewById(R.id.infoButton).setVisibility( splitInfoList.isEmpty() ? View.VISIBLE : View.GONE); }); }
코루틴은 모든 수명 주기 상태에서 실행할 수 있지만, 일반적으로 리소스를 보존하기 위해 STARTED
상태에서 실행됩니다 (자세한 내용은 수명 주기 인식 구성요소와 함께 Kotlin 코루틴 사용 참고).
활동이 중지되는 시점을 포함하여 모든 수명 주기 상태에서 콜백을 호출할 수 있습니다. 리스너는 일반적으로 onStart()
에 등록하고 등록 취소해야 합니다.
(onStop()
)
전체 크기 창 모달
일부 활동은 지정된 작업이 실행될 때까지 사용자가 애플리케이션과 상호작용하지 못하도록 차단합니다. 예를 들어 로그인 화면 활동, 정책 확인 화면, 오류 메시지 등이 있습니다. 모달 활동은 분할에 표시되지 않아야 합니다.
확장 구성을 사용하여 활동이 항상 작업 창을 채우도록 강제할 수 있습니다.
<ActivityRule
window:alwaysExpand="true">
<ActivityFilter
window:activityName=".FullWidthActivity"/>
</ActivityRule>
활동 종료
사용자는 가장자리에서 스와이프하여 분할의 양쪽에서 활동을 종료할 수 있습니다. 다음과 같습니다.
기기가 동작 탐색 대신 뒤로 버튼을 사용하도록 설정된 경우 포커스가 맞춰진 활동(마지막으로 터치되거나 실행된 활동)으로 입력이 전송됩니다.
컨테이너의 모든 활동을 종료할 때 다른 활동이 분할 구성에 따라 다릅니다
구성 속성
분할 쌍 규칙 속성을 지정하여 분할의 한쪽에 있는 활동이 다른 쪽의 활동에 영향을 주고 분할할 수 있습니다 속성은 다음과 같습니다.
window:finishPrimaryWithSecondary
: 보조 컨테이너의 모든 활동이 종료될 때 기본 컨테이너의 활동에 미치는 영향window:finishSecondaryWithPrimary
: 기본 컨테이너의 모든 활동이 종료될 때 보조 컨테이너의 활동에 미치는 영향
가능한 속성 값은 다음과 같습니다.
always
: 연결된 컨테이너의 활동을 항상 종료함never
: 연결된 컨테이너의 활동을 종료하지 않음adjacent
: 두 컨테이너가 서로 인접해 표시되는 경우 연결된 컨테이너에서 활동을 종료하지만 두 컨테이너가 스택된 경우에는 활동을 종료하지 않음
예를 들면 다음과 같습니다.
<SplitPairRule
<!-- Do not finish primary container activities when all secondary container activities finish. -->
window:finishPrimaryWithSecondary="never"
<!-- Finish secondary container activities when all primary container activities finish. -->
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
기본 구성
분할된 한 컨테이너의 모든 활동이 종료되면 나머지 컨테이너가 전체 창을 차지합니다.
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
활동 함께 종료
모든 활동이 실행될 때 기본 컨테이너의 활동을 자동으로 종료합니다. 종료하면 됩니다.
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
기본 컨테이너의 모든 활동이 종료되면 보조 컨테이너의 활동을 자동으로 종료합니다.
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
기본 또는 보조 컨테이너 완료:
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
컨테이너의 여러 활동 종료
분할 컨테이너에 여러 활동이 스택된 경우 스택 맨 아래에 있는 활동을 종료해도 맨 위에 있는 활동이 자동으로 종료되지는 않습니다.
예를 들어 보조 컨테이너에 두 활동, 즉 활동 B 위에 활동 C가 있으며
분할 구성이 활동 A와 활동 B의 구성에 의해 정의되는 경우
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
맨 위 활동을 종료하면 분할이 유지됩니다.
보조 컨테이너의 하단 (루트) 활동을 종료해도 삭제되지 않음 그 위에 있는 활동 분할도 유지합니다
기본 활동과 함께 보조 활동을 종료하는 경우와 같이 활동을 함께 종료하기 위한 추가 규칙도 실행됩니다.
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
기본과 보조를 함께 종료하도록 분할이 구성된 경우 다음과 같습니다.
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
런타임 시 분할 속성 변경
활성 상태이며 표시되는 분할의 속성은 변경할 수 없습니다. 분할 규칙을 변경하면 추가 활동 실행 및 새 컨테이너에 영향을 주지만 기존의 활성 분할에는 영향을 주지 않습니다.
활성 분할의 속성을 변경하려면 분할의 측면 활동을 종료한 후 새 구성으로 다시 측면으로 실행합니다.
동적 분할 속성
Jetpack WindowManager 1.4에서 지원하는 Android 15 (API 수준 35) 이상 더 높은 버전에서는 활동을 구성할 수 있는 동적 기능을 제공합니다. 임베딩 분할:
- 창 확장: 드래그 가능한 대화형 구분선을 통해 사용자는 다음 작업을 할 수 있습니다. 분할 프레젠테이션에서 창 크기를 조절할 수 있습니다.
- 활동 고정: 사용자가 한 컨테이너의 콘텐츠를 고정하고 컨테이너의 탐색을 다른 컨테이너의 탐색과 격리할 수 있습니다.
- 전체 화면 대화상자 어둡게 처리: 대화상자를 표시할 때 앱은 전체 작업 창을 어둡게 처리할지 아니면 대화상자를 연 컨테이너만 어둡게 처리할지 지정할 수 있습니다.
창 확장
창 확장을 통해 사용자는 할당된 화면 공간의 양을 조정할 수 있습니다. 두 활동을 이중 창 레이아웃으로 설정할 수 있습니다
창 구분선의 모양을 맞춤설정하고 구분선의 드래그 가능한 범위에 추가하려면 다음 단계를 따르세요.
DividerAttributes
인스턴스 만들기구분자 속성을 맞춤설정합니다.
color
: 드래그 가능한 창 분리자의 색상입니다.widthDp
: 드래그 가능한 창 구분자의 너비입니다. 시스템에서 구분자 너비를 결정하도록WIDTH_SYSTEM_DEFAULT
로 설정합니다.드래그 범위: 두 창이 한 창에 표시될 수 있는 화면의 최소 비율 있습니다. 범위는 0.33~0.66입니다. 다음으로 설정
DRAG_RANGE_SYSTEM_DEFAULT
: 시스템에서 드래그를 결정하도록 합니다. 범위입니다.
Kotlin
val splitAttributesBuilder: SplitAttributes.Builder = SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) if (WindowSdkExtensions.getInstance().extensionVersion >= 6) { splitAttributesBuilder.setDividerAttributes( DividerAttributes.DraggableDividerAttributes.Builder() .setColor(getColor(context, R.color.divider_color)) .setWidthDp(4) .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT) .build() ) } val splitAttributes: SplitAttributes = splitAttributesBuilder.build()
자바
SplitAttributes.Builder splitAttributesBuilder = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT); if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) { splitAttributesBuilder.setDividerAttributes( new DividerAttributes.DraggableDividerAttributes.Builder() .setColor(ContextCompat.getColor(context, R.color.divider_color)) .setWidthDp(4) .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT) .build() ); } SplitAttributes splitAttributes = splitAttributesBuilder.build();
활동 고정
활동 고정을 사용하면 사용자가 분할 창 중 하나를 고정하여 활동은 사용자가 다른 창 안을 탐색하는 동안 그대로 유지됩니다. 활동 고정을 사용하면 향상된 멀티태스킹 환경을 제공할 수 있습니다.
앱에서 활동 고정을 사용 설정하려면 다음 단계를 따르세요.
고정하려는 활동의 레이아웃 파일에 버튼을 추가합니다. 목록 세부정보 레이아웃의 세부정보 활동을 예로 들 수 있습니다.
<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/detailActivity" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" tools:context=".DetailActivity"> <TextView android:id="@+id/textViewItemDetail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="36sp" android:textColor="@color/obsidian" app:layout_constraintBottom_toTopOf="@id/pinButton" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.appcompat.widget.AppCompatButton android:id="@+id/pinButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/pin_this_activity" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/textViewItemDetail"/> </androidx.constraintlayout.widget.ConstraintLayout>
활동의
onCreate()
메서드에서 버튼에 onclick 리스너를 설정합니다.Kotlin
pinButton = findViewById(R.id.pinButton) pinButton.setOnClickListener { val splitAttributes: SplitAttributes = SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.66f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build() val pinSplitRule = SplitPinRule.Builder() .setSticky(true) .setDefaultSplitAttributes(splitAttributes) .build() SplitController.getInstance(applicationContext).pinTopActivityStack(taskId, pinSplitRule) }
자바
Button pinButton = findViewById(R.id.pinButton); pinButton.setOnClickListener( (view) => { SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.66f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build(); SplitPinRule pinSplitRule = new SplitPinRule.Builder() .setSticky(true) .setDefaultSplitAttributes(splitAttributes) .build(); SplitController.getInstance(getApplicationContext()).pinTopActivityStack(getTaskId(), pinSplitRule); });
전체 화면 어둡게
활동은 일반적으로 대화상자에 관심을 유도하기 위해 디스플레이를 어둡게 합니다. 활동 삽입에서는 통합된 UI 환경을 위해 대화상자를 연 활동이 포함된 창뿐만 아니라 듀얼 창 디스플레이의 두 창 모두 어두워야 합니다.
WindowManager 1.4 이상에서는
대화상자가 열립니다 (EmbeddingConfiguration.DimAreaBehavior.ON_TASK
참고).
대화상자를 연 활동의 컨테이너만 어둡게 하려면 EmbeddingConfiguration.DimAreaBehavior.ON_ACTIVITY_STACK
를 사용하세요.
분할에서 전체 크기 창으로 활동 추출
측면 활동 전체 크기 창을 표시하는 새 구성을 만든 다음 동일한 인스턴스로 확인되는 인텐트로 활동을 다시 실행합니다.
런타임 시 분할 지원 확인
활동 삽입은 Android 12L (API 수준 32) 이상에서 지원되지만
이전 플랫폼 버전을 실행하는 일부 기기에서도 사용할 수 있습니다. 확인:
런타임을 하려면
SplitController.splitSupportStatus
속성 또는
SplitController.getSplitSupportStatus()
메서드:
Kotlin
if (SplitController.getInstance(this).splitSupportStatus == SplitController.SplitSupportStatus.SPLIT_AVAILABLE) { // Device supports split activity features. }
자바
if (SplitController.getInstance(this).getSplitSupportStatus() == SplitController.SplitSupportStatus.SPLIT_AVAILABLE) { // Device supports split activity features. }
분할이 지원되지 않으면 활동이 비활동 삽입 모델에 따라 액티비티 스택의 맨 위에서 실행됩니다.
시스템 재정의 방지
Android 기기 제조업체(OEM)는 기기 시스템의 기능으로 활동 삽입을 구현할 수 있습니다. 시스템은 여러 활동이 있는 앱에 분할 규칙을 지정하여 앱의 윈도잉 동작을 재정의합니다. 시스템 재정의는 여러 활동이 있는 앱에 시스템 정의 활동 삽입 모드를 강제 적용합니다.
시스템 활동 삽입은 앱을 변경하지 않고도 list-detail과 같은 다중 창 레이아웃을 통해 앱 프레젠테이션을 개선할 수 있습니다. 그러나 시스템의 활동 삽입으로 인해 잘못된 앱 레이아웃, 버그가 발생하거나 시스템 활동 삽입이 앱에서 구현된 활동 삽입과 충돌할 수도 있습니다.
앱은 앱 매니페스트 파일에 속성을 설정하여 시스템 활동 삽입을 방지하거나 허용할 수 있습니다. 예를 들면 다음과 같습니다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<property
android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
android:value="true|false" />
</application>
</manifest>
속성 이름은 Jetpack WindowManager의 WindowProperties
에 정의되어 있습니다.
객체를 지정합니다. 앱에서 활동 삽입을 구현하는 경우 값을 false
로 설정합니다.
시스템에서 활동 삽입을 적용하지 못하게 하려는 경우
앱에 적용할 수 있습니다. 값을 true
로 설정하여 시스템이 적용되도록 합니다.
앱에 추가할 수 있습니다.
제한, 제약, 주의사항
- 작업의 루트 활동의 소유자로 식별되는 작업의 호스트 앱만 다른 활동을 구성하고 작업에 삽입할 수 있습니다. 삽입 및 분할을 지원하는 활동이 다른 애플리케이션에 속한 작업에서 실행되는 경우 이러한 활동에는 삽입 및 분할이 작동하지 않습니다.
- 단일 작업 내에서만 활동을 구성할 수 있습니다. 새 작업에서 활동을 실행하면 항상 기존 분할 외부의 새로운 확장 창에 활동이 배치됩니다.
- 동일한 프로세스의 활동만 구성하고 분할에 배치할 수 있습니다.
SplitInfo
콜백은 동일한 프로세스에 속한 활동만 보고합니다. 다른 프로세스의 활동에 관해 알 수 있는 방법이 없기 때문입니다. - 각 쌍 또는 단일 활동 규칙은 규칙 등록 후 발생하는 활동 실행에만 적용됩니다. 기존 분할이나 시각적 속성을 업데이트할 수 있는 방법은 현재 없습니다.
- 분할 쌍 필터 구성은 활동을 완전히 실행할 때 사용된 인텐트와 일치해야 합니다. 이러한 일치는 애플리케이션 프로세스에서 새로운 활동이 시작될 때 발생합니다. 따라서 암시적 인텐트를 사용할 때 시스템 프로세스의 후반에 확인되는 구성요소 이름을 모를 수도 있습니다. 출시 시점에 구성요소 이름을 알 수 없는 경우 대신 와일드 카드('*/*')를 사용할 수 있으며 할 수 있습니다.
- 현재는 컨테이너 간에 또는 컨테이너 안으로 활동을 이동할 수 있는 방법이 없습니다. 삭제됩니다. 일치하는 규칙이 있는 새 활동이 실행될 때만 WindowManager 라이브러리에 의해 분할이 생성되며 분할 컨테이너의 마지막 활동이 종료될 때 분할이 소멸됩니다.
- 구성이 변경될 때 활동을 다시 실행할 수 있으므로 분할을 만들거나 삭제하고 활동 경계가 변경되면 활동은 이전 인스턴스가 완전히 소멸되고 새 인스턴스가 생성되는 과정을 거칠 수 있습니다. 따라서 앱 개발자는 수명 주기 콜백에서 새 활동을 실행하는 등의 작업에 주의해야 합니다.
- 활동을 지원하려면 기기에 창 확장 프로그램 인터페이스가 포함되어야 합니다. 있습니다. Android 12L (API 수준)을 실행하는 거의 모든 대형 화면 기기 32) 이상에서만 가능합니다. 그러나 여러 활동을 실행할 수 없는 일부 대형 화면 기기에는 창 확장 프로그램 인터페이스가 포함되어 있지 않습니다. 대형 화면 기기에서 멀티 윈도우를 지원하지 않는 경우 활동 삽입을 지원하지 않을 수 있습니다.
추가 리소스
- Codelab: <ph type="x-smartling-placeholder">
- 학습 개발자 과정: 활동 삽입
- 샘플 앱: activity-embedding