ViewPager+Fragment布局中的Fragment数据更新


-------------------------------------------------------------------------------------
请尊重作者劳动成果,转载请声明文章出处(http://blog.csdn.net/chdjj/
-------------------------------------------------------------------------------------
相信大家对ViewPager和Fragment都比较熟悉了。使用ViewPager+Fragment可以实现”选项卡“布局,通过左右滑动屏幕切换选项卡。
但是有些场景,我们的Fragment中的内容不是固定的,甚至布局都不是固定的,这时我们需要动态更新Fragment的数据或布局。所以本文将介绍更新Fragment数据的一种方法(可能不是最好的,如果大家有更好的方法一定要跟我说啊~)。
首先我们快速实现下“选项卡”切换效果。
注:为了简单起见,我们不加选项卡的标题。
步骤很简单,在activity布局中创建一个ViewPager节点,为ViewPager设置适配器(PagerAdapter),适配器产生数据填充ViewPager。
Activity布局:
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.        android:orientation="vertical"  
  6.     tools:context=".MainActivity" >  
  7.      <android.support.v4.view.ViewPager  
  8.         android:id="@+id/viewpager"  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="fill_parent" />  
  11. </LinearLayout>  
主界面只有一个ViewPager节点。
下面创建3个Fragment:
  1. package com.example.viewpagerdemo2;  
  2.   
  3. import android.os.Bundle;  
  4. import android.support.v4.app.Fragment;  
  5. import android.view.LayoutInflater;  
  6. import android.view.View;  
  7. import android.view.ViewGroup;  
  8.   
  9. public class Tab3 extends Fragment  
  10. {  
  11.     @Override  
  12.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  13.             Bundle savedInstanceState)  
  14.     {  
  15.         return inflater.inflate(R.layout.tab3,null);  
  16.     }  
  17. }  
代码很简单,直接在oncreateView方法中使用布局填充器(LayoutInflater)填充一个View布局即可。
布局如下:
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:background="#0000ff"  
  6.     android:orientation="vertical" >  
  7.       
  8.     <TextView   
  9.         android:id="@+id/tab1"  
  10.         android:layout_width="match_parent"  
  11.         android:layout_height="match_parent"  
  12.         android:text="我是第三个界面"  
  13.         />  
  14. </LinearLayout>  
其他两个Fragment跟这个一模一样,这里就不贴了。
Fragment都创建好之后,我们来写Activity的逻辑,我们需要为ViewPager指定一个PagerAdapter。
google为我们提供了方便的类叫FragmentPagerAdapter,我们只需继承这个类并复写getItem和getCount即可。
MainActivity如下:
  1. package com.example.viewpagerdemo2;  
  2.   
  3. import android.os.Bundle;  
  4. import android.support.v4.app.Fragment;  
  5. import android.support.v4.app.FragmentActivity;  
  6. import android.support.v4.app.FragmentManager;  
  7. import android.support.v4.app.FragmentPagerAdapter;  
  8. import android.support.v4.view.ViewPager;  
  9.   
  10. public class MainActivity extends FragmentActivity  
  11. {  
  12.     private ViewPager vPager = null;  
  13.     private static final int TAB_COUNT = 3;  
  14.     @Override  
  15.     protected void onCreate(Bundle savedInstanceState)  
  16.     {  
  17.         super.onCreate(savedInstanceState);  
  18.         setContentView(R.layout.activity_main);  
  19.         vPager = (ViewPager) findViewById(R.id.viewpager);  
  20.         vPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));  
  21.     }  
  22.     public class MyPagerAdapter extends FragmentPagerAdapter  
  23.     {  
  24.         public MyPagerAdapter(FragmentManager fm)  
  25.         {  
  26.             super(fm);  
  27.         }  
  28.         @Override  
  29.         public Fragment getItem(int position)  
  30.         {  
  31.             switch (position)  
  32.             {  
  33.             case 0:  
  34.                 return new Tab1();  
  35.             case 1:  
  36.                 return new Tab2();  
  37.             case 2:  
  38.                 return new Tab3();  
  39.             }  
  40.             return null;  
  41.         }  
  42.   
  43.         @Override  
  44.         public int getCount()  
  45.         {  
  46.             return TAB_COUNT;  
  47.         }  
  48.     }  
  49. }  
