LoginSignup
3
2

More than 5 years have passed since last update.

ViewPager + Fragment + GoogleAnalytics の整合性の保ち方

Last updated at Posted at 2015-11-05

何の話?

ViewPagerの中身にFragmentを持たせて、 FragmentPagerAdapterでsetAdapterって、よくやりますよね。
で、その時、ViewPagerの特性上、現在UIに乗ってるFragmentの両隣もすでにonCreateViewされています。
そのせいで、GoogleAnalyticsの出力が、思っているのと異なる結果になります。
その解決策として、ViewPager#addOnPageChangeListener()を使おうって話です。

今回作成したプロジェクトは、GitHubで公開してます
一度やってみたかったんです。
わざわざリポジトリつくるほどのものじゃないけど、許してやってください。

備考)筆者はプログラミング一年目の初心者です。あまり信用しないでください。たくさんの突っ込みお待ちしてます。よろしくです。

前提条件

  • Activityは基本的にFragmentの管理のみで、実際のユーザアクションにひも付けられているのはもっぱらFragment
  • だから、FragmentのScreenNameが欲しい
  • けど、ViewPagerだとFragmentのレイアウトがinflateされるのは、実際に表示される以前
  • だから、Fragment内でgetTracker().setScreenNameとかやっても、思ったのと違う結果に...

今回の方針

ViewPagerのスクロール(もち、水平方向。スワイプ)を探知するリスナーで、ページが変わったらsetScreenNameなりをする。

サンプルアプリ

7c5c2cb47dfbdc6c38ba8bb857af0a50.gif

まず、ダメな例

PlaceHolderFragment.java
public class PlaceHolderFragment extends Fragment {

    private static final String ARG_SECTION_NUMBER = "section_number";
    private int sectionNumber;

    public static PlaceHolderFragment newInstance(int sectionNumber) {
        Log.d("Fragment#" + sectionNumber, "newInstance");
        PlaceHolderFragment fragment = new PlaceHolderFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }

    public PlaceHolderFragment() {
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        Log.d("Fragment#" + sectionNumber, "onCreate start");
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            sectionNumber = getArguments().getInt(ARG_SECTION_NUMBER);

            // ScreenNameをセットしたつもりだけど...
            GoogleAnalyticsUtil.setScreenName("PlaceHolderFragment "
                    + Integer.toString(sectionNumber));
            Log.d("Fragment#" + sectionNumber, "onCreate finish");
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        Log.d("Fragment#" + sectionNumber, "onCreateView start");
        View rootView = inflater.inflate(R.layout.fragment_main, container, false);
        TextView textView = (TextView) rootView.findViewById(R.id.section_label);

        String text = getString(R.string.section_format, sectionNumber);
        textView.setText(text);

        final Button button = (Button) rootView.findViewById(R.id.button);
        final String sectionString = Integer.toString(sectionNumber);

        button.setText("Button " + sectionString);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(getActivity(),
                        button.getText() + " is pushed!",
                        Toast.LENGTH_SHORT).show();

                  // クリックイベントをGAにsendしてるんだけど,,,
                GoogleAnalyticsUtil.sendEvent(
                        "Category:Button",
                        "Action:Push",
                        "Label:" + sectionString);
            }
        });
        Log.d("Fragment#" + sectionNumber, "onCreateView finish");
        return rootView;
    }
}

GoogleAnalyticsUtilってのは、いちいちBuilder.build()するのが面倒だから作ったやつです。大したことないのでお気になさらず。

実行結果ログ

もちろん、Fragment#の後の数字はViewPagerのpositionです。

