【安卓笔记】ViewPager+Fragment布局中的Fragment数据更新

-------------------------------------------------------------------------------------
请尊重作者劳动成果,转载请声明文章出处(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对象。
效果如下:
【安卓笔记】ViewPager+Fragment布局中的Fragment数据更新
在往下讲之前,有必要让大家了解下FragmentPagerAdapter的特性:
【安卓笔记】ViewPager+Fragment布局中的Fragment数据更新
这段话摘自文档,上面说用户每访问到一个选项卡,代表该选项卡的Fragment对象会被保存到内存中(缓存),这样做的目的自然是节省资源并提高响应速度。但是带来的问题是当我们Fragment的数据发生改变时如何提醒系统重新创建Fragment对象呢?
大家可能会说FragmentPagerAdapter继承自PagerAdapter,而PagerAdapter有个notifiyDataSetChanged方法用于通知系统数据发生改变需更新视图。下面我们就测试下这个方法是否管用。
依然使用上面的代码,稍微修改下。
我想实现这样的效果,当我们点击选项卡2中的一个按钮时,更改选项卡1的布局。
注:如果仅仅修改选项卡1中textView的文字,其实可以使用下面这行代码实现,即在Fragment中通过获取FragmentManager来间接获得另一个Fragment实例,然后调用这个实例的方法设置文本。
getActivity().getSupportFragmentManager().findFragmentById(R.id.fg1);
首先我们在Mainctivity中添加一个方法,用于获取Adapter:
/**
     * 获取适配器
     * @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>
然后在选项卡2中添加一个按钮,布局就不贴了:
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布局:
【安卓笔记】ViewPager+Fragment布局中的Fragment数据更新
点击按钮前:

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

下面说下我的解决方案:
适配器中应该提供一个设置适配器数据的方法,这个方法可以向适配器填充新的数据,并remove掉旧的数据。
按照这个思路,我们重构下MyPagerAdapter类:
首先加上这个成员:
  /**
     * 页面内容集合
     */
    private List<Fragment> fgs = null;
然后为其增加set方法:
 /**
     * 重新设置页面内容
     * @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;
    }
最后再提供一个reLoad方法:
/**
     *当页面数据发生改变时你可以调用此方法
     * 
     * 重新载入数据,具体载入信息由回调函数实现
     */
    public void reLoad()
    {
        if(mListener != null)
        {
            mListener.onReload();
        }
         this.notifyDataSetChanged();//不可少,通知系统数据改变
    }
Activity在设置适配器时,先为适配器实现添加一个回调函数,在回调方法中调用setPagerItems方法重新设置数据。
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);
            }
        });
再刚才的界面2的Fragment的按钮响应事件中调用:
  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();
    }
}
MainActivity:
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;
    }
}
仅仅是提供一种思路,如果大家有好的思路请留言。











【安卓笔记】ViewPager+Fragment布局中的Fragment数据更新,布布扣,bubuko.com

【安卓笔记】ViewPager+Fragment布局中的Fragment数据更新

上一篇:?手机游戏美术工作规范


下一篇:redis持久化之AOF(Append Only File)及其总结