代码很简单,就不过多解释了。我在FragmentPagerAdapter的getItem方法中根据position直接new出Fragment对象。
效果如下:
在往下讲之前,有必要让大家了解下FragmentPagerAdapter的特性:
这段话摘自文档,上面说用户每访问到一个选项卡,代表该选项卡的Fragment对象会被保存到内存中(缓存),这样做的目的自然是节省资源并提高响应速度。但是带来的问题是当我们Fragment的数据发生改变时如何提醒系统重新创建Fragment对象呢?
大家可能会说FragmentPagerAdapter继承自PagerAdapter,而PagerAdapter有个notifiyDataSetChanged方法用于通知系统数据发生改变需更新视图。下面我们就测试下这个方法是否管用。
依然使用上面的代码,稍微修改下。
我想实现这样的效果,当我们点击选项卡2中的一个按钮时,更改选项卡1的布局。
注:如果仅仅修改选项卡1中textView的文字,其实可以使用下面这行代码实现,即在Fragment中通过获取FragmentManager来间接获得另一个Fragment实例,然后调用这个实例的方法设置文本。
  1. getActivity().getSupportFragmentManager().findFragmentById(R.id.fg1);  
首先我们在Mainctivity中添加一个方法,用于获取Adapter:
  1. /** 
  2.      * 获取适配器 
  3.      * @return 
  4.      */  
  5.     public MyPagerAdapter getAdapter()  
  6.     {  
  7.         return adapter;  
  8.     }  
这里先贴出更新的布局:
tab_new.xml
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:background="#ff0000"  
  6.     android:orientation="vertical" >  
  7.       
  8.     <TextView   
  9.         android:id="@+id/tab1"  
  10.         android:layout_width="wrap_content"  
  11.         android:layout_height="wrap_content"  
  12.         android:text="我是第一个界面--->已更新"  
  13.         />  
  14.     <TextView   
  15.         android:id="@+id/tab1_ss"  
  16.         android:layout_width="wrap_content"  
  17.         android:layout_height="wrap_content"  
  18.         android:text="我是第一个界面--->已更新"  
  19.         />  
  20. </LinearLayout>  
然后在选项卡2中添加一个按钮,布局就不贴了:
  1. package com.example.viewpagerdemo2;  
  2.   
  3. import android.content.Context;  
  4. import android.content.SharedPreferences;  
  5. import android.content.SharedPreferences.Editor;  
  6. import android.os.Bundle;  
  7. import android.support.v4.app.Fragment;  
  8. import android.util.Log;  
  9. import android.view.LayoutInflater;  
  10. import android.view.View;  
  11. import android.view.View.OnClickListener;  
  12. import android.view.ViewGroup;  
  13. import android.widget.Button;  
  14. import android.widget.Toast;  
  15.   
  16. public class Tab2 extends Fragment  
  17. {  
  18.     private static final String TAG = "Tab2";  
  19.     private Button but = null;  
  20.     @Override  
  21.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  22.             Bundle savedInstanceState)  
  23.     {  
  24.         View view = inflater.inflate(R.layout.tab2,null);  
  25.         but = (Button) view.findViewById(R.id.but);  
  26.         Log.i(TAG,"TAB2 CREATED...");  
  27.         return view;  
  28.     }  
  29.     @Override  
  30.     public void onActivityCreated(Bundle savedInstanceState)  
  31.     {  
  32.         super.onActivityCreated(savedInstanceState);  
  33.         but.setOnClickListener(new OnClickListener()  
  34.         {  
  35.             @Override  
  36.             public void onClick(View v)  
  37.             {  
  38.                 SharedPreferences sp = Tab2.this.getActivity().getSharedPreferences(Tab1.CONTENT_VIEW,Context.MODE_PRIVATE);  
  39.                 boolean state = sp.getBoolean(Tab1.IS_UPDATE, false);  
  40.                 Toast.makeText(getActivity(),state+"",0).show();  
  41.                 Editor editor = sp.edit();  
  42.                 editor.putBoolean(Tab1.IS_UPDATE,!state);  
  43.                 editor.commit();  
  44.                 MainActivity a = (MainActivity) getActivity();  
  45.                 a.getAdapter().notifyDataSetChanged();  
  46.             }  
  47.         });  
  48.     }  
  49. }  
按钮的响应事件执行这样的逻辑:当点击按钮时,先去sharepref中寻找某个属性,如果该属性为true,那就写回false,属性为false,那就写回true。最后调用notifyDataSetChanged方法。
最后我们看下选项卡1的逻辑:
  1. package com.example.viewpagerdemo2;  
  2.   
  3. import android.content.Context;  
  4. import android.content.SharedPreferences;  
  5. import android.os.Bundle;  
  6. import android.support.v4.app.Fragment;  
  7. import android.util.Log;  
  8. import android.view.LayoutInflater;  
  9. import android.view.View;  
  10. import android.view.ViewGroup;  
  11.   
  12. public class Tab1 extends Fragment  
  13. {  
  14.     public static final String CONTENT_VIEW = "content_view";  
  15.     public static final String IS_UPDATE = "is_update";  
  16.     private static final String TAG = "Tab1";  
  17.     @Override  
  18.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  19.             Bundle savedInstanceState)  
  20.     {  
  21.         Log.i(TAG,"TAB1 CREATED...");  
  22.         SharedPreferences sp =getActivity().getSharedPreferences(CONTENT_VIEW,Context.MODE_PRIVATE);  
  23.         boolean is_update = sp.getBoolean(IS_UPDATE,false);  
  24.         View view = null;  
  25.         if(is_update)  
  26.         {  
  27.             view = inflater.inflate(R.layout.tab1_new, null);  
  28.         }else  
  29.         {  
  30.             view = inflater.inflate(R.layout.tab1,null);  
  31.         }  
  32.         return view;   
  33.     }  
  34. }  