logcat
11-05 11:15:12.635 com.mickamy.ga_viewpager D/PagerAdapter: created
11-05 11:15:12.635 com.mickamy.ga_viewpager D/PagerAdapter: setAdapter
11-05 11:15:12.705 com.mickamy.ga_viewpager D/Fragment#1: newInstance
11-05 11:15:12.706 com.mickamy.ga_viewpager D/Fragment#2: newInstance
11-05 11:15:12.706 com.mickamy.ga_viewpager D/Fragment#0: onCreate start
11-05 11:15:12.759 com.mickamy.ga_viewpager D/Fragment#1: onCreate finish
11-05 11:15:12.759 com.mickamy.ga_viewpager D/Fragment#1: onCreateView start
11-05 11:15:12.763 com.mickamy.ga_viewpager D/Fragment#1: onCreateView finish
11-05 11:15:12.767 com.mickamy.ga_viewpager D/Fragment#0: onCreate start
11-05 11:15:12.767 com.mickamy.ga_viewpager D/Fragment#2: onCreate finish
11-05 11:15:12.767 com.mickamy.ga_viewpager D/Fragment#2: onCreateView start
11-05 11:15:12.770 com.mickamy.ga_viewpager D/Fragment#2: onCreateView finish
11-05 11:15:23.368 com.mickamy.ga_viewpager D/Fragment#3: newInstance
11-05 11:15:23.368 com.mickamy.ga_viewpager D/Fragment#0: onCreate start
11-05 11:15:23.368 com.mickamy.ga_viewpager D/Fragment#3: onCreate finish
11-05 11:15:23.368 com.mickamy.ga_viewpager D/Fragment#3: onCreateView start
11-05 11:15:23.371 com.mickamy.ga_viewpager D/Fragment#3: onCreateView finish

ね、せっかちでしょ。
そのおかげで、スワイプにも対応できてるんだろうけどさ。

当然、GAの出力もこんな感じ。
もちろん、Fragment#1, #2, #3の順にボタンクリックしてます。

11-05 11:15:12.709 com.mickamy.ga_viewpager I/GAv4: Google Analytics 8.1.15 is starting up. To enable debug logging on a device run:
11-05 11:15:20.852 com.mickamy.ga_viewpager I/GAv4: Dry run enabled. Would have sent hit: ht=1446740120849, _v=ma8.1.15, a=1008483049, aid=com.mickamy.ga_viewpager, an=GA-ViewPager, av=1.0, cd=PlaceHolderFragment 2, cid=b2ec3ade-0563-4b8e-829b-3b8d0b87da85, ea=Action:Push, ec=Category:Button, el=Label:1, sr=1440x2392, t=event, tid=UA-69540714-4, ul=en-us, v=1
11-05 11:15:23.593 com.mickamy.ga_viewpager I/GAv4: Dry run enabled. Would have sent hit: ht=1446740123593, _v=ma8.1.15, a=1008483049, aid=com.mickamy.ga_viewpager, an=GA-ViewPager, av=1.0, cd=PlaceHolderFragment 3, cid=b2ec3ade-0563-4b8e-829b-3b8d0b87da85, ea=Action:Push, ec=Category:Button, el=Label:2, sr=1440x2392, t=event, tid=UA-69540714-4, ul=en-us, v=1
11-05 11:15:27.020 com.mickamy.ga_viewpager I/GAv4: Dry run enabled. Would have sent hit: ht=1446740127017, _v=ma8.1.15, a=1008483049, aid=com.mickamy.ga_viewpager, an=GA-ViewPager, av=1.0, cd=PlaceHolderFragment 3, cid=b2ec3ade-0563-4b8e-829b-3b8d0b87da85, ea=Action:Push, ec=Category:Button, el=Label:3, sr=1440x2392, t=event, tid=UA-69540714-4, ul=en-us, v=1

読みにくいのはご勘弁願います。
ログってどうやったら読みやすくできるのよ。だれか教えてください。

で、上の読みにくい出力の中に、

Categoy:Button
Action:Push
Label:sectionNumber(1~3)

ってのがあります。これが送られるイベントです。
ってあれ、普通にできてるや。
じゃあイベントはオッケーでいいです笑

問題はScreenNameですよ!
今回は、PlaceHolderFragment + sectionNumber
でセットしたつもりなんですけど、ほら!
1,2,3って順番になってるべきところが2,3,3ってなってますよね

