FragmentPageAdapter切换时无法更新数据问题解析

在一个 Android 应用中,我们可以使用 FragmentPageAdapter 来处理多 Fragment 页面的横向滑动。但是当 Fragment 对应的数据集发生改变时,我们都希望能够通过调用 mAdapter.notifyDataSetChanged() 来触发 Fragment 页面使用新的数据调整或重新生成其内容,可是当我们使用 notifyDataSetChanged() 后,我们会发现这个方法不会生效。那为什么会这样呢,之前遇到了相同的问题一直没法解决,在网上给出的乱七八糟的答案感觉也不是正解,终于在今早彻底解决了这个问题。我们先来了解下FragmentPagerAdapter的源代码。

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }  

        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));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }  

        return fragment;
    }  

我们特别关注下以下这句

    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);  

根据原代码我们可以知道系统给每一个Fragment都打上了一个标签,通过标签来寻找相应的fragment,所以当我们第二次进入fragment的时候,fragment的oncreate,oncreateView方法都不会被调用的,因为FragmentPageAdapter中的getitem()方法根本不会被调用,因为系统会根据标签找到相应的fragment,如果已经存在,就不会被调用,fragment有一个缓存机制在这里。那么如果我们一定要更新fragment,那么我们又该如何处理呢,我们就可以以其人之道,还治其人之身,我们可以通过tag标签找到我们相应的fragment,就可以对该fragment进行更新。那么我们现在就开始吧。

我们先来定义一个list<String> tagList 来存储一下tag

private List<String> tagList;

第二部重写instantiateItem方法,把fragment对应的标签存储在taglist集合里

    public Object instantiateItem(ViewGroup container, int position) {
        tagList.add(makeFragmentName(container.getId(), getItemId(position)));
        return super.instantiateItem(container, position);
    }  

makeFragmentName()是FragmentPageAdapter源码里打fragment标签的方法

public static String makeFragmentName(int viewId, int index) {
		return "android:switcher:" + viewId + ":" + index;
	}

第三步我们再adapter里自己写一个update()方法

	public void update(int item) {
			Fragment fragment = fm.findFragmentByTag(tagList.get(item));
			if (fragment != null) {
				switch (item) {
				case 0:

					break;
				case 1:
					((QueryFragment) fragment).update();
					break;
				case 2:

					break;
				default:
					break;
				}
			}
		}

我们可以发现在queryFragment里也有一个update()方法,这个方法呢就是接下来我们用接口回掉机制更新我们相应fragment的要使用的方法,方法里的内容根据自己的需要编译就可以了。

好的现在万事具备只要实现fragment的数据更新问题就可以了,通过接口回掉机制来实现。

首先我们定义一个接口,让我们的主Activity来实现

    public interface FragmentListener {  

        public void onFragmentClickListener(int item);
    }  

之后比如我们在A Fragment 里头插入了数据,需要BFragment对该数据进行显示,那么我们现在AFragment的onAttach方法中

    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            listener = (FragmentListener)activity;
        } catch (Exception e) {
            e.printStackTrace();  

        }
    }  

然后对数据更新的时候,我们就在Afragment调用这个接口

if (listener != null) {
							listener.onFragmentClickListener(1);
						}

然后我们回到Activity实现该接口里的方法,更新BFragment界面

public void onFragmentClickListener(int item) {
		adapter.update(item);
		adapter.update(item);
	}

大功告成了,贴一下完整的MainAcitivity代码吧!

package com.example.account.main;

import java.util.ArrayList;

import java.util.List;

import com.example.account.add.AddFragment;
import com.example.account.query.QueryFragment;
import com.example.account.setting.SettingFragment;
import com.example.account.utils.PagerSlidingTabStrip;
import com.melhc.xiji.R;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

import android.support.v4.view.ViewPager;

import android.util.DisplayMetrics;

import android.util.TypedValue;

import android.view.KeyEvent;
import android.view.ViewGroup;
import android.widget.Toast;

public class MainActivity extends FragmentActivity implements FragmentListener{
	private MyPagerAdapter adapter;
	private AddFragment addFragment;
	private List<String> tagList;
	private SettingFragment settingFragment;
	private boolean isExit;
	private QueryFragment queryFragment;
	private ViewPager pager;
	private PagerSlidingTabStrip tabs;
	private FragmentManager fm;
	private DisplayMetrics dm;
	private List<Fragment> list;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		pager = (ViewPager) findViewById(R.id.pager);
		tabs = (PagerSlidingTabStrip) findViewById(R.id.tabs);
		initFragment();
		fm = getSupportFragmentManager();
		tagList = new ArrayList<String>();
		adapter = new MyPagerAdapter(fm);
		pager.setAdapter(adapter);
		tabs.setViewPager(pager);

	}

	public void initFragment() {
		list = new ArrayList<Fragment>();
		addFragment = new AddFragment();
		queryFragment = new QueryFragment();
		settingFragment = new SettingFragment();
		list.add(addFragment);
		list.add(queryFragment);
		list.add(settingFragment);
	}

	public class MyPagerAdapter extends FragmentPagerAdapter {

		public MyPagerAdapter(FragmentManager fm) {

			super(fm);
		}

		private final String[] titles = { "1", "2", "3" };

		@Override
		public CharSequence getPageTitle(int position) {
			return titles[position];
		}

		@Override
		public int getCount() {
			return titles.length;
		}

		@Override
		public Fragment getItem(int position) {
			return list.get(position);
		}

		public Object instantiateItem(ViewGroup container, int position) {
			tagList.add(makeFragmentName(container.getId(),
					(int) getItemId(position)));
			return super.instantiateItem(container, position);
		}

		public void update(int item) {
			Fragment fragment = fm.findFragmentByTag(tagList.get(item));
			if (fragment != null) {
				switch (item) {
				case 0:

					break;
				case 1:
					((QueryFragment) fragment).update();
					break;
				case 2:

					break;
				default:
					break;
				}
			}
		}

	}

	public static String makeFragmentName(int viewId, int index) {
		return "android:switcher:" + viewId + ":" + index;
	}

	public void onFragmentClickListener(int item) {
		adapter.update(item);
		adapter.update(item);
	}

}

