Help us understand the problem. What is going on with this article?

Androidのお勉強 第三回 フラグメントの使用

More than 3 years have passed since last update.

Androidのお勉強 第三回 フラグメントの使用

第三回はフラグメントの使い方を紹介します。
※編集中です

フラグメントって??

FragmentはAndroid 3.x系から追加されたコンポーネントです。
従来は画面を作成するために、「ViewとActivity」を画面ごとに作成してきました。
似たような画面が複数必要な場合であっても、「ViewとロジックがセットになったActivity」を再度作らなければいけません。

つまり、「画面の再利用」ができなかったわけです。
※layoutでincludeする手法は「Viewの再利用しかできません」「ロジックを含んだViewの再利用」ができなかったって意味です。

そんな中現れたのがFragmentです。

Fragmentが「「ロジックを含んだViewの再利用」」を可能にします

  1. FragmentはActivityにレイアウトの一部(layout.xml)に部品として組み込むことができます。
  2. javaのソース(Activity)内で、動的に追加、削除ができます。

そしてFragmentは何よりも、タブレットと携帯端末でのUIを意識して設計されています。

Fragmentを切り替えるという設計を利用することで、以下のようなデザインをアプリに組み込むことができるのです。

alt

詳しくは以下のDeveloperにも記載されていますので、目を通してみてください。

http://developer.android.com/guide/components/fragments.html

使ってみる1(xmlに組み込む)

先の記述の1.FragmentはActivityにレイアウトの一部(layout.xml)に部品として組み込むことができます。を実際にコードを書いてやってみます。

MainActivity.javaにボタン追加

今回作成したい画面に遷移するためのボタンを追加します。
以下の画面イメージを実装します。

fra1.png

面倒なのでActivityとlayoutの全文を載せます。

■R.layout.activity_main

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <TextView
        android:id="@+id/title_text"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/name_text"
            android:hint="@string/message_name_text_hint"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <Button
            android:id="@+id/go_button"
            android:text="@string/label_go_button_go"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>
    <Button
        android:id="@+id/go_sample_list_button"
        android:text="@string/label_go_sample_list_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/go_fragment_samle1"
        android:text="@string/label_go_xml_fragment_sample"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

■MainActivity

public class MainActivity extends Activity implements View.OnClickListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState == null) {
            TextView titleText = (TextView) findViewById(R.id.title_text);
            titleText.setText(getString(R.string.app_name));
            Button goButton = (Button) findViewById(R.id.go_button);
            goButton.setOnClickListener(this);
            Button goSamleListButton = (Button) findViewById(R.id.go_sample_list_button);
            goSamleListButton.setOnClickListener(new SampleListButtonClickListener(this));
            Button goXmlFragmentSampleButton = (Button) findViewById(R.id.go_fragment_samle1);
            goXmlFragmentSampleButton.setOnClickListener(new FragmentSample1ClickListener(getApplication()));
        }
    }

    @Override
    public void onClick(View v) {

        Intent intent = new Intent(this, NextActivity.class);
        intent.putExtra("titleText", ((TextView) findViewById(R.id.title_text)).getText());
        startActivity(intent);
    }

    static  class SampleListButtonClickListener implements View.OnClickListener {

        private Activity activity;

        public SampleListButtonClickListener(Activity activity) {
            this.activity = activity;
        }

        @Override
        public void onClick(View v) {
            Intent intent = new Intent(activity, ListSampleActivity.class);
            activity.startActivity(intent);
        }
    }

    private static class FragmentSample1ClickListener implements View.OnClickListener {

        Context con;

        public FragmentSample1ClickListener(Context con) {
            this.con = con;
        }

        @Override
        public void onClick(View v) {
            Intent intent = FragmentSample1Activity.createInstance(con);
            con.startActivity(intent);
        }
    }
}

新たに追加されたボタン「XML組み込みFragmentサンプルへ」を押すと、新しい画面に遷移します。

ではさっそく、新規画面を作りましょう。

今回追加される画面のイメージは以下のようなものです。

frafra1.png

とても単純です。TextViewが一枚あり、その横に画像が表示されています。
簡単ですが、今回はこれをFragmentで実現しています!

では、新規画面の実装に入ります。

FragmentSample1Activityの作成

ルートパッケージに以下のActivityを追加

package jp.co.kenshu;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

public class FragmentSample1Activity extends Activity {
    public static Intent createInstance(Context con) {
        Intent intent = new Intent(con, FragmentSample1Activity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        return intent;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_sample1);
    }
}

R.layout.activity_fragment_sample1の作成

layout下にactivity_fragment_sample1.xmlを作成してください。
名前がややこしいですが、あくまでActivityで表示するためのレイアウトです。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
<fragment
    android:id="@+id/fragment_sample1"
    android:name="jp.co.kenshu.fragment.FragmentSample1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
</LinearLayout>

FragmentSample1.javaの作成

FragmentはJavaで書きます。Activityと似たようなプロセスを経て作成できます。

jp.co.keshu.fragmentパッケージを作成し、以下のjavaを追加してください。

package jp.co.kenshu.fragment;

import android.app.Fragment;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import jp.co.kenshu.R;

public class FragmentSample1 extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_sample1, container, false);
        TextView textView = (TextView) rootView.findViewById(R.id.fragment_sample1_textview);
        Drawable icon = getDrawableResource(android.R.drawable.btn_star_big_on);
        icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
        textView.setCompoundDrawables(icon, null, null, null);
        return rootView;
    }
    @SuppressWarnings("deprecation")
    public Drawable getDrawableResource(int id){
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
            return getActivity().getApplicationContext().getDrawable(id);
        }
        else {
            return getResources().getDrawable(id);
        }
    }
}

