Давайте сделаем небольшое приложение "на фрагментах".
Что мы видим?
На первый взгляд может показаться, что на экране мы видим два Activity при горизонтальной ориентации экрана и одно - при вертикальной. Это, конечно же, не так. В обоих случаях мы видим одно и то же Activity, но layout-ы для него система выбирает разные. Как этого добиться? Очень просто. Нужно положить один main.xml res/layout, а второй (для "портретной", т.е. вертикальной ориентации) в res/layout-port. Вот текст наших layout-ов:
<?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:orientation="horizontal" > <fragment android:id="@+id/listFragment" android:layout_width="150dip" android:layout_height="match_parent" class="net.multipi.fragtest.ListFragment" ></fragment> <fragment android:id="@+id/detailFragment" android:layout_width="match_parent" android:layout_height="match_parent" class="net.multipi.fragtest.DetailFragment" > </fragment> </LinearLayout>
И для вертикальной ориентации:
<?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:orientation="horizontal" > <fragment android:id="@+id/listFragment" android:layout_width="match_parent" android:layout_height="match_parent" class="net.multipi.fragtest.ListFragment" /> </LinearLayout>
Добиваемся кажущегося "склеивания" двух Activity при горизонтальной ориентации мы использованием двух элементов fragment. При вертикальной ориентации мы используем только один из них. А где же второй? Он при вертикальной ориентации будет принадлежать уже другому Activity и значит описывается в другом layout-е - details_activity_layout.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:orientation="vertical" > <fragment android:id="@+id/detailFragment" android:layout_width="match_parent" android:layout_height="match_parent" class="net.multipi.fragtest.DetailFragment" /> </LinearLayout>
Фрагменты!
В разметке с нашими фрагментами атрибутом class мы связываем два класса, наследующих android.support.v4.app.Fragment. Это ListFragment:
import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView; public class ListFragment extends android.support.v4.app.ListFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); String[] values = new String[] { "Android", "iPhone", "WindowsMobile", "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2" }; ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, values); setListAdapter(adapter); } @Override public void onListItemClick(ListView l, View v, int position, long id) { String item = (String) getListAdapter().getItem(position); DetailFragment fragment = (DetailFragment) getFragmentManager() .findFragmentById(R.id.detailFragment); if (fragment != null && fragment.isInLayout()) { fragment.setText(item); } else { Intent intent = new Intent(getActivity().getApplicationContext(), DetailActivity.class); intent.putExtra("value", item); startActivity(intent); } } }
и DetailsFragment:
import android.support.v4.app.Fragment; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class DetailFragment extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.details, container, false); return view; } public void setText(String item) { TextView view = (TextView) getView().findViewById(R.id.detailsText); view.setText(item); }
Обратите внимание на реализацию метода onItemListClick первого фрагмента. Тут мы пытаемся найти в layout-е второй фрагмент, и если находим - устанавливаем ему содержимое. А если его нет (вертикальная ориентация) - передаём это содержимое второму Activity через intent.putExtra(). Вообще, вся схема работы с фрагментами держится на таких вот "некрасивых" проверках.
Кстати, для второго фрагмента делаем отдельный layout - details.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:orientation="vertical" > <TextView android:id="@+id/detailsText" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center_horizontal|center_vertical" android:layout_marginTop="20dip" android:text="Large Text" android:textAppearance="?android:attr/textAppearanceLarge" android:textSize="30dip" /> </LinearLayout>
Activity: обязательное и не обязательное
Оба наши фрагмента замечательно уживаются в главном Activity при горизонтальной ориентации. Его код предельно прост, поэтому привожу его только для полноты картины:
import android.os.Bundle; import android.support.v4.app.FragmentActivity; public class MyActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } }
А вот второе Activity будет вызвано только при клике на элементе списка при вертикальном положении экрана. Вот его код:
import android.content.res.Configuration; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.widget.TextView; public class DetailActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { finish(); return; } setContentView(R.layout.details_activity_layout); Bundle extras = getIntent().getExtras(); if (extras != null) { String s = extras.getString("value"); TextView view = (TextView) findViewById(R.id.detailsText); view.setText(s); } } }
Тут снова ставим костыль: проверяем в onCreate ориентацию экрана и останавливаем Activity, чтобы вернуться на предыдущее, "двухстворчатое".
Вот такое у нас получилось приложение. Исходники его взяты отсюда.
В целом впечатление от технологии создаётся двойственное: с одной стороны есть возможность собирать интерфейсы из автономных "кубиков" в каждом из которых своя логика. Это здорово. С другой стороны мы получаем достаточно запутанную смесь xml и java, которая к тому же держится на нескольких костылях. Впрочем при реализации более сложной задачи преимущества технологии вероятно перевесят её недостатки.
спасибо!
ОтветитьУдалитьФрагменты доступны еще со времен honeycomb.
ОтветитьУдалитьфрагменты появились в третьей версии (API Level 11)
ОтветитьУдалитьТы не раздуваешь фрагмент DetailFragment в новом актитиви, а тупо разворачиваешь обычное view из xml. d(R.id.detailsText.
ОтветитьУдалить