选项卡1从sharepref中获取该属性,然后根据该属性的值设置布局。这样我们就完成了整个代码的修改。下面测试下:
界面2布局:
点击按钮前:

点击按钮后:
很遗憾,并没有实现我们的效果(原因我也不清楚,有知道的告诉我一声啊)。
然而,比较有趣的是当我们点击按钮后,并不马上滑回第一个界面,而是先滑到第三个界面,再滑到第一个界面,惊奇的发现,布局改变了:
这里可能是GC回收掉了代表选项卡1的Fragment1对象,这时当我们滑会第一个界面时,重新创建了Fragment。这显然不是我们期望的效果。

下面说下我的解决方案:
适配器中应该提供一个设置适配器数据的方法,这个方法可以向适配器填充新的数据,并remove掉旧的数据。
按照这个思路,我们重构下MyPagerAdapter类:
首先加上这个成员:
  1. /** 
  2.    * 页面内容集合 
  3.    */  
  4.   private List<Fragment> fgs = null;  
然后为其增加set方法:
  1. /**  
  2.     * 重新设置页面内容  
  3.     * @param items  
  4.     */  
  5.    public void setPagerItems(List<Fragment> items)  
  6.    {  
  7.        if (items != null)  
  8.        {  
  9.            for (int i = 0; i < fgs.size(); i++)  
  10.            {  
  11.                mFragmentManager.beginTransaction().remove(fgs.get(i)).commit();  
  12.            }  
  13.            fgs = items;  
  14.        }  
  15.    }  
这样当数据改变时,我们仅需调用setPagerItems方法即可重新设置数据。
但是考虑到刚才的需求是在一个Fragment中修改另一个Fragment布局,在Fragment中调用setPagerItems似乎并不是很优雅,因为该Fragment并不应该知道父视图中有哪些选项卡(Fragment),故而我们应该让Activity调用setPagerItems方法。 这时我们可以这样做:
在适配器中添加一个回调接口:
  1. /** 
  2.      * @author Rowand jj 
  3.      *回调接口 
  4.      */  
  5.     public interface OnReloadListener  
  6.     {  
  7.         public void onReload();  
  8.     }  
再添加一个设置回调接口的方法:
  1. public void setOnReloadListener(OnReloadListener listener)  
  2.     {  
  3.         this.mListener = listener;  
  4.     }  
最后再提供一个reLoad方法:
  1. /** 
  2.      *当页面数据发生改变时你可以调用此方法 
  3.      *  
  4.      * 重新载入数据,具体载入信息由回调函数实现 
  5.      */  
  6.     public void reLoad()  
  7.     {  
  8.         if(mListener != null)  
  9.         {  
  10.             mListener.onReload();  
  11.         }  
  12.          this.notifyDataSetChanged();//不可少,通知系统数据改变  
  13.     }  
Activity在设置适配器时,先为适配器实现添加一个回调函数, 在回调方法中调用setPagerItems方法重新设置数据。
  1. adapter.setOnReloadListener(new OnReloadListener()  
  2.         {  
  3.             @Override  
  4.             public void onReload()  
  5.             {  
  6.                 fgs = null;  
  7.                 List<Fragment> list = new ArrayList<Fragment>();  
  8.                 list.add(new Tab1());  
  9.                 list.add(new Tab2());  
  10.                 list.add(new Tab3());  
  11.                 adapter.setPagerItems(list);  
  12.             }  
  13.         });  
再刚才的界面2的Fragment的按钮响应事件中调用:
  1. MainActivity a = (MainActivity) getActivity();  
  2.   a.getAdapter().reLoad();  
即可更新布局,图就不贴了。

