如何在listfragment使用中使用loader

Android实战技巧:Fragment的那些坑 - 推酷
Android实战技巧:Fragment的那些坑
Fragment是Android在3.0(Homeycomb)版本时加入的用以更灵活的构建多屏幕界面的可UI组件。关于Fragment以基本使用方法可以参考官方的
。 但是Fragment使用起来却远没有教程中说的那样简单,也远比Activity要复杂一些,这里总结了孤在使用Fragment时所遇到的坑。
嵌套Fragment时Duplicated id或者Tag之痒
这是一个小坑,但是初学者很容易遇到,特别是在Fragment之中套有Fragment时,且又是布局中添加子Fragment时更容易遇到。
Fragment中套有另一个Fragment,当第二次进入父Fragment时或者由Fragment创建的界面时会抛异常,大致意思是子Fragment的Id或Tag重复了。如果你在layout中给子fragment加了id或者tag,那么一定会遇到此异常。
在添加Fragment时都可以为Fragment指定一个Id或者Tag用以标识这个Fragment。因为每个Activity所附带的Fragment都是放在一个对象池中,在Activity的生命周期里,Fragment仍然在池中,即使是把某一个Fragment从Activity中detach掉(也即用FragmentManager pop掉),这个池是由FragmentManager来管理的。当你再次要以某个id或者Tag添加Fragment时,FragmentManager会在池中检索,如果发现已经存在Fragment对象带有此Id或者Tag时,就会抛此异常并报怨Id重复。这么做的目的就是减少对象的创建,尽可以的复用对象。
如何破解:
. 在布局中写fragment时,不要添加id或者tag;
. 如果非要添加id或者tag,就在代码中添加fragment,如使用Id或者Tag时,先到FragmentManager中查找对象是否存在,不存在时再创建,也即:
Fragment target = getFragmentManager().findFragmentByTag(&tag&);
if (target == null) {
targe = new SomeFragment();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(R.id.content, target, &tag&);
ft.commit();
replace之痛
当有二个相同的整体页面层叠时,想把最后一个布局中的某个用Fragment来replace,会发现,它把前面的replace,后面的没效果。
布局的Id在一个窗体(Activity)中是唯一的,Fragment的replace也是使用此唯一的Id来把相应布局替换成Fragment的。当相同的页面层叠时,同一个Id的布局出现了二次,但Id是一样的。所以FragmentTransaction在replace时仅替换了一个。而不会像期待的那样,替换最后一个页面。
如何破解:
如果相同的页面非要层叠,要么不使用Fragment,要么为布局设置不同的Id。这种情况多出现在布局的复用上面,比如某二个页面长的像,所以复用了同一整体布局。但实际的逻辑上不是相同的页面,完全可以为布局设置不同的Id。
可见性之疼
当有多个Fragment层叠在一起时,每个Fragment如何能感知其对用户的可见性。比如应用有三个页面,A,B和C,比如A是整体类别列表,B是每个类别的详情,C又是类别的某种更详细的信息,当C显示出来时,A和B怎么能知道它其实对于用户已经不可见了,所以就可以不刷新,不加载数据等等。当C被用户BACK后,B又如何感觉它变成可见了?
Fragment的生命周期与Activity是一样的,添加到Activity会把OnCreate类似的回调走一遍,然后,Activity onResume/onPause/onstart/onStop时,其所持有的Fragment也走相应的onResume/onPause/onstart/onPause。但是Fragment与Activity非常不同的是,Activity当有另一个Activity显示时,当前的Activity会走onPause/onStop,而Fragment则完全没有感知。最多只能从FragmentManager那里知道BackStackState改变了,但是是Fragment增加了,还是减少了,并不能知道。
如何破解:
这个一个非常令人蛋疼的问题,简单的页面还好,但是涉及到数据加载或者要针对某些事件(网络)刷新时就有问题了,对用户不可见的页面没必要刷新。可行的解法就是:
. 监听FragmentManager的BackStackState的改变
. 定义页面路径深度然后与BackStack深度比较,以感知是否对用户可见 如前面A是一级,其path为1,B是2,C是3。当前Stack深度为3时,C是可见的,A与B不可见,以此类推。
空白区域的点击之脓
一个Fragment,层叠在另外一个Fragment或者Activity之上,此Fragment中有一些空白区域,也即Widget之外的空白区域,当点击这些空白区域的时候发现这个Fragment下面的Fragment或者Activity中的View收到了事件并且响应了点击事件。
Fragment的本质就是一个View布局的管理器,当Fragment attach到Activity时,其实就是把Fragment#onCreateView()返回的View,替换掉(如果是用replace)FragmentTransaction#replace中指定的View,或者添加到(如果是add)FragmentTransaction#add()中指定的ViewGroup里面。
当我们以层叠方式显示多个Fragment时,通常的做法就是弄一个FrameLayout,然后每次把Fragment add到此布局。因此,这时Activity的页面布局树实际上就是一个FrameLayout里面包含几个View。
所以,当点击上面Fragment的空白区域时,如果事件没被吃掉,就会向下传递。
如何破解:
在Fragment的根布局加上一个clickable=true,这会让根布局把点击事件吃掉,以防止事件会继续传递下去,造成上面的情况。
Activity重新创建之殇
这个没有一般性的错误,只会有与项目相关的具体的错误异常,或者页面显示不正确。以及为什么教程中都有这么一句:
onCreate(Bundle savedInstance) {
if (savedIntance == null) {
// create fragment and add it to Activity.
Activity除了正常启动走到onCreate,还有另外的入口,比如系统配置信息发生变化时,或者Activity在栈比较深的地方,系统会把Activity杀掉,然后再 重新创建 它,问题就是在这个重新创建。重新创建与新建一个Activity不同,它是要尽可能的恢复先前所在的状态,因为这对用户来说是透明的,也就是说不能让用户感知到,否则体验会相当差。唯一与常规创建的区别就在于传给onCreate的参数savedInstanceState是不是null.
如何破解:
为了能在Activity重建时恢复状态,需要:
. 对于Activity 要在onSaveInstanceState()时,把一些变量保存,然后在onCreate时恢复
. 对于Fragment
告诉系统,你想恢复状态Fragment#setRetainInstance(true)。然后,也在onSavedInstance()中保存状态,在onCreate时恢复。 这就够了,系统会在重新创建Activity时把其所持有的Fragment也创建出来。所以为什么每个Fragment子类都需要定义一个默认的Constructor。更多的可以参考
FragmentTransaction的异步操作之殇
FragmentTransaction是异步的,commit()仅是相当于把操作加入到FragmentManager的队列,然后FragmentManager会在某一个时刻来执行,并不是立即执行。所以,真正开始执行commit()时,如果Activity的生命周期发生了变化,比如走到了onPause,或者走到了onStop,或者onDestroy都走完了,那么就会报出IllegalStateException。具体的原因,以及如何避免可以参考大牛的
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致使用ImageLoader当ListView图片从网络加载时滑动卡,但复用缓存的流畅(ListVIew在Fragment中)
[问题点数:40分]
使用ImageLoader当ListView图片从网络加载时滑动卡,但复用缓存的流畅(ListVIew在Fragment中)
[问题点数:40分]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
匿名用户不能发表回复!|
每天回帖即可获得10分可用分!小技巧:
你还可以输入10000个字符
(Ctrl+Enter)
请遵守CSDN,不得违反国家法律法规。
转载文章请注明出自“CSDN(www.csdn.net)”。如是商业用途请联系原作者。android(16)
* CopyRight 2014 ZhuYan
* @author Zhu Yan
* All right reserved
* Created on
下午3:29:13
package com.zhuyan.
import com.mengqi.base.R;
import android.content.C
import android.database.C
import android.os.B
import android.support.v4.app.F
import android.support.v4.app.LoaderManager.LoaderC
import android.support.v4.content.AsyncTaskL
import android.support.v4.content.L
import android.text.E
import android.text.TextW
import android.view.LayoutI
import android.view.V
import android.view.ViewG
import android.widget.EditT
* @author Zhu Yan
* Created on
下午3:29:13
public class TestFragment extends Fragment implements LoaderCallbacks&Cursor&
private EditText viewEditT
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
viewEditText = (EditText) inflater.inflate(R.layout.name_filter, null);
return viewEditT
public void onActivityCreated(Bundle savedInstanceState)
super.onActivityCreated(savedInstanceState);
getLoaderManager().initLoader(0, null, this);
//对于EditText内容进行监听。然后重启查询器
viewEditText.addTextChangedListener(new TextWatcher()
public void onTextChanged(CharSequence s, int start,
int before, int count)
public void beforeTextChanged(CharSequence s, int start,
int count, int after)
public void afterTextChanged(Editable s)
//变换内容后 重启Loader
getActivity().getSupportLoaderManager().restartLoader(0, null, TestFragment.this);
public Loader&Cursor& onCreateLoader(int arg0, Bundle arg1)
if(arg0 == 0){
return new TestLoader(getActivity());
public void onLoadFinished(Loader&Cursor& arg0, Cursor arg1)
System.out.println(&loader finished &);
public void onLoaderReset(Loader&Cursor& loader)
loader.reset();
* 这是个异步加载的Loader
* 熟悉AsyncTask的应该知道。
* 所以必须覆写loadInBackground();方法。
* loadInBackground()是在异步线程执行的。
* @author Zhu Yan
* Created on
下午3:35:49
private static class TestLoader extends AsyncTaskLoader&Cursor&{
* @param context
public TestLoader(Context context)
super(context);
// TODO Auto-generated constructor stub
public Cursor loadInBackground()
* 必须调用。要不然这个Loader不去加载
protected void onStartLoading()
// LogUnit.Log(TAG, &onStartLoading:&);
forceLoad();
对于TestFragment 我们启动的时候调用onActivityCreated()方法中调用getLoaderManager().initLoader(0, null, this);去启动Loader
然后当EditView的文字改变后。我们又getActivity().getSupportLoaderManager().restartLoader(0, null, TestFragment.this);去重启Loader。
然后我按home键退出。然后再进入该界面。这样会去触发loader重新加载。我发现打印了2遍loader finished.
也就是存在2个loader实例。Api中明确说了。对于同一个Id的Loader只有一个实例存在与一个LoaderManager中。
最后对比getActivity().getSupportLoaderManager().restartLoader(0, null, TestFragment.this); 与getLoaderManager().initLoader(0, null, this);
然后才发现。虽然TestFragment挂载在activity上。但是fragment的LoaderManager跟Activity的LoaderManager不一样。
在我的程序中变现为 TestLoader实例化在fragment一个和activity中一个。导致2个相同id的TestLoader存在于不同的LoaderManager中。
所以导致执行2遍。
public void afterTextChanged(Editable s)
//变换内容后 重启Loader
getLoaderManager()..restartLoader(0, null, TestFragment.this); }
这样改就出现问题了。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:46171次
排名:千里之外
原创:20篇
评论:21条
(2)(4)(5)(8)(1)浅谈Android Fragment嵌套使用存在的一些BUG以及解决方法 - 推酷
浅谈Android Fragment嵌套使用存在的一些BUG以及解决方法
自从Android3.0引入了Fragment之后,使用Activity去嵌套一些Fragment的做法也变得更加流行,这确实是Fragment带来的一些优点,比如说:Fragment可以使你能够将activity分离成多个可重用的组件,每个都有它自己的生命周期和UI,更重要的是Fragment解决了Activity间的切换不流畅,实现了一种轻量及的切换,但是在官方提供的android.support.v4包中,Fragment还是或多或少的存在一些BUG,今天就与大家分享一下这些BUG和解决方法。
Case 1:当使用Fragment去嵌套另外一些子Fragment的时候,我们需要去管理子Fragment,这时候需要调用ChildFragmentManager去管理这些子Fragment,由此可能产生的Exception主要是:
java.lang.IllegalStateException: No activity
首先我们来分析一下Exception出现的原因:
通过DEBUG发现,当第一次从一个Activity启动Fragment,然后再去启动子Fragment的时候,存在指向Activity的变量,但当退出这些Fragment之后回到Activity,然后再进入Fragment的时候,这个变量变成null,这就很容易明了为什么抛出的异常是No activity
这个Exception是由什么原因造成的呢?如果想知道造成异常的原因,那就必须去看Fragment的相关代码,发现Fragment在detached之后都会被reset掉,但是它并没有对ChildFragmentManager做reset,所以会造成ChildFragmentManager的状态错误。
找到异常出现的原因后就可以很容易的去解决问题了,我们需要在Fragment被detached的时候去重置ChildFragmentManager,即:
public void onDetach() {
super.onDetach();
Field childFragmentManager = Fragment.class
.getDeclaredField(&mChildFragmentManager&);
childFragmentManager.setAccessible(true);
childFragmentManager.set(this, null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
Case 2:当我们从一个Activity启动了一个Fragment,然后在这个Fragment中又去实例化了一些子Fragment,在子Fragment中去有返回的启动了另外一个Activity,即通过startActivityForResult方式去启动,这时候造成的现象会是,子Fragment接收不到OnActivityResult,如果在子Fragment中是以getActivity.startActivityForResult方式启动,那么只有Activity会接收到OnActivityResult,如果是以getParentFragment.startActivityForResult方式启动,那么只有父Fragment能接收(此时Activity也能接收),但无论如何子Fragment接收不到OnActivityResult。
这是一个非常奇怪的现象,按理说,应该是让子Fragment接收到OnActivityResult才对,究竟是什么造成的呢?这是由于某位写代码的员工抱怨没发奖金,稍稍偷懒了,少写了一部分代码,没有考虑到Fragment再去嵌套Fragment的情况。
我们来看看FragmentActivity中的代码:
protected void onActivityResult(int requestCode, int resultCode, Intent data)
this.mFragments.noteStateNotSaved();
int index = requestCode && 16;
if (index != 0) {
if ((this.mFragments.mActive == null) || (index & 0) || (index &= this.mFragments.mActive.size())) {
Log.w(&FragmentActivity&, &Activity result fragment index out of range: 0x& + Integer.toHexString(requestCode));
Fragment frag = (Fragment)this.mFragments.mActive.get(index);
if (frag == null) {
Log.w(&FragmentActivity&, &Activity result no fragment exists for index: 0x& + Integer.toHexString(requestCode));
frag.onActivityResult(requestCode & 0xFFFF, resultCode, data);
super.onActivityResult(requestCode, resultCode, data);
很显然,设计者把Fragment的下标+1左移16位来标记这个request是不是Fragment的,拿到result再解码出下标,直接取对应的Fragment,这样并没有去考虑对Fragment嵌套Fragment做一个Map映射,所以出现了这种BUG。
但是如果我们需要在OnActivityResult的时候处理一些事情的话,我们可以通过在子Fragment中以getParentFragment.startActivityForResult的方式来启动,然后在父Fragment中去接收数据,我们需要在子Fragment中提供一个方法,如:getResultData(Object obj),通过父Fragment中的子Fragment实例去调用这个方法,把相应的数据传过去,然后去更新子Fragment。
以上是在使用Fragment去嵌套Fragment的时候可能会遇到的BUG,了解了BUG存在的原因之后,就可以完美的解决问题。
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致

我要回帖

更多关于 fragment tag如何使用 的文章

 

随机推荐