[Программирование] Работа со строкой состояния в Android

    image
    Статья рассказывает о том как работать со строкой состояния в Android. Статья рассчитана на начинающих программистов под Android только осваивающих разработку под платформу. Также стоит заметить что строка состояния в большей степени используется для уведомления пользователя о каких либо событиях произошедших в системе, что по целевой задаче роднит её со всплывающими уведомлениями Toast. На Хабрахабре уже имеется достаточно полная статья о работе с Toast, и данный пост лишь развивает тему уведомлений.

    Сам текст статьи писался для песочницы, и по сути Hello world как он есть. Сейчас меня интересует интересно ли вообще кому либо это направление, или же оно того не стоит.


    Кратко о строке уведомления


    На первом скриншоте представлена раскрытая строка состояния в стандартной оболочке Android (некоторые производители мобильных устройств в своих оболочках могут изменить её внешний вид, хотя суть остаётся той же). Строка состояния в Android по большей части используется для уведомления пользователя о каких либо событиях произошедших в системе, а также о результатах работы каких либо приложений. Существенным отличием от всплывающих уведомлений Toast является то что уведомления в строке состояния не пропадают спустя время, и «висят» там до тех пор пока пользователь как-то на них отреагирует. Строку состояния удобно использовать для получения уведомлений от приложений запущенных в фоновом режиме, а также с появлением уведомления можно проиграть какой либо звук, вибрацию, или же воспользоваться мигающими индикаторами на устройстве (если они имеются). Уведомление представленное на скриншоте — именно то чего сегодня мы и будем добиваться.

    Создание простого уведомления в строке состояния


    Для начала попробуем создать стандартное уведомление для командной строки так как это рекомендуют в Google. Разметку интерфейса приложения оставим без изменений (уведомление будет появляться сразу после его запуска). И так, пример кода (с комментарием того что возможно может быть не ясным):

    public class NotificationBar extends Activity {
      /** Called when the activity is first created. */
      
      private static final int NOTIFY_ID = 1; // Уникальный индификатор вашего уведомления в пределах класса
      @Override
      
        public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Создаем экземпляр менеджера уведомлений
        int icon = android.R.drawable.sym_action_email; // Иконка для уведомления, я решил воспользоваться стандартной иконкой для Email
        CharSequence tickerText = "Hello Habrahabr"; // Подробнее под кодом
        long when = System.currentTimeMillis(); // Выясним системное время
        Notification notification = new Notification(icon, tickerText, when); // Создаем экземпляр уведомления, и передаем ему наши параметры
        Context context = getApplicationContext(); 
        CharSequence contentTitle = "Habrahabr"; // Текст заголовка уведомления при развернутой строке статуса
        CharSequence contentText = "Пример простого уведомления"; //Текст под заголовком уведомления при развернутой строке статуса
        Intent notificationIntent = new Intent(this, NotificationBar.class); // Создаем экземпляр Intent
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);class); // Подробное описание в UPD к статье
        notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent); // Передаем в наше уведомление параметры вида при развернутой строке состояния
        mNotificationManager.notify(NOTIFY_ID, notification); // И наконец показываем наше уведомление через менеджер передав его ID
      }
    }

    * This source code was highlighted with Source Code Highlighter.

    CharSequence tickerText = «Hello Habrahabr»; — в этой строке мы указываем текст который будет показан в свёрнутой строке состояния на несколько секунд при появлении уведомления. Спустя несколько секунд он исчезнет, а в строке останется лишь иконка.



    Вот пожалуй и всё. Можно открывать шампанское, и наблюдать то что у нас получилось.

    image

    Создание расширенного уведомления в строке состояния


    Теперь несколько усложним задачу — мы будем создавать уведомление не по шаблону что предлагает Google, а по собственной разметке (благо такая возможность имеется). И так создадим новый файл разметки в папке layout, у вас он должен получиться таким:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
           android:orientation="horizontal"
           android:layout_width="fill_parent"
           android:layout_height="fill_parent"
           android:padding="3dp"
           >
      <ImageView android:id="@+id/image"
           android:layout_width="wrap_content"
           android:layout_height="fill_parent"
           android:layout_marginRight="10dp"
           />
      <TextView android:id="@+id/text"
           android:layout_width="wrap_content"
           android:layout_height="fill_parent"
           android:textColor="#000"
           />
    </LinearLayout>


    * This source code was highlighted with Source Code Highlighter.

    Также добавим какую либо картинку в папку drawable дабы потом установить её в ImageView.

    Теперь код. Код не сильно отличается от кода простого уведомления, но тем не менее требует комментария
    public class NotificationBar extends Activity {
      /** Called when the activity is first created. */
      
      private static final int NOTIFY_ID = 1; // Уникальный индификатор вашего уведомления в пределах класса
      @Override
      
        public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Создаем экземпляр менеджера уведомлений
        int icon = android.R.drawable.sym_action_email; // Иконка для уведомления, я решил воспользоваться стандартной иконкой для Email
        CharSequence tickerText = "Hello Habrahabr"; // Подробнее под кодом
        long when = System.currentTimeMillis(); // Выясним системное время
        Intent notificationIntent = new Intent(this, NotificationBar.class); // Создаем экземпляр Intent
        Notification notification = new Notification(icon, tickerText, when); // Создаем экземпляр уведомления, и передаем ему наши параметры
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); // Подробное описание смотреть в UPD к статье
        RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.notlayout); // Создаем экземпляр RemoteViews указывая использовать разметку нашего уведомления
        contentView.setImageViewResource(R.id.image, R.drawable.habr); // Привязываем нашу картинку к ImageView в разметке уведомления
        contentView.setTextViewText(R.id.text,"Привет Habrahabr! А мы тут, плюшками балуемся..."); // Привязываем текст к TextView в нашей разметке
        notification.contentIntent = contentIntent; // Присваиваем contentIntent нашему уведомлению
        notification.contentView = contentView; // Присваиваем contentView нашему уведомлению
        mNotificationManager.notify(NOTIFY_ID, notification); // Выводим уведомление в строку
        }
    }


    * This source code was highlighted with Source Code Highlighter.

    В итоге можем открывать вторую бутылку, и наблюдать примерно такую картину:

    image

    Добавляем звук и вибрацию, мигаем индикаторами.


    Для пущей важности добавим звук при выводе уведомления и вибрацию. Сделать это совсем не сложно.

    notification.defaults |= Notification.DEFAULT_SOUND; — данная строка присваивает уведомлению звук что используется в системе по умолчанию.
    notification.sound = Uri.parse(«file:///sdcard/notification/ringer.mp3»); — таким способом можно установить звук из файла на SD карте.

    notification.defaults |= Notification.DEFAULT_VIBRATE; — данная строка добавляет вибрацию в колличестве времени по умолчанию.

    Кроме того имеется возможность задать время вибрации самостоятельно. Делается это двумя строчками
    long[] vibrate = {0,100,200,300}; — создаем массив, в котором 1-ое число — время которое следует подождать до того как запустить вибрацию. Второе значение — время первой вибрации в миллисекундах (аналогично и 3, и 4 значение). Количество вибраций может быть бесконечно большим по усмотрению программиста.
    notification.vibrate = vibrate; — присваиваем массив нашему уведомлению.

    notification.defaults |= Notification.DEFAULT_LIGHTS; — данной строкой мы можем по мигать индикаторами с параметрами по умолчанию.

    Конечно можно настроить параметры и в ручную.Для этого нам потребуется 4 строки:
    notification.ledARGB = 0xff00ff00; — задаем цвет для RGB индикатора.
    notification.ledOnMS = 300; — задаем время между миганиями
    notification.ledOffMS = 1000; — задаем время спустя которое горящий индикатор потухнет
    notification.flags |= Notification.FLAG_SHOW_LIGHTS; — разрешаем мигать
    Тут следует заметить что далеко не на всех девайсах вообще имеются какие либо индикаторы.

    Естественно все эти установки нужно производить до строчки вызова уведомления.

    Заключение


    Вот пожалуй и всё. Пост никак не претендует на полноту, и всё же данной информации должно быть вполне достаточно новичкам. В качестве источника, а также более полного описания работы с NotificationBar могу привести оригинальную статью на developer.android.com. Надеюсь статья поможет кому либо в изучении возможностей платформы. Спасибо за внимание.

    UPD: при внимательном рассмотрении выяснилось что комментарий к одной из строчек кода отсутствует (обещается объяснить под кодом, но самого объяснения нет). Досадную ошибку исправляет, а также ряд ф-и строки состояния о которых я сообщить забыл восполняет пользователь djvu, взглянуть можно в комментариях: 1, 2. О существовании серьезных подводных камней сообщает sdmitry вот тут.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 28

      –2
      При написании статьи ни одна бутылка не пострадала.

        +9
        Меня интересует интересно ли вообще кому либо это направление, или же оно того не стоит.

        Мне кажется, что разработка под Android сейчас очень перспективное направление.
          0
          Тоже выскажусь о том, что направление интересно. Хотя это видно и по количеству плюсов. Было бы неинетересно, минусовали бы по-любас.

          Перспективное оно, или нет — это не важно. Оно востребованное. Как минимум для себя программки пописать могут захотеть многие. А значит тема жива и писать нужно.
          +1
          Разработка — да, согласен. А вот статьи для начинающих как мне кажется актуальность успели растерять (судя по тому что эта статья в своё время инвайт не получила). С другой стороны в песочницу заглядывают далеко не все, а потому мне интересно как на неё отреагируют сейчас.
            +3
            Я не знаю как кому, но мне нравится читать подобные статьи.
            Написано все коротко, ясно и по делу.
            Пишите по больше таких статей, и у Вас будет много читателей.
              0
              Думаю неплохим индикатором будет служить количество закладок на эту статью :)
              Спасибо за статью, очень надеюсь на продолжение цикла, тем более в таком стиле.
              +2
              Не знаю, как другим, но мне интересно (неделю назад приехал Desire HD), поэтому спасибо!
                0
                Отлично расписано, четко и по делу.
                  0
                  Интересно.
                    +1
                    Ну тогда рад что хоть время не пропало :). В иной раз попробую взять тему по серьёзней.
                      0
                      А можно как-нибудь скрыть статусбар? А то в Sense она слишком широкая, и очень с ней некрасиво все смотрится…
                        0
                        В сторонних лончерах можно настроить скрытый статусбар, или повесить скрытие, например, на свайп по иконке быстрого доступа внизу экрана. Даже на Desire я перешёл на LauncherPro, шустрее намного. Однако, пришлось отказаться от Сенсовых виджетов, но аналоги найти не проблема.
                          +1
                          Если вы хотите сделать свое приложение полноэкранным (что бы статусбар был невиден),
                          то решение вот:
                              public void onCreate(Bundle savedInstanceState) {
                                  super.onCreate(savedInstanceState);
                                  // No Title bar
                                  requestWindowFeature(Window.FEATURE_NO_TITLE);
                                  ...
                              }
                          

                          Если вы конечно именно это имели ввиду
                            0
                            либо через AndroidManifest, но это для собственного приложения. Я же понял, спрашивают про лончер, или нет? Наличие статус-бара в приложениях является нормой, в нём заключается привычный юзер-экспириенс (наличие часов и иконок состояния, из него вытягивается обсуждаемая строка уведомлений и пр.), ограничение пользователя без необходимой на то причины считается дурным тоном.
                          0
                          спасибо
                            +6
                            Для полноты описания нехватает только пары нужных в хозяйстве фишек:
                            1) Контроль за нажатием на нотификейшен
                            В статье не пояснил, что это связь «клика» по нотификейшену и запуска активити(можно любой активити привязывать):
                            PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MyActivity.class), 0);
                            ...
                            notification.setLatestEventInfo(this, getText(R.string.app_name), message, contentIntent);
                            

                            Пример простого скрытия нотификейшена если по нему «кликнуть»:
                            PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(), 0);
                            notification.flags |= Notification.FLAG_AUTO_CANCEL;
                            ...
                            notification.setLatestEventInfo(this, getText(R.string.app_name), message, contentIntent);
                            


                            2) Ручное убирание/блокировка убирания по «Clear events»
                            Блокируем скрытие:
                            notification.flags |= Notification.FLAG_ONGOING_EVENT;//do not hide notification after press "Clear events"
                            

                            Скрываем вручную (например в onDestroy()):
                            mNM.cancel(NOTIFY_ID); //(mNM - NotificationManager)  Remove notification from tray by id
                            
                              0
                              Да, одна из фишек нотификейшенов – быстрый переход к приложению, их вызвавшему. Странно, что в статье это упустили.

                              А ещё очень сильно интересует создание постоянных уведомлений, которые располагаются в разделе «Текущие» и не исчезают при нажатии «Очистить». Точнее, не столь создание, сколько привязка и управление. Это просьба автору, как вариант следующей статьи, или для обсуждения в комментариях.
                                0
                                Спасибо за дополнение, вы безусловно правы. Более того — объяснение первого пункта планировалось, но когда дошел до строки задумался как бы об этом рассказать не вдаваясь в глубокие подробности. Пытался описать красиво, но т.к. до мастера слога мне далеко, и уже поздно было — отложил на следующий день, а после успешно об этом забыл, и без задней мысли дописав вывод отправил как есть. Спасибо за исправление моих ошибок.
                                0
                                Оставил UPD к статье с ссылками на поправки в комментариях, ещё раз спасибо.
                                  0
                                  Ваше расширенное уведомление будет нечитабельно на устройствах HTC и может быть других — будет черный текст на черном фоне. Некоторые популярные программы на этот счет имеют 2 цветовые схемы (нормальная и «инвертированая»). Я бы предпочел, что бы статьи для новичков рассказывали в первую очередь о подводных камнях, а не только пересказ примеров.
                                    0
                                    К сожалению не разу не держал HTC в руках, а потому сам о таком подводном камне не знал :). Спасибо за поправку.
                                      0
                                      Добавил замечание в UPD.
                                        +1
                                        Если не использовать свой layout, то система сама позаботится о цветах.
                                          0
                                          Мое замечание относиться только к расширенному уведомлению, а его смысл и есть в кастомном layout.
                                            +1
                                            Раскопал в исходниках системные стили, можно указывать их или отдельно цвета из них:
                                            <style name="TextAppearance.StatusBar">
                                                <item name="android:textSize">14sp</item>
                                                <item name="android:textStyle">normal</item>
                                                <item name="android:textColor">?android:attr/textColorPrimary</item>
                                            </style>
                                            <style name="TextAppearance.StatusBar.Ticker">
                                            </style>
                                            <style name="TextAppearance.StatusBar.Title">
                                                <item name="android:textStyle">bold</item>
                                            </style>
                                            
                                            <style name="TextAppearance.StatusBar.Icon">
                                                <item name="android:textStyle">bold</item>
                                            </style>
                                            <style name="TextAppearance.StatusBar.EventContent">
                                                <item name="android:textColor">#ff6b6b6b</item>
                                            </style>
                                            <style name="TextAppearance.StatusBar.EventContent.Title">
                                                <item name="android:textSize">16sp</item>
                                                <item name="android:textStyle">bold</item>
                                                <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
                                            </style>
                                            
                                    +1
                                    Спасибо за статью! Для старта — то что нужно: просто и доходчиво.
                                      +1
                                      баааальшое спасибо!
                                      продолжайте писать, интересно читать и учиться
                                        0
                                        а кто-то может на пальцах объяснить про канвас и анимацию (в частности интересует работа с таймером).

                                        Only users with full accounts can post comments. Log in, please.