fragment_sample1.xmlの作成

Fragmentで実際に読み込んで表示するためのlayoutファイルを記述します。
layout下に「fragment_sample1.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/fragment_sample1_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/label_fragment_sample1_text"/>
</LinearLayout>

動かしてみる(xmlからFragmentをattachしたケース)

これでXMLからFragmentをアタッチしたケースの準備はOKです。
実行してみてください。

画面に先ほどのイメージがでれば成功です。

今回は、Activityのlayoutファイルに自作したFragmentを要素として組み込んだ例となります。

以下がそれに該当します。

▼activity_fragment_sample1.xml

<fragment
    android:id="@+id/fragment_sample1"
    android:name="jp.co.kenshu.fragment.FragmentSample1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
</LinearLayout>

Activityに要素として組み込む際には、以下のお約束を守る必要があります。

  1. nameでどこの何てFragmentかを明記する
  2. idで一意に特定できる識別子を与える

今回は、1が「jp.co.kenshu.fragment.FragmentSample1」であり、2がfragment_sample1なんですね。

これでActivityがonCreateで生成される際に、layout要素のFragmentがアタッチされるため、FragmentSample1.javaが実行・評価されます。

では実際に書いたFragmentを見てみます。

Fragmentはそれ独自にライフサイクルを持ちます。
しかし、FragmentはあくまでActivityの要素です。Fragmentは以下のライフサイクルに基づいて、生成されます。

alt

※参考…http://developer.android.com/guide/components/fragments.html

Fragmentが実際に描画するレイアウトは

 onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 

内でinflate経由でlayoutファイルをロードします。
あとは今までやってきたActivityでの実装と変わりません。

extends Fragment

今回は

android.app.Fragment

をimportしてます。
support4のfragmentを使ったとしても、記述方法は変わりませんので。

TextView textView = (TextView) rootView.findViewById(R.id.fragment_sample1_textview);
Drawable icon = getDrawableResource(android.R.drawable.btn_star_big_on);
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
textView.setCompoundDrawables(icon, null, null, null);

これは今回は遊び程度に掲載しました。
TextViewの文字列の上下左右に画像を表示できる機能です。

textView.setCompoundDrawables(icon, null, null, null);

上位の引数は左から順番に「左に表示する画像、上に表示する画像、右に表示する画像、下に表示する画像」と指定できます。全て指定するとこんな感じになりますw

textView.setCompoundDrawables(icon, icon, icon, icon);

stars.png

使ってみる2(javaからaddする)

次はActivityから動的にFragmentを追加したり、更新したりする例です。
画面イメージは以下です。

fff.png

MainActivity.javaにボタン追加

例によって、追加します。
MainActivityに以下のメソッドを最終行に追加してください。

public void hoge(View view) {
        Intent intent = FragmentSample2Activity.newInstance(getApplicationContext(), "Fragmentをjavaからaddするサンプル");
        startActivity(intent);
}

activity_main.xmlにボタン追加

<Button
        android:id="@+id/go_fragment_samle2"
        android:text="@string/label_go_java_add_fragment_sample"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="hoge"/>

onClickに指定した名前がリスナーメソッドとして登録されます。
Activityに定義されているhogeメソッドが今回のリスナーになるってことです。
※数あるリスナーの実装方法の中でも、この使い方は一番お勧めしません。紹介程度で出しました。

FragmentSample2Activityの作成

package jp.co.kenshu;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.ViewGroup;

import jp.co.kenshu.fragment.FragmentSample2;

public class FragmentSample2Activity extends Activity {
    public static Intent newInstance(Context con, String message) {
        Intent intent = new Intent(con, FragmentSample2Activity.class);
        intent.putExtra("hoge", message);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        return intent;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_sample2);
        setFragment();
    }

    private void setFragment() {
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        Fragment fragment = FragmentSample2.newInstance(getIntent().getStringExtra("hoge"));
        ViewGroup root = (ViewGroup)getWindow().getDecorView().findViewById(android.R.id.content);
        fragmentTransaction.add(root.getId(), fragment);
        fragmentTransaction.commit();
    }
}

FragmentSample2.javaの作成

package jp.co.kenshu.fragment;

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Chronometer;
import android.widget.TextView;
import jp.co.kenshu.R;

public class FragmentSample2 extends Fragment{

    private String message;

    public static FragmentSample2 newInstance(String messagge) {
        Bundle bundle = new Bundle();
        bundle.putString("hoge", messagge);
        FragmentSample2 fragment = new FragmentSample2();
        fragment.setArguments(bundle);
        return fragment;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        message = getArguments().getString("hoge");
    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_sample2, container, false);
        Chronometer meter = (Chronometer) rootView.findViewById(R.id.fragment_sample2_chronometer);
        meter.start();
        TextView textView = (TextView) rootView.findViewById(R.id.fragment_sample2_textview);
        textView.setText(message);
        return rootView;
    }
}

fragment_sample2.xmlの作成

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <TextView
        android:id="@+id/fragment_sample2_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Chronometer
        android:id="@+id/fragment_sample2_chronometer"
        android:format="chronometer:%s"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

動かしてみる2(javaからaddする)

フラグメントの使い時についての考察

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away