ViewPagerで表示するviewのclickがひろえないなと思ったら
Twitter公式クライアントの画像プレビューに限りなく近いものを作りたくて、ViewPager
にわたすFragment
の中でImageView
を作って、OnClickListener
をセットしてクリックイベントをとろうと思って次のように書いたのだが、だめだった。
@Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); return new ImageView(getContext()); } @Override public void onStart() { super.onStart(); getView().setOnClickListener(new View.OnClickListener(View view) { // immersiveな状態を解除したりUIを表示したりする }); }
stackoverflowで解決策を探していると、PagerAdapter.instantiateItem()
をオーバライドしてその中でクリックリスナやタッチリスナをセットしろというアドバイスがあったので、onCreateView
の中でImageView
にsetOnClickListener
してから返したりして難を逃れたりしていたが、次のようにも書けた。
private ImageView imageView; @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); this.imageView = new ImageView(getContext()); return this.imageView; } @Override public void onStart() { super.onStart(); this.imageView.setOnClickListener(new View.OnClickListener(View view) { // immersiveな状態を解除したりUIを表示したりする }); }
onStart
でセットしてonStop
で解除すれば生存期間が若干短くなるのでこっちの方がいいかなと思いました(こなみ)。
ただ、これだとimmersive modeから復帰するための「システムUIを引っ張り出すスワイプ」でも、指を離したときにクリックイベントがなぜか発生してしまうので、独自実装したタッチリスナを同様に渡したほうがよい。
SYSTEM_UI_FLAG_IMMERSIVEとSYSTEM_UI_FLAG_IMMERSIVE_STICKYとの違い
API Level 19からは全画面表示をしたいときにどっちかのフラグをセット(View.setSystemUiVisibility()
)することで没入感をより高めることができる。
SYSTEM_UI_FLAG_HIDE_NAVIGATION
とSYSTEM_UI_FLAG_FULLSCREEN
とを合わせてセットすることで、システムUIが画面外に押し出されている状態になる。外にあるUIを引っ張り出すように(上か下エッジからスワイプ)すると表示される。二つのフラグの違いは次の通り。
SYSTEM_UI_FLAG_IMMERSIVE
: システムUIが引っ張り出されたら、システムUIが消えた状態に戻らないSYSTEM_UI_FLAG_IMMERSIVE_STICKY
: システムUIが引っ張り出されたあと、しばらくするとまたシステムUIが引っ込む
IMMERSIVE
の方を使い、View#setOnSystemUiVisibilityChangeListener()
にリスナをセットすることでシステムUIの見え方が変わったイベントを受け取れる。このリスナの中で独自のUIを表示させるようにして、時間とか特定の操作で再びimmersiveな状態に遷移させれば良い。
システムUIが無いレイアウトに設定するためのフラグも一緒にセットしてやればカクついたりしない。詳しくは公式リファレンスを。
カスタムビューに独自のスタイル属性を定義する
メディアのサムネイルを同じ大きさで横一列に並べるコンテナクラスを作ったので、中にいれるサムネイルの数をレイアウトリソースで定義できるようにした。
res/values/attr.xml
にdeclare-styleable
を追加する
<resources> <declare-styleable name="MyCustomView"> <attr name="thumbCount" format="integer" /> </declare-styleable> </resources>
layoutリソースファイルで次のように使う
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <com.freshdigitable.MyCustomView android:id="@+id/custom_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" app:thumbCount="4" /> </LinearLayout>
独自のネームスペースを定義しようとしたら、「gradleプロジェクトではapp:.../res-auto
を使え」的なことを言われた気がする。
カスタムビューのコンストラクタで設定した値を受け取る
(色々と省略している)
public class MyCustomView extends View { private final int thumbCount; public MyCustomView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0); try { this.thumbCount = a.getInt(R.styleable.MyCustomView_thumbCount, 0); } finally { a.recycle(); } } }