понедельник, 25 марта 2013 г.

Работаем с Fragment-ами в Android

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

Примеры стараюсь элементарными делать для понимания их работы, комментарии так же буду стараться приводить к каждой строчке.
Создаем проект в еклипсе. И сразу сходу смотрим есть ли в папке libs файл android-support-v4.jar, если его нету значит мы не сможем использовать фрагменты в нашей программе, по этому скачиваем его отсюда, но по идее он должен быть так как в новых сборках android sdk его вшили в проекты.

Разметка


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

layout/main.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="horizontal" >
    <fragment
            android:id="@+id/listFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            class="fragments.ListFragment" />
</LinearLayout>


Тут у нас идет вызов класса фрагмента, тут у нас он вызывается один, только для вертикальной разметки, так же нам нужно создать такой же файл только в папке layout-land и он будет содержать в себе уже горизонтальную разметку со вторым фрагментом.



layout-land/main.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="horizontal" >
    <fragment
            android:id="@+id/listFragment"
            android:layout_width="150dip"
            android:layout_height="match_parent"
            class="fragments.ListFragment" ></fragment>
    <fragment
            android:id="@+id/detailFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            class="fragments.DetailFragment" ></fragment>
</LinearLayout>


Как видите тут мы вызываем уже второй фрагмент, выглядеть он будет так:


и теперь нам нужно описать второй фрагмент для склеивания в главной активности, для сглаживания

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="fragments.DetailFragment" />
</LinearLayout> 


Теперь нам нужно создать дополнительные элементы которые будут заполнять эти фрагменты. У нас их будет всего лишь два, первый будет списком для отображения списка, а второй будет обычным текствью который будет отображать выбраню категорию по клику из листвью.

list_mobile.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="5dp" >

    <ImageView
        android:id="@+id/logo"
        android:layout_width="50px"
        android:layout_height="50px"
        android:layout_marginLeft="5px"
        android:layout_marginRight="20px"
        android:layout_marginTop="5px" >
    </ImageView>

    <TextView
        android:id="@+id/label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@+id/label"
        android:textSize="30px" >
    </TextView>
</LinearLayout>


А вот и второй

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> 




Так с разметкой вроде бы все понятно, пока вроде бы ничего сложного не случилось. 

Пояснение:
У нас есть два фрагмента которые нам нужно отобразить на экране, для этого мы создали первый фрагмент которым у нас является main.xml, и создали второй details_activity_layout.xml, дальше мы создали в папочке layout-land еще один горизонтальный вид для нашего окна и в нем уже прописали как оно будет выглядеть переопределив перед этим ниши фрагменты в main и details_activity_layout. 

Блин надеюсь не сложно объяснил.

Дальше мы создали разметку, теперь уже самую обычную которая будет показывать все что мы захотим, просто уже на наших фрагментах, а не обычных активностях.

То есть как бы по сути мы вывели при помощи фрагментов две активности в одном экране.

Фух! Ну а теперь давайте напишем код который будет делать собственно все что я описал выше.

Фрагменты


У нас сейчас есть файл MainActivity который выглядит вот так:

MainActivity
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 мы наследуем FragmentActivity не для сложности, а наоборот для простоты восприятия. В фрагментах легче работать именно с FragmentActivity так как он обеспечивает более тесную совместимость с фрагментами чем обычная Activity.

Дальше нам нужно создать второй FragmentActivity который будет отображать наш textView в который мы выводим текст из листвью.

DetailActivity
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);
                //принимаем intent из фрагмента если у нас вертикальная ориентация
                Bundle extras = getIntent().getExtras();
                if (extras != null) {
                        //выводим текст из полученной ссылки
                        String s = extras.getString("value");
                        TextView view = (TextView) findViewById(R.id.detailsText);
                        view.setText(s);
                }
        }
}


Тут вроде бы по комментариям понятно что и как делается, проверили какая ориентация, если вертикальная тогда проверяем что пришло с фрагмента, если не пусто то смотрим что и отображаем в текствью. Если же ориентация горизонтальная то активность финишируется и запускается фрагмент из папки layout-land. 

Дальше нам нужно создать фрагмент который будет выводить в листвью список мобильных операционных систем.

ListFragment
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.fragments.android.DetailActivity;
import com.fragments.android.R;
public class ListFragment extends android.support.v4.app.ListFragment {

        @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 onCreate(Bundle savedInstanceState) {

                super.onCreate(savedInstanceState);
        }

        @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);
                }
        }
}


Обратите внимание на реализацию метода onItemListClick первого фрагмента. Тут мы пытаемся узнать какая ориентация на данный момент, если горизонтальная тогда вызываем фрагмент и передаем в него, если же вертикальная то вызываем интент и передаем на него параметр кликнутого елемента.

Далее нам нужно создать последний фрагмент который будет выводить в горизонтальном положении наш текствью.

DetailFragment
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.fragments.android.R;
public class DetailFragment extends Fragment {

        @Override
        public void onActivityCreated(Bundle savedInstanceState) {

                super.onActivityCreated(savedInstanceState);
        }

        @Override
        public void onCreate(Bundle savedInstanceState) {

                super.onCreate(savedInstanceState);
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

                //обязательно нужно реализовывать, тут мы показываем какую разметку используем
                //так же здесь можно переопределять елементы, findViewByID и остальное делается так view.findViewById...
                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);
        }
}


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

Не забываем прописать FragmentActivity в манифестве, а то будут сыпаться ошибки. Всем спасибо за внимание.

Ссылки



--


Скачать с GitHub | Скачать с Dropbox

4 комментария:

  1. Старался ты, но написано плохо... тут тут , добавил и первую разметку потом вторую потом третью... жесть... быстрее читать доки

    ОтветитьУдалить
  2. Добрый день. Я хоть и новичок во фрагментах и пытаюсь их понять, но мне непонятна тут одна вещь:
    Для чего нужен list_mobile.xml, если он нигде не используется, а вместо него используется стандартный android.R.layout.simple_list_item_1 ? Другими словами, list_mobile.xml - мертвый код.

    ОтветитьУдалить
  3. И еще, details_activity_layout.xml и details.xml стоит объединить в один xml.

    ОтветитьУдалить
  4. Привет!
    Заинтересовала твоя работа, есть деловое предложение!
    Откликнись, плз, на имэйл: games4funn@ya.ru

    ОтветитьУдалить