[解決済み] AndroidのViewPagerからフラグメントページを削除する
質問
ViewPagerにフラグメントを動的に追加・削除しようとしていますが、追加は問題なく行われますが、削除が期待通りに行われません。
現在のアイテムを削除したい場合、常に最後のアイテムが削除されます。
FragmentStatePagerAdapterを使ったり、アダプタのgetItemPositionメソッドでPOSITION_NONEを返してみたりもしました。
何が間違っているのでしょうか?
基本的な例を挙げます。
MainActivity.java
public class MainActivity extends FragmentActivity implements TextProvider {
private Button mAdd;
private Button mRemove;
private ViewPager mPager;
private MyPagerAdapter mAdapter;
private ArrayList<String> mEntries = new ArrayList<String>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mEntries.add("pos 1");
mEntries.add("pos 2");
mEntries.add("pos 3");
mEntries.add("pos 4");
mEntries.add("pos 5");
mAdd = (Button) findViewById(R.id.add);
mRemove = (Button) findViewById(R.id.remove);
mPager = (ViewPager) findViewById(R.id.pager);
mAdd.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
addNewItem();
}
});
mRemove.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
removeCurrentItem();
}
});
mAdapter = new MyPagerAdapter(this.getSupportFragmentManager(), this);
mPager.setAdapter(mAdapter);
}
private void addNewItem() {
mEntries.add("new item");
mAdapter.notifyDataSetChanged();
}
private void removeCurrentItem() {
int position = mPager.getCurrentItem();
mEntries.remove(position);
mAdapter.notifyDataSetChanged();
}
@Override
public String getTextForPosition(int position) {
return mEntries.get(position);
}
@Override
public int getCount() {
return mEntries.size();
}
private class MyPagerAdapter extends FragmentPagerAdapter {
private TextProvider mProvider;
public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
super(fm);
this.mProvider = provider;
}
@Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mProvider.getTextForPosition(position));
}
@Override
public int getCount() {
return mProvider.getCount();
}
}
}
TextProvider.java
public interface TextProvider {
public String getTextForPosition(int position);
public int getCount();
}
MyFragment.java
public class MyFragment extends Fragment {
private String mText;
public static MyFragment newInstance(String text) {
MyFragment f = new MyFragment(text);
return f;
}
public MyFragment() {
}
public MyFragment(String text) {
this.mText = text;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment, container, false);
((TextView) root.findViewById(R.id.position)).setText(mText);
return root;
}
}
アクティビティ_メイン.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" >
<Button
android:id="@+id/add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="add new item" />
<Button
android:id="@+id/remove"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="remove current item" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</LinearLayout>
フラグメント.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/position"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="35sp" />
</LinearLayout>
どのように解決するのですか?
Louth による解決策では、既存のフラグメントが破壊されないため、私の場合、物事をうまく進めるには十分ではありませんでした。動機は
この回答
によって動機づけられ、私は解決策が
getItemId(int position)
メソッドをオーバーライドすることです。
FragmentPagerAdapter
メソッドを用いて、Fragment の予想される位置が変更されるたびに新しい一意の ID を与えることができます。
ソースコードです。
private class MyPagerAdapter extends FragmentPagerAdapter {
private TextProvider mProvider;
private long baseId = 0;
public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
super(fm);
this.mProvider = provider;
}
@Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mProvider.getTextForPosition(position));
}
@Override
public int getCount() {
return mProvider.getCount();
}
//this is called when notifyDataSetChanged() is called
@Override
public int getItemPosition(Object object) {
// refresh all fragments when data set changed
return PagerAdapter.POSITION_NONE;
}
@Override
public long getItemId(int position) {
// give an ID different from position when position has been changed
return baseId + position;
}
/**
* Notify that the position of a fragment has been changed.
* Create a new ID for each position to force recreation of the fragment
* @param n number of items which have been changed
*/
public void notifyChangeInPosition(int n) {
// shift the ID returned by getItemId outside the range of all previous fragments
baseId += getCount() + n;
}
}
さて、例えばタブを一つ削除したり、順番に何らかの変更を加えたりする場合、以下のように
notifyChangeInPosition(1)
を呼び出す前に
notifyDataSetChanged()
を呼び出すことで、すべてのフラグメントが再作成されることを保証します。
このソリューションが機能する理由
getItemPosition()をオーバーライドしています。
いつ
notifyDataSetChanged()
が呼ばれると、アダプタは
notifyChanged()
のメソッドを呼び出します。
ViewPager
メソッドを呼び出します。その
ViewPager
は、アダプタの
getItemPosition()
が返す値をチェックし、その中から
POSITION_NONE
(を返すアイテムを削除します。
ソースコード
を参照)し、再投入してください。
getItemId()をオーバーライドしています。
これは、アダプタが
ViewPager
が再投入される際に、アダプタが古いフラグメントを再読み込みするのを防ぐために必要です。なぜこれが機能するのか、それは
ソースコード
にある instantiateItem() の
FragmentPagerAdapter
.
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
ご覧のように
getItem()
メソッドは、フラグメントマネージャが同じ ID を持つ既存のフラグメントを見つけない場合にのみ呼び出されます。私にとっては、古いフラグメントが
notifyDataSetChanged()
が呼び出された後でも古いフラグメントが添付されているのはバグのように思えますが
ViewPager
はそのことを明確に述べています。
このクラスは現在、初期の設計と開発中であることに注意してください。API は、互換性ライブラリの将来のアップデートで変更される可能性があり、新しいバージョンに対してコンパイルするときにアプリのソース コードを変更する必要があります。
したがって、ここで示された回避策は、互換ライブラリの将来のバージョンでは必要ないことを期待します。
関連
-
Gradle の同期に失敗しました。com.android.tools.build:gradle が見つかりませんでした。
-
アンドロイドスタジオでJunitのエラー問題を解決する
-
Android studioのインストールと問題発生、Emulator: PANIC: AVDのシステムパスが見つかりません。
-
android bluetooth--Bluetooth on、検索、ペアリング、接続
-
[解決済み] Androidのソフトキーボードをプログラムで閉じる/隠すにはどうすればよいですか?
-
[解決済み] Androidでアクティビティ起動時にEditTextにフォーカスが当たらないようにする方法
-
[解決済み] Androidの「コンテキスト」とは何ですか?
-
[解決済み] ViewPagerでFragmentが表示されるタイミングを決定する方法
-
[解決済み] ViewPager PagerAdapter がビューを更新しない
-
[解決済み】Android UserManager.isUserAGoat()の正しい使用例?)
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
プログラム "git.exe "を実行できない場合の正しい解決方法です。CreateProcessエラー=2
-
RuntimeException: アクティビティを開始できません ComponentInfo solution
-
アプリケーションがメインスレッドで過剰に作業している可能性があります。
-
エラー:未宣言の識別子(AS)の使用
-
Android Bluetooth 開発の基本プロセス
-
Android Get set image.setImageResource(R.drawable.xxx) リソース
-
Android--shape--描画のコーナー、グラデーション、パディング、サイズ、ソリッド、ストロークのプロパティを指定する。
-
Android.support.v7.widget.Toolbar が見つかりませんでした。
-
[解決済み] ViewPagerとフラグメント - フラグメントの状態を保存する正しい方法は何ですか?
-
[解決済み] ビューページャーへのビューの動的な追加と削除