-------------------------------------------------------------------------------------
请尊重作者劳动成果,转载请声明文章出处(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);
首先我们在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布局:
点击按钮前:
点击按钮后:
很遗憾,并没有实现我们的效果(原因我也不清楚,有知道的告诉我一声啊)。
然而,比较有趣的是当我们点击按钮后,并不马上滑回第一个界面,而是先滑到第三个界面,再滑到第一个界面,惊奇的发现,布局改变了:
这里可能是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;
}
}
仅仅是提供一种思路,如果大家有好的思路请留言。

本文介绍了在Android中使用ViewPager+Fragment实现选项卡布局时,如何动态更新Fragment的数据和布局。通过创建不同的Fragment并设置PagerAdapter,实现了选项卡切换。当需要更新Fragment内容时,由于Fragment会被缓存,直接调用PagerAdapter的notifyDataSetChanged()方法无法立即更新。解决办法是通过适配器添加一个回调接口,在Fragment中调用该接口通知Activity重新设置数据。
2950

被折叠的 条评论
为什么被折叠?