FragmentPageAdapter切换时无法更新数据问题解析

时间: 2024-09-27 15:12:26

FragmentPageAdapter切换时无法更新数据问题解析的相关文章

WPF:解决数据绑定时不更新数据的问题

当用户在窗体控件中修改数据时,如果此时用户点击其它窗体会出现数据没有更新的情况. 这是由于数据绑定默认是通过失去焦点来提交数据的.可通过属性变更方法来更新数据: <TextBox.Text> <Binding Path="MyProperty" UpdateSourceTrigger="PropertyChanged"/> </TextBox.Text> 或者仅使用代码调用UpdateSource强制更新数据: <TextB

listview刷新数据,滑到底部就会更新数据,解析本地数据,用for循环赋值

滑到低不就会有更新,不同于Xlistview,这个用法非常的简单: activity.xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_h

在页面切换时使用RxJava持续更新UI界面

欢迎Follow我的GitHub, 关注我的CSDN. 在应用开发中, 我们需要使用后台任务更新前台界面, 不因页面切换而导致重新开始, 或因某些任务阻塞界面刷新, 比如显示下载或播放进度等. 为了追求更优质的用户体验, 需要大量使用后台任务, 常见的就是异步任务(AsyncTask)和后台服务(Service), 当然还有RxJava. 我写了一个示例, 来讲讲如何使用这些常用的后台方式. 主要 (1) 使用异步任务和后台服务更新页面, 避免内存泄露. (2) 使用RxJava的时间间隔\延迟

mysql insert插入时实现如果数据表中主键重复则更新,没有重复则插入的四种方法

[CSDN下载] Powerdesigner 设计主键code不能重复等问题 [CSDN博客] Oracle中用一个序列给两个表创建主键自增功能的后果 [CSDN博客] MySQL自增主键删除后重复问题 [CSDN博客] mysql 主从复制 双主从复制原理 防止主键重复问题(必看) [CSDN博客] replace into导致mysql自增列导致主键重复问题分析 [CSDN博客] 一个循环更新某库所有表所有非主键列的值方法(sql 2005 & mysql) [CSDN博客] mysql i

iOS:切换视图时,反向传递数据方法一:通知

通知方式: 1.有一个(单例)通知中心,负责管理iOS中的所有通知 2.需要获取某种通知,必须注册成为观察者(订阅) 3.不再需要取某种通知时,要取消注册. 4.你可以向通知中心发送某种通知,通知中心会转发给相应的观察者(订阅者). 将第一个控制器和第二个控制器以modal方式联接后,每一个控制器和各自的类相关联,同时将segue的idetifier标识设置一个名字,正向传数据时,需要根据segue的标识符进行唯一的识别.反向传数据时,采用通知的方法. 1.所有的文件: 2.第一个控制器Firs

iOS:切换视图时,反向传递数据方法二:代理

代理: 1.发送信息的控制器设置一个代理,并自定义一个代理的方法,用来传递数据 2.接受信息的控制器遵循发送信息的控制器的协议 3.接受信息的控制器设置发送信息的控制器的代理为自己self 4.接受信息的控制器实现协议中的方法,即接受数据 首先将控制器通过modal(push)方式联接,同时设置segue的identifier标识,因为这个identifier是与目的控制器唯一的识别的方式,最后再进行代码操作. 1.所有文件: 2.让FirstViewController关联自己的类ViewCo

SQL 更新数据时触发器造成的性能问题

有时候,我们更新数据时,发现更新的时间很长,一般我们会想到查询的性能很慢,是索引引起的.但有时候不一定,触发器也会造成性能问题. 我们需要在更新数据时,先禁用触发器,然后再恢复 1 --禁用触发器 2 DISABLE TRIGGER 触发器名称 ON 表 3 --启用触发器 4 ENABLE TRIGGER 触发器名称 ON 表

数据库更新数据时的传值问题

如果数据库中有多列值时,但是不定个数更新是需要采取“先通过查找数据库中的所有数据保存为一个model类,再给相应要改变的值复制,其他的更新的值保持原有数据不变” 比如:DAL(数据访问层) /// <summary> /// 更新数据,虽然有的时候只是更新某个值,其他的值可以原样更新.在更新之前先要查询一下原有的数据,以便于不更新没必要的更新 /// </summary> /// <param name="model"></param>

更新数据时如何使时间自动更新

更新数据时不能使用time类型 使用timestamp类型才可以自动获取当前系统时间 TIMESTAMP的变体 1,TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP  在创建新记录和修改现有记录的时候都对这个数据列刷新 2,TIMESTAMP DEFAULT CURRENT_TIMESTAMP  在创建新记录的时候把这个字段设置为当前时间,但以后修改时,不再刷新它 3,TIMESTAMP ON UPDATE CURRE