请尊重作者劳动成果,转载请声明文章出处(http://blog.csdn.net/chdjj/)
-------------------------------------------------------------------------------------
相信大家对ViewPager和Fragment都比较熟悉了。使用ViewPager+Fragment可以实现”选项卡“布局,通过左右滑动屏幕切换选项卡。
但是有些场景,我们的Fragment中的内容不是固定的,甚至布局都不是固定的,这时我们需要动态更新Fragment的数据或布局。所以本文将介绍更新Fragment数据的一种方法(可能不是最好的,如果大家有更好的方法一定要跟我说啊~)。
首先我们快速实现下“选项卡”切换效果。
注:为了简单起见,我们不加选项卡的标题。
步骤很简单,在activity布局中创建一个ViewPager节点,为ViewPager设置适配器(PagerAdapter),适配器产生数据填充ViewPager。
Activity布局:
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- tools:context=".MainActivity" >
- <android.support.v4.view.ViewPager
- android:id="@+id/viewpager"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" />
- </LinearLayout>
主界面只有一个ViewPager节点。
下面创建3个Fragment:
- package com.example.viewpagerdemo2;
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- public class Tab3 extends Fragment
- {
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState)
- {
- return inflater.inflate(R.layout.tab3,null);
- }
- }
代码很简单,直接在oncreateView方法中使用布局填充器(LayoutInflater)填充一个View布局即可。
布局如下:
- <?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:background="#0000ff"
- android:orientation="vertical" >
- <TextView
- android:id="@+id/tab1"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="我是第三个界面"
- />
- </LinearLayout>
其他两个Fragment跟这个一模一样,这里就不贴了。
Fragment都创建好之后,我们来写Activity的逻辑,我们需要为ViewPager指定一个PagerAdapter。
google为我们提供了方便的类叫FragmentPagerAdapter,我们只需继承这个类并复写getItem和getCount即可。
MainActivity如下:
- package com.example.viewpagerdemo2;
- import android.os.Bundle;
- 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;
- public class MainActivity extends FragmentActivity
- {
- private ViewPager vPager = null;
- private static final int TAB_COUNT = 3;
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- vPager = (ViewPager) findViewById(R.id.viewpager);
- vPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));
- }
- public class MyPagerAdapter extends FragmentPagerAdapter
- {
- public MyPagerAdapter(FragmentManager fm)
- {
- super(fm);
- }
- @Override
- public Fragment getItem(int position)
- {
- switch (position)
- {
- case 0:
- return new Tab1();
- case 1:
- return new Tab2();
- case 2:
- return new Tab3();
- }
- return null;
- }
- @Override
- public int getCount()
- {
- return TAB_COUNT;
- }
- }
- }
代码很简单,就不过多解释了。我在FragmentPagerAdapter的getItem方法中根据position直接new出Fragment对象。
效果如下:
在往下讲之前,有必要让大家了解下FragmentPagerAdapter的特性:
这段话摘自文档,上面说用户每访问到一个选项卡,代表该选项卡的Fragment对象会被保存到内存中(缓存),这样做的目的自然是节省资源并提高响应速度。但是带来的问题是当我们Fragment的数据发生改变时如何提醒系统重新创建Fragment对象呢?
大家可能会说FragmentPagerAdapter继承自PagerAdapter,而PagerAdapter有个notifiyDataSetChanged方法用于通知系统数据发生改变需更新视图。下面我们就测试下这个方法是否管用。
依然使用上面的代码,稍微修改下。
我想实现这样的效果,当我们点击选项卡2中的一个按钮时,更改选项卡1的布局。
注:如果仅仅修改选项卡1中textView的文字,其实可以使用下面这行代码实现,即在Fragment中通过获取FragmentManager来间接获得另一个Fragment实例,然后调用这个实例的方法设置文本。
- getActivity().getSupportFragmentManager().findFragmentById(R.id.fg1);
- /**
- * 获取适配器
- * @return
- */
- public MyPagerAdapter getAdapter()
- {
- return adapter;
- }
这里先贴出更新的布局:
tab_new.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:background="#ff0000"
- android:orientation="vertical" >
- <TextView
- android:id="@+id/tab1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="我是第一个界面--->已更新"
- />
- <TextView
- android:id="@+id/tab1_ss"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="我是第一个界面--->已更新"
- />
- </LinearLayout>
- package com.example.viewpagerdemo2;
- import android.content.Context;
- import android.content.SharedPreferences;
- import android.content.SharedPreferences.Editor;
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.util.Log;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.view.ViewGroup;
- import android.widget.Button;
- import android.widget.Toast;
- public class Tab2 extends Fragment
- {
- private static final String TAG = "Tab2";
- private Button but = null;
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState)
- {
- View view = inflater.inflate(R.layout.tab2,null);
- but = (Button) view.findViewById(R.id.but);
- Log.i(TAG,"TAB2 CREATED...");
- return view;
- }
- @Override
- public void onActivityCreated(Bundle savedInstanceState)
- {
- super.onActivityCreated(savedInstanceState);
- but.setOnClickListener(new OnClickListener()
- {
- @Override
- public void onClick(View v)
- {
- SharedPreferences sp = Tab2.this.getActivity().getSharedPreferences(Tab1.CONTENT_VIEW,Context.MODE_PRIVATE);
- boolean state = sp.getBoolean(Tab1.IS_UPDATE, false);
- Toast.makeText(getActivity(),state+"",0).show();
- Editor editor = sp.edit();
- editor.putBoolean(Tab1.IS_UPDATE,!state);
- editor.commit();
- MainActivity a = (MainActivity) getActivity();
- a.getAdapter().notifyDataSetChanged();
- }
- });
- }
- }
按钮的响应事件执行这样的逻辑:当点击按钮时,先去sharepref中寻找某个属性,如果该属性为true,那就写回false,属性为false,那就写回true。最后调用notifyDataSetChanged方法。
最后我们看下选项卡1的逻辑:
- package com.example.viewpagerdemo2;
- import android.content.Context;
- import android.content.SharedPreferences;
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.util.Log;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- public class Tab1 extends Fragment
- {
- public static final String CONTENT_VIEW = "content_view";
- public static final String IS_UPDATE = "is_update";
- private static final String TAG = "Tab1";
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState)
- {
- Log.i(TAG,"TAB1 CREATED...");
- SharedPreferences sp =getActivity().getSharedPreferences(CONTENT_VIEW,Context.MODE_PRIVATE);
- boolean is_update = sp.getBoolean(IS_UPDATE,false);
- View view = null;
- if(is_update)
- {
- view = inflater.inflate(R.layout.tab1_new, null);
- }else
- {
- view = inflater.inflate(R.layout.tab1,null);
- }
- return view;
- }
- }
选项卡1从sharepref中获取该属性,然后根据该属性的值设置布局。这样我们就完成了整个代码的修改。下面测试下:
界面2布局:
点击按钮前:
点击按钮后:
很遗憾,并没有实现我们的效果(原因我也不清楚,有知道的告诉我一声啊)。
然而,比较有趣的是当我们点击按钮后,并不马上滑回第一个界面,而是先滑到第三个界面,再滑到第一个界面,惊奇的发现,布局改变了:
这里可能是GC回收掉了代表选项卡1的Fragment1对象,这时当我们滑会第一个界面时,重新创建了Fragment。这显然不是我们期望的效果。
下面说下我的解决方案:
适配器中应该提供一个设置适配器数据的方法,这个方法可以向适配器填充新的数据,并remove掉旧的数据。
按照这个思路,我们重构下MyPagerAdapter类:
首先加上这个成员:
- /**
- * 页面内容集合
- */
- private List<Fragment> fgs = null;
- /**
- * 重新设置页面内容
- * @param items
- */
- public void setPagerItems(List<Fragment> items)
- {
- if (items != null)
- {
- for (int i = 0; i < fgs.size(); i++)
- {
- mFragmentManager.beginTransaction().remove(fgs.get(i)).commit();
- }
- fgs = items;
- }
- }
这样当数据改变时,我们仅需调用setPagerItems方法即可重新设置数据。
但是考虑到刚才的需求是在一个Fragment中修改另一个Fragment布局,在Fragment中调用setPagerItems似乎并不是很优雅,因为该Fragment并不应该知道父视图中有哪些选项卡(Fragment),故而我们应该让Activity调用setPagerItems方法。
这时我们可以这样做:
在适配器中添加一个回调接口:
- /**
- * @author Rowand jj
- *回调接口
- */
- public interface OnReloadListener
- {
- public void onReload();
- }
- public void setOnReloadListener(OnReloadListener listener)
- {
- this.mListener = listener;
- }
- /**
- *当页面数据发生改变时你可以调用此方法
- *
- * 重新载入数据,具体载入信息由回调函数实现
- */
- public void reLoad()
- {
- if(mListener != null)
- {
- mListener.onReload();
- }
- this.notifyDataSetChanged();//不可少,通知系统数据改变
- }
- adapter.setOnReloadListener(new OnReloadListener()
- {
- @Override
- public void onReload()
- {
- fgs = null;
- List<Fragment> list = new ArrayList<Fragment>();
- list.add(new Tab1());
- list.add(new Tab2());
- list.add(new Tab3());
- adapter.setPagerItems(list);
- }
- });
- MainActivity a = (MainActivity) getActivity();
- a.getAdapter().reLoad();
即可更新布局,图就不贴了。
最后贴出完整的MyPagerAdapter源码和MainActivity的源码:
MyPagerAdapter
:
- package com.example.viewpagerdemo2;
- import java.util.List;
- import android.support.v4.app.Fragment;
- import android.support.v4.app.FragmentManager;
- import android.support.v4.app.FragmentPagerAdapter;
- import android.util.Log;
- /**
- * @author Rowand jj
- * 页面适配器
- */
- public class MyPagerAdapter extends FragmentPagerAdapter
- {
- private static final String TAG = "YiPageAdapter";
- /**
- * 页面内容集合
- */
- private List<Fragment> fgs = null;
- private FragmentManager mFragmentManager;
- /**
- * 当数据发生改变时的回调接口
- */
- private OnReloadListener mListener;
- public MyPagerAdapter(FragmentManager fm, List<Fragment> fgs)
- {
- super(fm);
- this.fgs = fgs;
- mFragmentManager = fm;
- }
- @Override
- public Fragment getItem(int index)
- {
- Log.i(TAG,"ITEM CREATED...");
- return fgs.get(index);
- }
- @Override
- public int getCount()
- {
- return fgs.size();// 返回选项卡总数
- }
- @Override
- public int getItemPosition(Object object)
- {
- return POSITION_NONE;
- }
- /**
- * 重新设置页面内容
- * @param items
- */
- public void setPagerItems(List<Fragment> items)
- {
- if (items != null)
- {
- for (int i = 0; i < fgs.size(); i++)
- {
- mFragmentManager.beginTransaction().remove(fgs.get(i)).commit();
- }
- fgs = items;
- }
- }
- /**
- *当页面数据发生改变时你可以调用此方法
- *
- * 重新载入数据,具体载入信息由回调函数实现
- */
- public void reLoad()
- {
- if(mListener != null)
- {
- mListener.onReload();
- }
- this.notifyDataSetChanged();
- }
- public void setOnReloadListener(OnReloadListener listener)
- {
- this.mListener = listener;
- }
- /**
- * @author Rowand jj
- *回调接口
- */
- public interface OnReloadListener
- {
- public void onReload();
- }
- }
- package com.example.viewpagerdemo2;
- import java.util.ArrayList;
- import java.util.List;
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.support.v4.app.FragmentActivity;
- import android.support.v4.view.ViewPager;
- import com.example.viewpagerdemo2.MyPagerAdapter.OnReloadListener;
- public class MainActivity extends FragmentActivity
- {
- private ViewPager vPager = null;
- private static final int TAB_COUNT = 3;
- private MyPagerAdapter adapter = null;
- private List<Fragment> fgs = null;
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- vPager = (ViewPager) findViewById(R.id.viewpager);
- fgs = new ArrayList<Fragment>();
- fgs.add(new Tab1());
- fgs.add(new Tab2());
- fgs.add(new Tab3());
- adapter = new MyPagerAdapter(getSupportFragmentManager(), fgs);
- adapter.setOnReloadListener(new OnReloadListener()
- {
- @Override
- public void onReload()
- {
- fgs = null;
- List<Fragment> list = new ArrayList<Fragment>();
- list.add(new Tab1());
- list.add(new Tab2());
- list.add(new Tab3());
- adapter.setPagerItems(list);
- }
- });
- vPager.setAdapter(adapter);
- }
- /**
- * 获取适配器
- * @return
- */
- public MyPagerAdapter getAdapter()
- {
- return adapter;
- }
- }