刷新片段内的视图
Posted
技术标签:
【中文标题】刷新片段内的视图【英文标题】:Refreshing a view inside a fragment 【发布时间】:2012-09-24 04:40:52 【问题描述】:我已经搜索了许多看起来像这个问题的问题,但都没有找到我的答案。
我的活动有 3 个可通过操作栏访问的选项卡。我通过添加 3 个片段来扩展我扩展视图类的自定义视图来实现这一点。
在数据库更改的那一刻,我尝试通过调用 invalidate()/postinvalidate() 来刷新选项卡中的视图,但这不起作用。就像我考虑的许多其他选项一样,调用片段的 onCreateView 也是如此。
但是,当我转到另一个选项卡并返回时,已进行更改,并且我的视图已按应有的方式更新。
如何模拟切换到另一个选项卡时发生的相同情况?会发生什么。我试图查看 Fragment 生命周期(试图调用 onCreateView())来弄清楚,但它只是不想按应有的方式刷新/重绘。
数据已正确加载,因为当我切换到另一个选项卡时数据已更改。
我删除了一些代码,因为它不再相关。我实现了 Cursorloaders 而不是我自己的观察者模式来通知更改。这是我现在的主要活动。
问题是,如果我想重绘这些片段中的视图,我现在应该怎么做。如果我应用 fragmentObject.getView().invalidate() 它不起作用。我遇到了和以前一样的问题,但是现在我的 Observer 通知数据库中的更改已通过加载器正确实现。
public class ArchitectureActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor>
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ActionBar actionbar = getActionBar();
actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab EditTab = actionbar.newTab().setText("Edit");
ActionBar.Tab VisualizeTab = actionbar.newTab().setText("Visualize");
ActionBar.Tab AnalyseTab = actionbar.newTab().setText("Analyse");
Fragment editFragment = new EditFragment();
Fragment visualizeFragment = new VisualizeFragment();
Fragment analyseFragment = new AnalyseFragment();
EditTab.setTabListener(new MyTabsListener(editFragment));
VisualizeTab.setTabListener(new MyTabsListener(visualizeFragment));
AnalyseTab.setTabListener(new MyTabsListener(analyseFragment));
actionbar.addTab(EditTab);
actionbar.addTab(VisualizeTab);
actionbar.addTab(AnalyseTab);
ArchitectureApplication architectureApplication = (ArchitectureApplication)getApplicationContext();
architectureApplication.initialize();
getLoaderManager().initLoader(0, null, this);
getLoaderManager().initLoader(1, null, this);
public Loader<Cursor> onCreateLoader(int id, Bundle args)
if (id == 0)
return new CursorLoader(this, Graphprovider.NODE_URI , null, null, null, null);
else if (id == 1)
return new CursorLoader(this, GraphProvider.ARC_URI , null, null, null, null);
return null;
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor)
// Reloading of data, actually happens because when switching to another tab the new data shows up fine
Log.e("Data", "loaded");
public void onLoaderReset(Loader<Cursor> loader)
【问题讨论】:
也许你可以用 savedInstanceState 做点什么?如果您阅读了随 android API 提供的数据库教程,您将看到一个部分,如果 savedInstanceState 更改或未更改,则该部分会阻止执行操作。我记不清了。也许您可能会导致 savedInstanceState 被重置,这会导致数据库更新提示视图刷新?只是一个想法。 我注意到您没有对Fragment#getView()
进行任何调用... 由getView()
返回的视图不是您要操纵的视图吗?也许视图没有被重新绘制的原因是因为您没有更新正确的视图?
在目前的情况下我不叫这个。我尝试调用 editFragment.getView().invalidate();在观察者中,它被调用但它不起作用。我以其他方式调用了正确的视图,方法是将其作为参数传递给也调用上述 ArchitectureData 类中的 addNode() 方法的方法
【参考方案1】:
不要尝试自己调用onCreateView()
...这是一个生命周期方法,只能由框架调用。
Fragment
s 是可重用的 UI 组件。他们有自己的生命周期,展示自己的观点,定义自己的行为。你通常不需要让你的Activity
搞乱Fragment
的内部工作,因为Fragment
的行为应该是独立的,并且独立于任何特定的Activity
。
也就是说,我认为最好的解决方案是让您的每个Fragment
s 实现LoaderManager.LoaderCallbacks<D>
接口。每个Fragment
将初始化一个Loader
(即,如果您使用由SQLite 数据库支持的ContentProvider
,则为CursorLoader
),并且Loader
将负责(1)在后台加载数据线程,以及 (2) 侦听对数据源所做的内容更改,并在内容更改发生时将新数据传递给onLoadFinished()
。
此解决方案比您当前的解决方案更好,因为它完全是事件驱动的。您只需在数据传送到onLoadFinished()
时刷新视图(而不必在每次单击新选项卡时手动检查数据源是否已更改)。
如果你很懒,只想快速解决问题,你也可以在 Fragment
的 onResume()
方法中刷新视图。
【讨论】:
感谢您的回答和见解 :)。目前优先级很快,所以我首先尝试了您的第三个解决方案,但没有奏效。此时,与我的 Contentprovider 通信的类实现了 Observable,我的片段内的视图(或者当我尝试 onCreateView 事物时我的片段)实现了 Observer。这样,更新我的数据库时调用了 update 方法,我尝试调用 invalidate/onCreateView/onResume/... 。我认为这也是事件驱动的。明天我会仔细看看你的第二个解决方案,因为我不熟悉装载机。 @J.Maes 我已经写了很多关于Loader
s 的文章,以this blog post 开头...也许会有所帮助。 CursorLoader
会自动在其Cursor
上注册一个ContentObserver
,因此只要您的ContentProvider
在对数据库进行更改时在query()
和ContentResolver#notifyChange(...)
中调用Cursor#setNotificationUri(...)
,CursorLoader
就会加载新数据并将其交付给onLoadFinished()
。实现自己的ContentObserver
可能会使事情复杂化...
我知道这个实现会更好,但你能说一下与当前情况相比,更新和不更新之间的区别吗?我目前与我自己的观察者的解决方案并不是真正的解决方案,因为我的新数据没有显示(实际绘制)。我快速浏览了您的博文,据我所知,当数据发生变化时,我将不得不在 onLoadFinished()/另一种方法中做一些事情,但是我必须放在那里以确保我的数据被绘制.编辑/你的教程的结构是我见过的最好的 g+1 之一
@J.Maes 感谢您的 +1。 :) 嗯...好吧,我建议的所有Loader
内容都是在您的View
正确实施的假设下制作的。看起来情况就是这样,当您在选项卡之间切换时更改会更新(我认为这是因为Fragment
s 在片段关闭/打开屏幕时完全被破坏/重新创建)。因此,鉴于所有这些,我敢打赌,问题在于您当前在 Fragment
中更新视图的方式不正确。这可能是Fragment
或View
的问题...
@J.Maes 您能否发布一些您目前拥有的代码来说明您如何通知/设置视图的新数据?【参考方案2】:
我有一个类似(虽然不相同)的问题,我可以通过以下方式解决:
在我添加的片段中
public void invalidate()
myView.post(new Runnable()
@Override
public void run()
myView.invalidate();
);
当我想刷新片段的视图 myView 时,我从活动中调用了此方法。 post() 的使用确保了视图只有在准备好被绘制时才会失效。
【讨论】:
你如何从活动中调用这个方法?我们错过了您回答中的重要部分。【参考方案3】:我找到了一种解决方法来刷新我的片段中的视图。每次更新数据库时,我都会重新创建(新的 CustomView)视图。之后我调用 setContentView(CustomView view)。它看起来更像是一种 hack,但它确实有效,而我尝试过的其他方法都没有。
虽然我的问题并没有真正解决,但我还是把奖励给了 Alex Lockwood。他对 Loaders 的建议使我的应用程序变得更好,并促使我继续寻找最终找到的解决方案。
【讨论】:
对不起,我不能提供太多建议...我以前从未真正实现过我自己的自定义视图,所以我没有什么可以放弃的。我很高兴您找到了解决方法,并且我帮助您改进了您的应用程序。 :) @J.Maes 我也有同样的要求,能否告诉你你创建了一个 CustomView 并用它来设置内容视图。【参考方案4】:我有同样的问题。 我的解决方案是分离片段并重新附加它。
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment f = getFragment(action);
if(forceUpdate)
fragmentTransaction.detach(f);
fragmentTransaction.attach(f);
fragmentTransaction.replace(R.id.mainFragment, f);
fragmentTransaction.commit();
currentAction = action;
【讨论】:
Mik 的“修复”也对我有用....有谁知道更优雅的解决方案或可以解释为什么会这样? 这会进入 Activity 内部吗? 基本上是的。 getSupportFragmentManager() 是 FragmentActivity 的一个方法。但是,只要您有 FragmentManager 实例,您就可以放在任何地方。【参考方案5】:最适合我的解决方案:
@Override
public void onPause()
super.onPause();
if (isRemoving() && fragmentView != null)
((ViewGroup) fragmentView).removeAllViews();
【讨论】:
以上是关于刷新片段内的视图的主要内容,如果未能解决你的问题,请参考以下文章
片段中的 notifyDataSetChanged() 不刷新列表视图