В этом документе рассказывается, как следовать стилю Glass и внедрять общие передовые методы пользовательского интерфейса при использовании GDK.
Стеклянная тема
Glass применяет к вашему Glassware стандартную тему, чтобы она соответствовала остальному пользовательскому интерфейсу. Тема имеет следующие характеристики:
- Использует шрифт Roboto
- Отображает действия в полноэкранном режиме без строки состояния или панели действий.
- Применяет сплошной черный фон
Чтобы применить тему Glass, не объявляйте тему в манифесте Android.
Если у вас есть пользовательский стиль для частей вашего Glassware и вы хотите использовать тему Glass по умолчанию для всего остального, наследуйте от Theme.DeviceDefault
с parent
атрибутом:
<resources>
<style name="CustomTheme" parent="@android:style/Theme.DeviceDefault">
<!-- Theme customization goes here. -->
</style>
</resources>
Дополнительную информацию о создании тем см. в руководстве разработчика Android по стилям и темам .
Карты в стиле стекла
Класс CardBuilder
создает карты правильного формата с заданным набором свойств. По возможности используйте макеты, предоставляемые CardBuilder.Layout
, чтобы ваш контент выглядел и воспринимался как другой контент на Glass.
Чтобы использовать CardBuilder
:
- Создайте экземпляр
CardBuilder
, задав ему желаемый макет изCardBuilder.Layout
. - Задайте свойства карточки, например текст, сноску и отметку времени.
- Вызовите
CardBuilder.getView()
, чтобы преобразовать карту вView
Android, илиCardBuilder.getRemoteViews()
, чтобы преобразовать ее в объектRemoteViews
. - Используйте
View
в своих действиях, макетах или вCardScrollView
или используйтеRemoteViews
вLiveCard
.
Общие функции пользовательского интерфейса
Многие макеты, предоставляемые CardBuilder
поддерживают общие функции пользовательского интерфейса, описанные ниже. См. документацию по отдельным макетам в CardBuilder.Layout
для получения списка функций, поддерживаемых каждым типом карт.
Значок авторства
Значок атрибуции — это необязательный значок размером 36 × 36 пикселей, который появляется в правом нижнем углу карточки и справа от метки времени. Установите этот значок, вызвав CardBuilder.setAttributionIcon()
, чтобы идентифицировать ваше приложение, особенно на активных картах, чтобы пользователь мог быстро просмотреть и увидеть источник информации на этой карте.
Индикатор стека
Индикатор стека, управляемый CardBuilder.showStackIndicator()
, представляет собой угловую складку, которая появляется в правом верхнем углу карты. Используйте это как визуальный индикатор того, что ваша карта представляет собой набор других карт, к которым пользователь может напрямую подключиться.
View view = new CardBuilder(context, CardBuilder.Layout.TEXT)
.setText("A stack indicator can be added to the corner of a card...")
.setAttributionIcon(R.drawable.ic_smile)
.showStackIndicator(true)
.getView();
Макеты
В следующих примерах показаны макеты, доступные с помощью CardBuilder
.
TEXT
и TEXT_FIXED
Макет CardBuilder.Layout.TEXT
показывает текст без полей с необязательным мозаичным изображением на заднем плане. Размер текста динамически изменяется, чтобы наилучшим образом соответствовать доступному пространству. CardBuilder.Layout.TEXT_FIXED
аналогичен, но исправляет свой текст до меньшего размера.
View view1 = new CardBuilder(context, CardBuilder.Layout.TEXT)
.setText("This is the TEXT layout. The text size will adjust dynamically.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.getView();
View view2 = new CardBuilder(context, CardBuilder.Layout.TEXT)
.setText("You can also add images to the background of a TEXT card.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.addImage(R.drawable.image1)
.addImage(R.drawable.image2)
.addImage(R.drawable.image3)
.addImage(R.drawable.image4)
.addImage(R.drawable.image5)
.getView();
View view3 = new CardBuilder(context, CardBuilder.Layout.TEXT_FIXED)
.setText("This is the TEXT_FIXED layout. The text size is always the same.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.getView();
COLUMNS
и COLUMNS_FIXED
Макет CardBuilder.Layout.COLUMNS
показывает мозаику изображения или значок с левой стороны карточки и текст с правой стороны. Текст имеет динамический размер, чтобы наилучшим образом соответствовать доступному пространству. Чтобы сохранить фиксированный размер текста, используйте CardBuilder.Layout.COLUMNS_FIXED
.
View view1 = new CardBuilder(context, CardBuilder.Layout.COLUMNS)
.setText("This is the COLUMNS layout with dynamic text.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.addImage(R.drawable.image1)
.addImage(R.drawable.image2)
.addImage(R.drawable.image3)
.addImage(R.drawable.image4)
.addImage(R.drawable.image5)
.getView();
View view2 = new CardBuilder(context, CardBuilder.Layout.COLUMNS)
.setText("You can even put a centered icon on a COLUMNS card instead of a mosaic.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.setIcon(R.drawable.ic_wifi)
.getView();
View view3 = new CardBuilder(context, CardBuilder.Layout.COLUMNS_FIXED)
.setText("This is the COLUMNS_FIXED layout. The text size is always the same.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.addImage(R.drawable.image1)
.addImage(R.drawable.image2)
.addImage(R.drawable.image3)
.addImage(R.drawable.image4)
.addImage(R.drawable.image5)
.getView();
CAPTION
Макет CardBuilder.Layout.CAPTION
имеет мозаичное изображение на заднем плане и краткий текст подписи, выровненный по нижней части карточки. Значок также может быть размещен рядом с заголовком, чтобы представить, например, личность человека, связанного с содержимым карты.
View view1 = new CardBuilder(context, CardBuilder.Layout.CAPTION)
.setText("The caption layout.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.addImage(R.drawable.beach)
.setAttributionIcon(R.drawable.ic_smile)
.getView();
View view2 = new CardBuilder(context, CardBuilder.Layout.CAPTION)
.setText("The caption layout with an icon.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.addImage(R.drawable.beach)
.setIcon(R.drawable.ic_avatar)
.setAttributionIcon(R.drawable.ic_smile)
.getView();
TITLE
Макет CardBuilder.Layout.TITLE
имеет мозаичное изображение на заднем плане с заголовком по центру и необязательным значком в нижней части карточки. Этот макет часто используется для представления контактов или обмена целями. Сноска и метка времени не поддерживаются в этом макете.
View view = new CardBuilder(context, CardBuilder.Layout.TITLE)
.setText("TITLE Card")
.setIcon(R.drawable.ic_phone)
.addImage(R.drawable.beach)
.getView();
AUTHOR
Используйте макет CardBuilder.Layout.AUTHOR
для отображения сообщения или беседы, в которой основное внимание уделяется автору. Он поддерживает мозаичное изображение на заднем плане, значок, используемый в качестве аватара автора, а также заголовок и подзаголовок, в которых вы можете перечислить идентифицирующую информацию.
View view = new CardBuilder(context, CardBuilder.Layout.AUTHOR)
.setText("The AUTHOR layout lets you display a message or conversation "
+ " with a focus on the author.")
.setIcon(R.drawable.ic_avatar)
.setHeading("Joe Lastname")
.setSubheading("Mountain View, California")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.getView();
MENU
Макет CardBuilder.Layout.MENU
выглядит как стандартное меню Glass. Он имеет центрированный значок и заголовок, а также необязательную сноску. Используйте этот макет для экранов подтверждения (например, переход от «Удаление» к «Удалено» после выбора пользователем пункта меню). Если вам нужно настоящее меню, вы должны вместо этого использовать стандартное меню опций.
View view = new CardBuilder(context, CardBuilder.Layout.MENU)
.setText("MENU layout")
.setIcon(R.drawable.ic_phone)
.setFootnote("Optional menu description")
.getView();
EMBED_INSIDE
Макет CardBuilder.Layout.EMBED_INSIDE
встраивает пользовательский XML-код макета вашего собственного дизайна в стандартный шаблон карты Glass. Это позволяет вам разработать собственный пользовательский интерфейс для вашего приложения, но при этом иметь правильное размещение сноски карты, метки времени, значка атрибуции и индикатора стека, если они необходимы.
После вызова CardBuilder.getView()
используйте findViewById()
для результата, чтобы получить доступ к представлениям внутри вашего встроенного макета. Аналогичным образом, если вы вызываете CardBuilder.getRemoteViews()
, вы можете манипулировать представлениями вашего встроенного макета, передавая их идентификаторы непосредственно в методы установки RemoteViews
.
View view = new CardBuilder(context, CardBuilder.Layout.EMBED_INSIDE)
.setEmbeddedLayout(R.layout.food_table)
.setFootnote("Foods you tracked")
.setTimestamp("today")
.getView();
TextView textView1 = (TextView) view.findViewById(R.id.text_view_1);
textView1.setText("Water");
// ...and so on
Более подробный пример смотрите в проекте GitHub ApiDemo .
ALERT
Макет CardBuilder.Layout.ALERT
содержит большой значок по центру с основным сообщением и сноской. Используйте этот макет в Dialog
, чтобы отобразить важное информационное сообщение, предупреждение или ошибку в Glassware.
В следующем примере показана реализация AlertDialog
, которая закрывает карту и открывает настройки Wi-Fi, когда пользователь касается карты:
- Создайте класс, расширяющий
Dialog
. - Создайте карточку с помощью
CardBuilder
с макетомCardBuilder.Layout.ALERT
, а затем установите представление содержимого с этой карточкой. (Необязательно) Создайте
GestureDetector
для обработки жестов пользователя на этой карточке.public class AlertDialog extends Dialog { private final DialogInterface.OnClickListener mOnClickListener; private final AudioManager mAudioManager; private final GestureDetector mGestureDetector; /** * Handles the tap gesture to call the dialog's * onClickListener if one is provided. */ private final GestureDetector.BaseListener mBaseListener = new GestureDetector.BaseListener() { @Override public boolean onGesture(Gesture gesture) { if (gesture == Gesture.TAP) { mAudioManager.playSoundEffect(Sounds.TAP); if (mOnClickListener != null) { // Since Glass dialogs do not have buttons, // the index passed to onClick is always 0. mOnClickListener.onClick(AlertDialog.this, 0); } return true; } return false; } }; public AlertDialog(Context context, int iconResId, int textResId, int footnoteResId, DialogInterface.OnClickListener onClickListener) { super(context); mOnClickListener = onClickListener; mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mGestureDetector = new GestureDetector(context).setBaseListener(mBaseListener); setContentView(new CardBuilder(context, CardBuilder.Layout.ALERT) .setIcon(iconResId) .setText(textResId) .setFootnote(footnoteResId) .getView()); } /** Overridden to let the gesture detector handle a possible tap event. */ @Override public boolean onGenericMotionEvent(MotionEvent event) { return mGestureDetector.onMotionEvent(event) || super.onGenericMotionEvent(event); } }
(Необязательно) В своем действии внедрите
OnClickListener
для обработки любых дополнительных потоков, когда пользователь нажимает. Дополнительные сведения о запуске действий по настройке, таких как Wi-Fi, см. в разделе Начальные настройки .Вызовите конструктор
AlertDialog
, чтобы отобразить карточку предупреждения.public class MyActivity extends Activity { ... private final DialogInterface.OnClickListener mOnClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int button) { // Open WiFi Settings startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)); } }; @Override protected void onCreate(Bundle bundle) { ... new AlertDialog(context, R.drawable.ic_cloud_sad_150, R.string.alert_text, R.string.alert_footnote_text, mOnClickListener).show(); ... } }
XML-макеты
Вот два основных макета карточек, которые вы можете использовать, если класс CardBuilder не соответствует вашим потребностям.
Основной макет
Этот макет определяет стандартный отступ и нижний колонтитул для карты. Поместите свои представления в пустой RelativeLayout
.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<RelativeLayout
android:id="@+id/body_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/glass_card_body_height"
android:layout_marginLeft="@dimen/glass_card_margin"
android:layout_marginTop="@dimen/glass_card_margin"
android:layout_marginRight="@dimen/glass_card_margin"
tools:ignore="UselessLeaf"
>
<!-- Put your widgets inside this RelativeLayout. -->
</RelativeLayout>
<LinearLayout
android:id="@+id/footer_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|left"
android:layout_marginLeft="@dimen/glass_card_margin"
android:layout_marginBottom="@dimen/glass_card_footer_margin"
android:layout_marginRight="@dimen/glass_card_margin"
android:orientation="horizontal"
>
<!-- The footer view will grow to fit as much content as possible while the
timestamp view keeps a fixed width. If the footer text is too long, it
will be ellipsized with a 40px margin between it and the timestamp. -->
<TextView
android:id="@+id/footer"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/glass_card_margin"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
</LinearLayout>
</FrameLayout>
Макет левой колонки
Это определяет левый столбец размером 240 пикселей и правый столбец размером 400 пикселей в виде двух RelativeLayout
, в которые вы можете поместить свои представления.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<RelativeLayout
android:id="@+id/left_column"
android:layout_width="@dimen/glass_card_left_column_width"
android:layout_height="match_parent"
>
<!-- Put widgets for the left column inside this RelativeLayout. -->
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="@dimen/glass_card_body_height"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="@dimen/glass_card_two_column_margin"
android:layout_marginRight="@dimen/glass_card_margin"
android:layout_marginTop="@dimen/glass_card_margin"
android:layout_toRightOf="@+id/left_column"
tools:ignore="UselessLeaf"
>
<!-- Put widgets for the right column inside this RelativeLayout. -->
</RelativeLayout>
<LinearLayout
android:id="@+id/footer_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_gravity="bottom|left"
android:layout_marginBottom="@dimen/glass_card_footer_margin"
android:layout_marginLeft="@dimen/glass_card_two_column_margin"
android:layout_marginRight="@dimen/glass_card_margin"
android:layout_toRightOf="@+id/left_column"
android:orientation="horizontal"
>
<!--
The footer view will grow to fit as much content as possible while the
timestamp view keeps a fixed width. If the footer text is too long, it
will be ellipsized with a 40px margin between it and the timestamp.
-->
<TextView
android:id="@+id/footer"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/glass_card_margin"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
</LinearLayout>
</RelativeLayout>
Стандартные размеры
Используйте этот файл в сочетании с предыдущими макетами или собственными макетами, чтобы придерживаться стандартного стиля Glass. Создайте этот файл как res/values/dimens.xml
в своем проекте Android.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- The recommended margin for the top, left, and right edges of a card. -->
<dimen name="glass_card_margin">40px</dimen>
<!-- The recommended margin between the bottom of the card and the footer. This is
an adjusted value so that the baseline of the text in the footer sits 40px
from the bottom of the card, matching the other margins. -->
<dimen name="glass_card_footer_margin">33px</dimen>
<!-- The recommended margin for the left column of the two-column card. -->
<dimen name="glass_card_two_column_margin">30px</dimen>
<!-- The maximum height of the body content inside a card. -->
<dimen name="glass_card_body_height">240px</dimen>
<!-- The width of the left column in the two-column layout. -->
<dimen name="glass_card_left_column_width">240px</dimen>
</resources>