Урок 112. Android 3. ActionBar. Динамическое размещение элементов


В этом уроке:

- программно размещаем элементы в ActionBar
- используем элементы из фрагментов


Недавно на форуме был вопрос о том, как во время работы программы менять содержимое ActionBar. Тема действительно интересная, и незаслуженно мною пропущенная. В этом уроке будем с ней разбираться.

Опробуем три способа работы с элементами:

1) Добавление/удаление MenuItem в объект Menu

2) Показ/скрытие группы в Menu

3) Элементы, относящиеся к фрагментам

С первыми двумя пунктами все понятно, эти механизмы мы рассматривали еще в Уроке 14.

Более интересен третий пункт. Фрагмент может реализовать в себе метод onCreateOptionsMenu и, тем самым, создать свои элементы для ActionBar. Как только фрагмент будет добавлен на экран, эти элементы добавятся в ActionBar. А когда фрагмент с экрана уберут, элементы исчезнут.

Создадим приложение, которое реализует три этих варианта.


Создадим проект:

Project name: P1121_DynamicActionBar
Build Target: Android 4.1
Application name: DynamicActionBar
Package name: ru.startandroid.develop.p1121dynamicactionbar
Create Activity: MainActivity


Добавим строки в strings.xml:

<string name="add_del">Добавить/удалить</string>
<string name="visible">Показать/скрыть</string>
<string name="fragment">Фрагмент</string>
<string name="frag1_text">Fragment 1</string>
<string name="frag2_text">Fragment 2</string>
<string name="menu_item1">Item 1</string>
<string name="menu_item2">Item 2</string>
<string name="menu_item31">Item 31</string>
<string name="menu_item32">Item 32</string>


Создадим два фрагмента. Начнем с layout-файлов.

fragment1.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#77ff0000"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/frag1_text">
</TextView>
</LinearLayout>


fragment2.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#7700ff00"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/frag2_text">
</TextView>
</LinearLayout>


Файлы с пунктами меню (они же - элементы ActionBar):

res/menu/fragment1.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/frag1_item"
android:icon="@android:drawable/ic_dialog_info"
android:showAsAction="ifRoom|withText"
android:title="@string/menu_item31">
</item>
</menu>


res/menu/fragment2.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/frag2_item"
android:icon="@android:drawable/ic_dialog_email"
android:showAsAction="ifRoom|withText"
android:title="@string/menu_item32">
</item>
</menu>

По одному элементу для каждого фрагмента. Эти элементы будут появляться в ActionBar при выводе фрагмента на экран.


Классы.

Fragment1.java:

package ru.startandroid.develop.p1121dynamicactionbar;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;

public class Fragment1 extends Fragment {

 
public void onCreate(Bundle savedInstanceState) {
   
setHasOptionsMenu(true);
   
super.onCreate(savedInstanceState);
 
}

 
public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState
) {
   
return inflater.inflate(R.layout.fragment1, null);
 
}

 
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
   
inflater.inflate(R.menu.fragment1, menu);
   
super.onCreateOptionsMenu(menu, inflater);
 
}

}


Fragment2.java:

package ru.startandroid.develop.p1121dynamicactionbar;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;

public class Fragment2 extends Fragment {
 
 
public void onCreate(Bundle savedInstanceState) {
   
setHasOptionsMenu(true);
   
super.onCreate(savedInstanceState);
 
}
 
 
public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState
) {
   
return inflater.inflate(R.layout.fragment2, null);
 
}
 
 
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
   
inflater.inflate(R.menu.fragment2, menu);
   
super.onCreateOptionsMenu(menu, inflater);
 
}

}

В onCreate с помощью setHasOptionsMenu включаем режим вывода элементов фрагмента в ActionBar.

В onCreateView создаем View, в onCreateOptionsMenu – меню. Все как обычно.

Фрагменты готовы.


Теперь займемся Activity. Перепишем res/layout/main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<CheckBox
android:id="@+id/chbAddDel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@string/add_del">
</CheckBox>
<CheckBox
android:id="@+id/chbVisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@string/visible">
</CheckBox>
<Button
android:id="@+id/btnFrag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@string/fragment">
</Button>
<FrameLayout
android:id="@+id/cont"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</LinearLayout>

Два чекбокса и кнопка. Чекбоксы отвечают за работу с элементами с помощью первого и второго способов. Кнопка будет показывать поочередно два фрагмента в контейнере cont.


res/menu/main.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<group
android:id="@+id/groupVsbl">
<item
android:id="@+id/item2"
android:icon="@android:drawable/ic_menu_call"
android:showAsAction="always|withText"
android:title="@string/menu_item2">
</item>
</group>
</menu>

Создаем группу, а в ней элемент. Эту группу будем скрывать и показывать.


MainActivity.java:

package ru.startandroid.develop.p1121dynamicactionbar;

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;

public class MainActivity extends Activity {

 
final int MENU_ID = 1;

  CheckBox chbAddDel;
  CheckBox chbVisible;

  Fragment frag1;
  Fragment frag2;
  Fragment frag;

 
@Override
 
public void onCreate(Bundle savedInstanceState) {
   
super.onCreate(savedInstanceState);
    setContentView
(R.layout.main);

    chbAddDel =
(CheckBox) findViewById(R.id.chbAddDel);
    chbVisible =
(CheckBox) findViewById(R.id.chbVisible);

    frag = frag1 =
new Fragment1();
    frag2 =
new Fragment2();

 
}

 
@Override
 
public boolean onCreateOptionsMenu(Menu menu) {
   
getMenuInflater().inflate(R.menu.main, menu);
    menu.setGroupVisible
(R.id.groupVsbl, chbVisible.isChecked());
   
if (chbAddDel.isChecked()) {
     
menu.add(0, MENU_ID, 0, R.string.menu_item1)
         
.setIcon(android.R.drawable.ic_delete)
         
.setShowAsAction(
             
MenuItem.SHOW_AS_ACTION_ALWAYS
                  | MenuItem.SHOW_AS_ACTION_WITH_TEXT
);
   
} else {
     
menu.removeItem(MENU_ID);
   
}
   
return true;
 
}

 
public void onClick(View view) {
   
switch (view.getId()) {
   
case R.id.chbAddDel:
   
case R.id.chbVisible:
      invalidateOptionsMenu
();
     
break;
   
case R.id.btnFrag:
      frag =
(frag == frag1) ? frag2 : frag1;
      getFragmentManager
().beginTransaction().replace(R.id.cont, frag)
         
.commit();
     
break;
   
default:
     
break;
   
}

  }
}

В onCreateOptionsMenu настраиваем видимость группы groupVsbl в зависимости от значения чекбокса chbVisible.

В зависимости от значения чекбокса chbAddDel создаем или удаляем элемент.

В onСlick для чекбоксов вызываем метод invalidateOptionsMenu - перерисовка меню/ActionBar. А по нажатию на кнопку поочередно выводим на экран Fragment1 или Fragment2.


Все сохраняем и запускаем приложение.


Жмем галку Добавить/удалить. Появляется элемент. Мы добавили MenuItem в Menu.


Жмем галку Показать/скрыть. Появляется элемент. Мы показали группу меню, в которой один пункт.

Соответственно, убирая галки - убираете элементы.


Понажимаем кнопку Фрагмент. Появляется элемент то одного, то другого фрагмента.



Еще, как вариант, можно играться с видимостью не всей группы, а конкретного элемента - метод setVisible.

Если в ActionBar нажать на overflow-кнопку, будет вызван метод onPrepareOptionsMenu для Activity и для фрагмента, который сейчас отображен. 


На следующем уроке:

- работаем с ActionMode