10
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Androidその2Advent Calendar 2016

Day 22

SNSタイムライン動画の自動再生

Last updated at Posted at 2016-12-22

はじめに

FiNCのAndroidエンジニア兼Bisonの南里です。
AdventCalendarってなんで師走の忙しい時期なんでしょうね?
それでは参りましょう。

まずはこちらをご覧ください

pic.twitter.com/cLPXZHEonG

— 南里勇気 (@neonankiti) December 22, 2016

ラインの自動再生を録画してみました。
佐野ひなこかわいいですね。

色々なSNSでよくみる自動再生。
実際に実装している例があまり見受けられなかったので、考えてみました。

機能要件

  1. Wifiで自動再生(モバイルで自動再生はユーザーアンライクです)
  2. 自動再生はスクリーン内に入った際に行う。
  3. 複数ある場合は、占有面積が大きい1つのみを再生

1. Wifi部分の実装

ConnectivityManagerというクラスがあります。
このクラスは、ネットワークの状態、変化に関して責務を負うクラスです

現在のネットワークがwifiであることを取得するには、以下のコードを書きます。


// ネットワーク状態の取得
private static NetworkInfo getNetworkInfo(@NonNull Context context) {
    ConnectivityManager connectivityManager 
        = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    return connectivityManager.getActiveNetworkInfo();
}


// Wifiステータス
public static boolean isWifi(@NonNull Context context) {
    return getNetworkType(context) == ConnectivityManager.TYPE_WIFI;
}

簡単ですね。

2. 自動再生はスクリーン内に入った際に行う
3. 複数ある場合は、占有面積が大きい1つのみを再生

方針は、RecyclerView.OnScrollListerで画面上に表示されている各コンテンツ(ViewHolder)のpositionを取得し、動画を始めるべきpositionかどうか判断します。
動画を始めるべきpositionであれば開始し、そうでなければそのコンテンツは動画を止めます。

ReyclerViewを利用する際には、そのイベントリスナーとして、RecyclerView.OnScrollListener を利用します。

コンテンツが画面内にあるかどうかは取得できるのですが、コンテンツが再生すべき動画であることを特定するには、それぞれのコンテンツごとの座標を取得して、計算する必要があります。

簡単にスクリーン内にあるコンテンツを特定するためにまず、以下のようなinterfaceを定義します。
そして、OnScrollListener内でinsideScreenを呼び出してあげます。

SampleActivity.java
interface OnPositionListener {
    void insideScreen(int startPosition);
}

// positionのlisterはMapで持ちます。
private HashMap<Integer, OnCompletelyVisiblePositionListener> positionListeners = new HashMap<>();

public void setOnPositionListener(
        int position, OnPositionListener positionListener) {
    this.positionListeners.put(position, positionListener);
}

insideScreen内での実装では、スクリーン上の全てのコンテンツ(ViewHolder)に対して、startPositionを渡してあげます。
ViewHolderのgetAdapterPosition()と比較して、startPositionであれば、VideoView(ExoPlayerが最近の流行りですかね)をstart()するという形です。

SampleActivity.java
private final RecyclerView.OnScrollListener listener = new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        insideScreen(getMovieStartPosition(recyclerView));
    }
};


@Override
public void insideScreen(int startPosition) {
    for (Map.Entry<Integer, OnCompletelyVisiblePositionListener> e : onCompletelyVisiblePositionListeners.entrySet()) {
        e.getValue().insideScreen(startPosition);
    }
}

画面の占有面積を取得する部分

SampleActivity.java
private int getMovieStartPosition(RecyclerView recyclerView) {

    // 最初のコンテンツの取得
    int firstVisibleItemPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
    
    // 最後のコンテンツの取得
    int lastVisibleItemPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition();

    // コンテンツの大きさにより、画面上に表示されるコンテンツの数は異なる可能性がある。
    if (lastVisibleItemPosition - firstVisibleItemPosition == 0) {
        return firstVisibleItemPosition;
    }

    // 画面上に表示される最初と最後のアイテムのpositionの差分が1以上のとき、表示するべきコンテンツ一つを特定する必要がある。
    float ratio = 0.0f;
    int startPosition = firstVisibleItemPosition;
    
    // コンテンツ分for文を回す。
    for (int i = 0; i < lastVisibleItemPosition - firstVisibleItemPosition + 1; i++) {
    
        // positionのadapterを取得する
        RecyclerView.ViewHolder holder = recyclerView.findViewHolderForAdapterPosition(firstVisibleItemPosition + i);
        
        // findViewHolderForAdapterPosition could return null.
        if (holder == null) {
            continue;
        }
        
        // 各コンテンツのTop, Bottomの座標を取得する
        int topPosition = holder.itemView.getTop() < 0 ? 0 : holder.itemView.getTop();
        int bottomPosition = holder.itemView.getBottom() > displaymetrics.heightPixels
                ? displaymetrics.heightPixels
                : holder.itemView.getBottom();
                
        // 画面縦の比率を取得して、コンテンツごとに占有面積を比較。
        float pointedRatio = (bottomPosition - topPosition) / (float) displaymetrics.heightPixels;
        if (pointedRatio > ratio) {
            ratio = pointedRatio;
            startPosition = firstVisibleItemPosition + i;
        }
    }
    return startPosition;
}

上記で、タイムライン上において、動画を再生するべきpositionを特定したので、あとはViewHolder.getAdapterPosition()の値と比較して、再生するべきコンテンツなのか判断してあげてください。

まとめ

Line, Twitter, Facebook, Vineなどで自動再生どう実装しているんだろうなー。と考えていたので、実際に実装してみるといい勉強になりました。
もっといい実装あれば、教えて下さい。

10
7
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
10
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?