けど、これはおれがonCreateでsetScreenしたからで、onCreateViewに書いてたらよかったんじゃないのって?

ちがうでしょ。今回はたまたまこんな具合になったけど、それぞれのFragmentでのViewを作るのにかかる時間が、同じなわけないじゃん。
当然その都度、上にあげたログの生成順は変わります。たぶん
だから、adaptされるFragment内でsetScreenNameするのは危険なんです。たぶん。
もしかしたら僕がダメなだけかもしれないです。
お前がバカなだけだって思った方、コメントいただけたら幸いです。

ViewPager#addOnPageChangeListener()

ちょいと昔にはsetOnPageChangeってのがあったらしいけど、いまはもうdeprecatedらしいです。

で、これが書いたコードです。

MainActivity.java

        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                // 今回は無視
            }

            @Override
            public void onPageSelected(int position) {

                // 各ポジションでScreenNameをセット
                switch (position) {
                    case FIRST_FRAGMENT:
                        GoogleAnalyticsUtil.setScreenName("Fragment#1");
                        break;
                    case SECOND_FRAMGENT:
                        GoogleAnalyticsUtil.setScreenName("Fragment#2");
                        break;
                    case THIRD_FRAGMENT:
                        GoogleAnalyticsUtil.setScreenName("Fragment#3");
                        break;
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                // 今回は無視
            }
        });

はい、なんかScreenNameに指定した文字列変えちゃったけど、ごめんなさい。
直すの面倒です。

で、これが実行結果のログです。

11-05 12:18:18.141 com.mickamy.ga_viewpager I/GAv4: Dry run enabled. Would have sent hit: ht=1446743898140, _v=ma8.1.15, a=462218005, aid=com.mickamy.ga_viewpager, an=GA-ViewPager, av=1.0, cd=Fragment#1, cid=b2ec3ade-0563-4b8e-829b-3b8d0b87da85, ea=Action:Push, ec=Category:Button, el=Label:1, sr=1440x2392, t=event, tid=UA-69540714-4, ul=en-us, v=1
11-05 12:18:21.394 com.mickamy.ga_viewpager I/GAv4: Dry run enabled. Would have sent hit: ht=1446743901394, _v=ma8.1.15, a=462218005, aid=com.mickamy.ga_viewpager, an=GA-ViewPager, av=1.0, cd=Fragment#2, cid=b2ec3ade-0563-4b8e-829b-3b8d0b87da85, ea=Action:Push, ec=Category:Button, el=Label:2, sr=1440x2392, t=event, tid=UA-69540714-4, ul=en-us, v=1
11-05 12:18:23.543 com.mickamy.ga_viewpager I/GAv4: Dry run enabled. Would have sent hit: ht=1446743903542, _v=ma8.1.15, a=462218005, aid=com.mickamy.ga_viewpager, an=GA-ViewPager, av=1.0, cd=Fragment#3, cid=b2ec3ade-0563-4b8e-829b-3b8d0b87da85, ea=Action:Push, ec=Category:Button, el=Label:3, sr=1440x2392, t=event, tid=UA-69540714-4, ul=en-us, v=1

はい、きれいに1,2,3と並んでますね。
これだと、きちんとユーザがそのFragmentを視認した瞬間にsetScreenNameしてsendするわけですから、画面に対するセッション時間をきちんと取れます。
よかった。

まとめ

  • ViewPagerの中身がスワイプして初めてViewをinflateすると思ってたら痛い目に遭います。
  • その対策として、ViewPagerの横スクロールを探知するOnPageChangeListenerがあります。
  • この方法は、ViewPager間でViewを反映させたい時とかにも使えると思います。
  • だれか僕にログを見やすく出力する方法を教えてください。

今回貼ったlogcatだって、一旦全部コピーして関係ないとこいちいち消したんです。フィルタくらいはかけたけど。
だるかったです。お願いします。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2