本業はWebエンジニアでAndroid歴は半年くらいですが、色々試行錯誤したネタを投下していこうと思います。
やりたいこと
タイトルだけだと何のこっちゃという話ですが、例えばTwitterとかでフォローするボタンがあるじゃないですか。で、ViewPager管理下の各Fragmentにフォローするボタンを置いてどこかでフォローするを押したら、押したページ以外にも押された結果を反映させたいという話です。ViewPagerを使っていなければデータを引き回すなりしてonCreateViewあたりでデータに合わせてViewを編集すればいいのですが、ViewPagerを使ってるとスワイプする時に今見ているページと次に表示するページの両方を表示するので、1ページ目でフォローする→スワイプで2ページ目に行く、というアクションをすると2ページ目はすでに1ページ目の表示時に作成済なのでonCreateViewを通らずに未フォローの状態のままになってしまいます。
作ってみた
んで、Observerパターンを使うとすっきり作ることができました。フォロアーリストのModelを作ってそいつのObserverとして各フラグメントを登録することでデータが変更されたらObserverとして観測中のFragmentは変更の通知をキャッチしてViewを変更するようにします。
ActivityとPagerAdaptor
デフォルトで作成されるPager付きのActivityからいらないものを削ってFragmentを差し替えただけです。
package me.rei_m.androidsample.activitiy;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.support.v13.app.FragmentPagerAdapter;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import me.rei_m.androidsample.R;
import me.rei_m.androidsample.fragment.PagerItemFragment;
public class PagerSampleActivity extends Activity {
public static Intent createIntent(Context context) {
return new Intent(context, PagerSampleActivity.class);
}
SectionsPagerAdapter mSectionsPagerAdapter;
ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pager_sample);
mSectionsPagerAdapter = new SectionsPagerAdapter(getFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return PagerItemFragment.newInstance(position);
}
@Override
public int getCount() {
return 4;
}
@Override
public CharSequence getPageTitle(int position) {
return String.valueOf(position) + "枚目";
}
}
}
Fragment
フラグメントはこんな見た目でページ番号が変わる以外は全部同じです。
package me.rei_m.androidsample.fragment;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import java.util.Observable;
import java.util.Observer;
import me.rei_m.androidsample.R;
import me.rei_m.androidsample.entity.FollowerEntity;
import me.rei_m.androidsample.model.FollowerListModel;
import me.rei_m.androidsample.model.ModelLocator;
public class PagerItemFragment extends Fragment implements Observer, View.OnClickListener {
// Modelからの通知を受け取る必要があるのでObserverを実装する
private static final String ARG_PAGE_NO = "pageNo";
public static PagerItemFragment newInstance(int pageNo) {
Bundle args = new Bundle();
args.putInt(ARG_PAGE_NO, pageNo);
PagerItemFragment fragment = new PagerItemFragment();
fragment.setArguments(args);
return fragment;
}
private FollowerEntity mUser;
private int mPageNo;
private TextView mTextView;
private Button mButton;
private FollowerListModel mFollowerListModel;
public PagerItemFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ページ番号を取得
mPageNo = getArguments().getInt(ARG_PAGE_NO);
// ModelのObserverにインスタンスを登録
mFollowerListModel = ModelLocator.getInstance().getFollowerListModel();
mFollowerListModel.addObserver(this);
// 仮のユーザを設定
mUser = new FollowerEntity();
mUser.setId("0000001");
mUser.setName("ほげー");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_pager_item, container, false);
// TextViewとButtonを取得
mTextView = (TextView) view.findViewById(R.id.text_pager_item_title);
mTextView.setText(String.valueOf(mPageNo));
mButton = (Button) view.findViewById(R.id.button_pager_item);
mButton.setOnClickListener(this);
// 初期表示の設定。対象のユーザーをフォロー済かどうかで表示を変える
if(mFollowerListModel.hasFollower(mUser)){
setFollowedStyle();
}else{
setUnFollowedStyle();
}
return view;
}
@Override
public void onDestroy() {
super.onDestroy();
// ModelのObserverからインスタンスを解除
mFollowerListModel.deleteObserver(this);
}
@Override
public void onDetach() {
super.onDetach();
mTextView = null;
mButton = null;
mFollowerListModel = null;
mUser = null;
}
@Override
public void update(Observable observable, Object data) {
// Modelからフォロアーリストの更新通知が来たら走る処理
if(data instanceof FollowerListModel.ChangeFollowerListEvent){
FollowerListModel.ChangeFollowerListEvent event = (FollowerListModel.ChangeFollowerListEvent) data;
// フォローアクションの状態によって表示を変える
if(event.getIsAdded()){
setFollowedStyle();
}else{
setUnFollowedStyle();
}
}
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.button_pager_item:
// フォロアーリストを確認して、追加済であれば削除、未追加であれば追加
// ボタンのイベントではViewの表示は変えない
if(mFollowerListModel.hasFollower(mUser)){
mFollowerListModel.removeFollower(mUser);
}else{
mFollowerListModel.addFollower(mUser);
}
break;
}
}
// フォロー済の状態に表示を変更する
private void setFollowedStyle(){
mButton.setText(mUser.getName() + "をふぉろーしてるよ");
mTextView.setBackgroundColor(getResources().getColor(R.color.active));
}
// 未フォローの状態の表示を変更する
private void setUnFollowedStyle(){
mButton.setText(mUser.getName() + "はまだふぉろーしてないよ");
mTextView.setBackground(null);
}
}
ModelとEntity
Obserbleを実装したModelを作ってデータに変更があったら更新の通知を送るように実装します。Entitiyは適当。
package me.rei_m.androidsample.model;
import java.util.EventObject;
import java.util.HashMap;
import java.util.Observable;
import me.rei_m.androidsample.entity.FollowerEntity;
public class FollowerListModel extends Observable{
public static FollowerListModel createInstance(){
FollowerListModel instance = new FollowerListModel();
instance.mFollowerList = new HashMap<>();
return instance;
}
private FollowerListModel(){}
private HashMap<String, FollowerEntity> mFollowerList;
public HashMap<String, FollowerEntity> getFollowerList() {
return mFollowerList;
}
// フォロー済か確認する
public boolean hasFollower(FollowerEntity target){
return mFollowerList.containsKey(target.getId());
}
// フォロアーリストに追加する
public void addFollower(FollowerEntity target){
mFollowerList.put(target.getId(), target);
notifyEvent(true);
}
// フォロアーリストから削除する
public void removeFollower(FollowerEntity target){
mFollowerList.remove(target.getId());
notifyEvent(false);
}
// フォロアーリストの更新通知をObserverに対して送る
private void notifyEvent(boolean isAdded){
ChangeFollowerListEvent event = new ChangeFollowerListEvent(this);
event.setIsAdded(isAdded);
setChanged();
notifyObservers(event);
}
// 通知用のEventObject
public static class ChangeFollowerListEvent extends EventObject {
public ChangeFollowerListEvent(Object source){
super(source);
}
private boolean isAdded;
public boolean getIsAdded() {
return isAdded;
}
public void setIsAdded(boolean isAdded) {
this.isAdded = isAdded;
}
}
}
package me.rei_m.androidsample.entity;
import java.io.Serializable;
public class FollowerEntity implements Serializable {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
こんな感じで予定通りに動いてくれました。これで1ページ目でフォローする→スワイプで2ページ目に行っても2ページ目もちゃんとフォロー済の表示になります。変なとこがあればご指摘ください〜
コードはこちら