본 포스팅은 AlertDialogのスタイルをカスタマイズ 포스팅을 번역했습니다.
제 일본어 실력으로 인하여 오역이나 오타가 발생할 수 있습니다.
실제 발표내용에 해당하는 슬라이드와 슬라이드의 일본어 부분만 번역
만 번역했다는 점 양해바랍니다.
이 기사는 Android 3 Advent Calendar 2016 - Qiita의 12번째 기사입니다.
주의!!
이 문서는 appcompat-v7 버전 25.0.1에서 테스트한 내용입니다. 최신 버전 25.1.0에서는 android.support.v7.app.AlertController의 구현이 변경되어 사용할 수 없는 부분이 있습니다.
Support Library v7 appcompat 라이브러리를 사용하면 Android 2.3(API 레벨 9) 이상의 디바이스에서 앱에서 머티리얼 디자인을 사용할 수 있으며, 다이얼로그도 머티리얼 디자인입니다.
import android.support.v7.app.AlertDialog;
// 생략
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("확인");
builder.setMessage("XXX해도 괜찮습니까?");
builder.setPositiveButton("예", null);
builder.setNegativeButton("아니오", null);
builder.setNeutralButton("취소", null);
builder.create().show();
NeutralButton이 왼쪽에 있고, PositiveButton과 NegativeButton이 오른쪽에 있는 디자인이네요.
일반 사용자가 사용하는 어플이라면 이 디자인으로 괜찮을 거라고 생각합니다만, 유아/고령자용 어플을 생각했을 때 더 큰 글자로 누르기 쉬운 버튼 디자인으로 하고 싶었기 때문에 다이얼로그를 커스터마이즈 해보겠습니다.
먼저 스타일을 정의합니다.
// styles.xml
<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">
</style>
AlertDialog.Builder의 두 번째 인수 int themeResId에 테마를 지정하여 커스터마이즈 스타일을 적용할 수 있습니다. AlertDialog.Builder ┃ Android Developers
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this, R.style.MyAlertDialogStyle);
어플의 모든 다이얼로그에 스타일을 적용하려면 AppTheme의 android:alertDialogTheme에 지정합니다.
// themes.xml
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorAccent">@color/accent</item>
<item name="android:alertDialogTheme">@style/MyAlertDialogStyle</item>
</style>
colorAccent를 지정합니다.
// styles.xml
<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/primary</item>
</style>
android:textSize를 지정합니다.
// styles.xml
<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/primary</item>
<item name="android:textSize">@dimen/dialog_text_size</item>
</style>
버튼이 세로로 되어버렸습니다…
메시지의 크기는 변경되지 않네요…
android:windowMinWidthMajor, android:windowMinWidthMinor를 지정합니다.
// styles.xml
<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/primary</item>
<item name="android:textSize">@dimen/dialog_text_size</item>
<item name="android:windowMinWidthMajor">@dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>
// dimens.xml
<resources>
<item type="dimen" name="dialog_min_width_major">95%</item>
<item type="dimen" name="dialog_min_width_minor">95%</item>
</resources>
android:textColorPrimary를 지정하면 텍스트 색상을 변경할 수 있습니다.
// styles.xml
<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/primary</item>
<item name="android:textColorPrimary">@color/colorPrimary</item>
</style>
android:windowTitleStyle을 정의하면 타이틀 스타일을 변경할 수 있습니다.
// styles.xml
<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/primary</item>
<item name="android:windowTitleStyle">@style/windowTitleStyle</item>
</style>
<style name="windowTitleStyle" parent="TextAppearance.AppCompat.Large">
<item name="android:textSize">@dimen/dialog_text_size</item>
</style>
메시지의 텍스트 크기를 지정할 수 있는 속성이 존재하지 않았기 때문에 코드로 해킹합니다.
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this, R.style.MyAlertDialogStyle);
builder.setTitle("확인");
builder.setMessage("XXX해도 괜찮습니까?");
builder.setPositiveButton("예", null);
builder.setNegativeButton("아니오", null);
builder.setNeutralButton("취소", null);
AlertDialog dialog = builder.show();
// 메시지 텍스트를 변경한다.
TextView textView = (TextView) dialog.findViewById(android.R.id.message);
textView.setTextSize(48.0f);
buttonBarNeutralButtonStyle, buttonBarNegativeButtonStyle, buttonBarPositiveButtonStyle에서 지정할 수 있습니다.
// styles.xml
<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/primary</item>
<item name="android:textSize">@dimen/dialog_text_size</item>
<item name="android:windowMinWidthMajor">@dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
<item name="buttonBarNeutralButtonStyle">@style/buttonBarButtonStyle.Neutral</item>
<item name="buttonBarNegativeButtonStyle">@style/buttonBarButtonStyle.Negative</item>
<item name="buttonBarPositiveButtonStyle">@style/buttonBarButtonStyle.Positive</item>
</style>
// styles.xml
<style name="buttonBarButtonStyle" parent="Widget.AppCompat.Button.Borderless.Colored">
</style>
<style name="buttonBarButtonStyle.Neutral">
</style>
<style name="buttonBarButtonStyle.Negative">
<item name="android:textColor">@color/red</item>
</style>
<style name="buttonBarButtonStyle.Positive">
<item name="android:textColor">@color/blue</item>
</style>
부모(parent)에 Widget.AppCompat.Button.Borderless.Colored를 지정하면 텍스트 컬러의 기본이 colorAccent로 됩니다.
부모(parent)에 Widget.AppCompat.Button.Colored을 지정하면 colorAccent를 배경으로 한 버튼이 배치됩니다.
// styles.xml
<style name="buttonBarButtonStyle" parent="Widget.AppCompat.Button.Colored">
</style>
<style name="buttonBarButtonStyle.Neutral">
</style>
<style name="buttonBarButtonStyle.Negative">
<item name="android:textColor">@color/red</item>
</style>
<style name="buttonBarButtonStyle.Positive">
<item name="android:textColor">@color/blue</item>
</style>
배경의 크기를 크게하거나 색상을 변경할 경우에는 android:background를 변경합니다.
Ripple 효과가 있는 배경으로 하고싶은 경우는 appcompat 소스를 참고하면 좋을 것입니다.
배경 색상을 변경할 경우에는 app:backgroundTint를 지정합니다.
<style name="buttonBarButtonStyle.Positive">
<item name="backgroundTint">@color/colorAccent</item>
</style>
머티리얼 디자인 이전의 다이얼로그처럼 버튼이 중앙에 3개의 있는 스타일을 원한다면, 코드로 해킹합니다.
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this, R.style.MyAlertDialogStyle);
builder.setTitle("확인");
builder.setMessage("XXX해도 괜찮습니까?");
builder.setPositiveButton("예", null);
// builder.setNegativeButton("아니오", null);
builder.setNeutralButton("취소", null);
AlertDialog dialog = builder.show();
// 메시지 텍스트를 변경한다.
TextView textView = (TextView) dialog.findViewById(android.R.id.message);
textView.setTextSize(48.0f);
// 버튼 패널의 컨테이너를 가져온다.
LinearLayout containerButtons = (LinearLayout) dialog.findViewById(R.id.buttonPanel);
// Space View를 삭제한다
containerButtons.removeView(containerButtons.getChildAt(1));
// 버튼 패널의 자식 View의 버튼의 Gravity을 CENTER로 설정
containerButtons.setGravity(Gravity.CENTER);
// LinearLayout의 비율의 합계를 3으로 설정한다.
containerButtons.setWeightSum(3.0f);
// 각 버튼을 얻는다.
Button button3 = (Button) containerButtons.findViewById(android.R.id.button3);
Button button2 = (Button) containerButtons.findViewById(android.R.id.button2);
Button button1 = (Button) containerButtons.findViewById(android.R.id.button1);
// 각 버튼의 비율을 지정한다
((LinearLayout.LayoutParams)button3.getLayoutParams()).weight = 1.0f;
((LinearLayout.LayoutParams)button2.getLayoutParams()).weight = 1.0f;
((LinearLayout.LayoutParams)button1.getLayoutParams()).weight = 1.0f;
// 각 버튼의 폭을 0으로 한다
((LinearLayout.LayoutParams)button3.getLayoutParams()).width = 0;
((LinearLayout.LayoutParams)button2.getLayoutParams()).width = 0;
((LinearLayout.LayoutParams)button1.getLayoutParams()).width = 0;
appcompat 라이브러리에서 사용되는 리소스를 덮어쓰는 것으로 다이얼로그의 디자인을 변경할 수 있습니다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/parentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/topPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/title_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingLeft="?attr/dialogPreferredPadding"
android:paddingRight="?attr/dialogPreferredPadding"
android:paddingTop="@dimen/abc_dialog_padding_top_material">
<ImageView
android:id="@android:id/icon"
android:layout_width="32dip"
android:layout_height="32dip"
android:scaleType="fitCenter"
android:src="@null"
style="@style/RtlOverlay.Widget.AppCompat.DialogTitle.Icon"/>
<android.support.v7.widget.DialogTitle
android:id="@+id/alertTitle"
style="?attr/android:windowTitleStyle"
android:singleLine="true"
android:ellipsize="end"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="viewStart" />
</LinearLayout>
<!-- If the client uses a customTitle, it will be added here. -->
</LinearLayout>
<FrameLayout
android:id="@+id/contentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minHeight="48dp">
<View android:id="@+id/scrollIndicatorUp"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="top"
android:background="?attr/colorControlHighlight"/>
<android.support.v4.widget.NestedScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@android:id/message"
style="@style/TextAppearance.AppCompat.Subhead"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="?attr/dialogPreferredPadding"
android:paddingTop="@dimen/abc_dialog_padding_top_material"
android:paddingRight="?attr/dialogPreferredPadding"/>
<View
android:id="@+id/textSpacerNoButtons"
android:visibility="gone"
android:layout_width="0dp"
android:layout_height="@dimen/abc_dialog_padding_top_material"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<View android:id="@+id/scrollIndicatorDown"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:background="?attr/colorControlHighlight"/>
</FrameLayout>
<FrameLayout
android:id="@+id/customPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minHeight="48dp">
<FrameLayout
android:id="@+id/custom"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
<include layout="@layout/abc_alert_dialog_button_bar_material" />
</LinearLayout>
// layout/abc_alert_dialog_button_bar_material.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.ButtonBarLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/buttonPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutDirection="locale"
android:orientation="horizontal"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:gravity="bottom"
app:allowStacking="@bool/abc_allow_stacked_button_bar"
style="?attr/buttonBarStyle">
<Button
android:id="@android:id/button3"
style="?attr/buttonBarNeutralButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<android.support.v4.widget.Space
android:id="@+id/spacer"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
android:visibility="invisible" />
<Button
android:id="@android:id/button2"
style="?attr/buttonBarNegativeButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@android:id/button1"
style="?attr/buttonBarPositiveButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</android.support.v7.widget.ButtonBarLayout>
※ <android.support.v4.widget.Space android:id="@+id/spacer"/>
는 필수입니다. 삭제하면 오류가 발생합니다.
appcompat 라이브러리에서 사용되는 자원을 쫓는 것으로, 어디에서 어떻게 style이나 속성이 사용되고 있는지 확인할 수 있기 때문에, 대상 사용자에 따라 사용하기 쉬운 디자인으로 해킹해보고 싶다고 생각합니다.
comments powered by Disqus
Subscribe to this blog via RSS.
LazyColumn/Row에서 동일한 Key를 사용하면 크래시가 발생하는 이유
Posted on 30 Nov 2024