最后贴出完整的MyPagerAdapter源码和MainActivity的源码:
MyPagerAdapter :
  1. package com.example.viewpagerdemo2;  
  2.   
  3. import java.util.List;  
  4.   
  5. import android.support.v4.app.Fragment;  
  6. import android.support.v4.app.FragmentManager;  
  7. import android.support.v4.app.FragmentPagerAdapter;  
  8. import android.util.Log;  
  9.   
  10. /** 
  11.  * @author Rowand jj 
  12.  * 页面适配器 
  13.  */  
  14. public class MyPagerAdapter extends FragmentPagerAdapter  
  15. {  
  16.     private static final String TAG = "YiPageAdapter";  
  17.     /** 
  18.      * 页面内容集合 
  19.      */  
  20.     private List<Fragment> fgs = null;  
  21.     private FragmentManager mFragmentManager;  
  22.     /** 
  23.      * 当数据发生改变时的回调接口 
  24.      */  
  25.     private OnReloadListener mListener;  
  26.   
  27.     public MyPagerAdapter(FragmentManager fm, List<Fragment> fgs)  
  28.     {  
  29.         super(fm);  
  30.         this.fgs = fgs;  
  31.         mFragmentManager = fm;  
  32.     }  
  33.   
  34.     @Override  
  35.     public Fragment getItem(int index)  
  36.     {  
  37.         Log.i(TAG,"ITEM CREATED...");  
  38.         return fgs.get(index);  
  39.     }  
  40.   
  41.     @Override  
  42.     public int getCount()  
  43.     {  
  44.         return fgs.size();// 返回选项卡总数  
  45.     }  
  46.   
  47.     @Override  
  48.     public int getItemPosition(Object object)  
  49.     {  
  50.         return POSITION_NONE;  
  51.     }  
  52.   
  53.     /** 
  54.      * 重新设置页面内容 
  55.      * @param items 
  56.      */  
  57.     public void setPagerItems(List<Fragment> items)  
  58.     {  
  59.         if (items != null)  
  60.         {  
  61.             for (int i = 0; i < fgs.size(); i++)  
  62.             {  
  63.                 mFragmentManager.beginTransaction().remove(fgs.get(i)).commit();  
  64.             }  
  65.             fgs = items;  
  66.         }  
  67.     }  
  68.     /** 
  69.      *当页面数据发生改变时你可以调用此方法 
  70.      *  
  71.      * 重新载入数据,具体载入信息由回调函数实现 
  72.      */  
  73.     public void reLoad()  
  74.     {  
  75.         if(mListener != null)  
  76.         {  
  77.             mListener.onReload();  
  78.         }  
  79.         this.notifyDataSetChanged();  
  80.     }  
  81.     public void setOnReloadListener(OnReloadListener listener)  
  82.     {  
  83.         this.mListener = listener;  
  84.     }  
  85.     /** 
  86.      * @author Rowand jj 
  87.      *回调接口 
  88.      */  
  89.     public interface OnReloadListener  
  90.     {  
  91.         public void onReload();  
  92.     }  
  93. }  
MainActivity:
  1. package com.example.viewpagerdemo2;  
  2. import java.util.ArrayList;  
  3. import java.util.List;  
  4. import android.os.Bundle;  
  5. import android.support.v4.app.Fragment;  
  6. import android.support.v4.app.FragmentActivity;  
  7. import android.support.v4.view.ViewPager;  
  8. import com.example.viewpagerdemo2.MyPagerAdapter.OnReloadListener;  
  9. public class MainActivity extends FragmentActivity  
  10. {  
  11.     private ViewPager vPager = null;  
  12.     private static final int TAB_COUNT = 3;  
  13.     private MyPagerAdapter adapter = null;  
  14.     private List<Fragment> fgs = null;  
  15.     @Override  
  16.     protected void onCreate(Bundle savedInstanceState)  
  17.     {  
  18.         super.onCreate(savedInstanceState);  
  19.         setContentView(R.layout.activity_main);  
  20.           
  21.         vPager = (ViewPager) findViewById(R.id.viewpager);  
  22.         fgs = new ArrayList<Fragment>();  
  23.         fgs.add(new Tab1());  
  24.         fgs.add(new Tab2());  
  25.         fgs.add(new Tab3());  
  26.         adapter = new MyPagerAdapter(getSupportFragmentManager(), fgs);  
  27.         adapter.setOnReloadListener(new OnReloadListener()  
  28.         {  
  29.             @Override  
  30.             public void onReload()  
  31.             {  
  32.                 fgs = null;  
  33.                 List<Fragment> list = new ArrayList<Fragment>();  
  34.                 list.add(new Tab1());  
  35.                 list.add(new Tab2());  
  36.                 list.add(new Tab3());  
  37.                 adapter.setPagerItems(list);  
  38.             }  
  39.         });  
  40.         vPager.setAdapter(adapter);  
  41.     }  
  42.       
  43.     /** 
  44.      * 获取适配器 
  45.      * @return 
  46.      */  
  47.     public MyPagerAdapter getAdapter()  
  48.     {  
  49.         return adapter;  
  50.     }  
  51. }  
仅仅是提供一种思路,如果大家有好的思路请留言。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值