- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
这是我的应用的层次结构:
ViewPager 中的 3 个 fragment 包含一个 FrameLayout:
第一个和第二个 fragment 使用 CursorLoader 从 ContentProvider 获取数据。一切正常,除非发生以下情况:
当前面的情况发生时。第一个和第二个 fragment 留在 loadingSpinner 中,从不显示 ListView 。让我们看看 Fragment1 的代码(第二个 fragment 几乎相同):
public class FragmentOne extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private LinearLayout emptyMsgContainer;
private ListView listView;
private ProgressBar loadingSpinner;
private Details mActivity;
FragmentOneListAdapter mAdapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d("onCreateView()", "FragmentOne");
View view = inflater.inflate(R.layout.fragment_one_list_fragment, container, false);
emptyMsgContainer = (LinearLayout)view.findViewById(R.id.empty_message_container_1);
listView = (ListView)view.findViewById(R.id.listView_1);
loadingSpinner = (ProgressBar)view.findViewById(R.id.loading_spinner_1);
return view;
}
@Override
public void onActivityCreated(Bundle icicle) {
super.onActivityCreated(icicle);
Log.d("onActivityCreated()", "FragmentOne");
mAdapter = new FragmentOneListAdapter(getActivity(), null, 0);
listView.setAdapter(mAdapter);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setOnItemClickListener(mListListener);
// executes initLoader and logs at the same time
Log.d("onActivityCreated()", getActivity().getSupportLoaderManager().initLoader(1, null, this).toString());
}
@Override
public void onResume() {
super.onResume();
Log.d("onResume()", "FragmentOne");
// used to communicate direclty with the MainActivity
MainFragment parentFragment = (MainFragment)
getActivity().getSupportFragmentManager().findFragmentByTag("MAIN_FRAGMENT");
mActivity = (Details)parentFragment.getDetailsListener();
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri uri = ...;
String selection = "...";
String[] selectionArgs = new String[] { ... };
CursorLoader loader = new CursorLoader(getActivity(), uri, null, selection, selectionArgs, null);
Log.d("onCreateLoader()", loader.toString());
return loader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Log.d("onLoadFinished()", loader.toString());
if(data.getCount() == 0) {
loadingSpinner.setVisibility(View.GONE);
listView.setVisibility(View.GONE);
emptyMsgContainer.setVisibility(View.VISIBLE);
} else {
mAdapter.swapCursor(data);
myCursor = data;
loadingSpinner.setVisibility(View.GONE);
listView.setVisibility(View.VISIBLE);
emptyMsgContainer.setVisibility(View.GONE);
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
myCursor = null;
}
private OnItemClickListener mListListener = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mActivity.ShowStoredDetails(position, 1);
}
};
}
我记录了 Activity 和 Fragment 的生命周期回调、onCreateLoader 和 onLoadFinished 以试图弄清楚发生了什么。首先让我们纵向打开应用程序:
23:04:00.089: D/onCreate()(8240): <!> ... 21<!> MainActivity
23:04:00.139: D/onAttach()(8240): <!> ... 61<!> MainFragment
23:04:00.139: D/onCreate()(8240): <!> ... 68<!> MainFragment
23:04:00.139: D/onCreateView()(8240): <!> ... 75<!> MainFragment
23:04:00.169: D/onActivityCreated()(8240): <!> ... 95<!> MainFragment
23:04:00.169: D/onStart()(8240): <!> ... 101<!> MainFragment
23:04:00.169: D/onStart()(8240): <!> ... 31<!> MainActivity
23:04:00.169: D/onResume()(8240): <!> ... 41<!> MainActivity
23:04:00.169: D/onResume()(8240): <!> ... 107<!> MainFragment
23:04:00.179: D/onattach()(8240): <!> ... 51<!> FragmentOne
23:04:00.189: D/onCreate()(8240): <!> ... 57<!> FragmentOne
23:04:00.189: D/onCreateView()(8240): <!> ... 62<!> FragmentOne
23:04:00.199: D/onActivityCreated()(8240): <!> ... 74<!> FragmentOne
23:04:00.199: D/onCreateLoader()(8240): <!> ... 146<!> CursorLoader{405c0e50 id=0}
23:04:00.209: D/onActivityCreated()(8240): <!> ... 83<!> CursorLoader{405c0e50 id=1}
23:04:00.209: D/onStart()(8240): <!> ... 90<!> FragmentOne
23:04:00.209: D/onResume()(8240): <!> ... 96<!> FragmentOne
23:04:00.219: D/onattach()(8240): <!> ... 50<!> FragmentTwo
23:04:00.219: D/onCreate()(8240): <!> ... 56<!> FragmentTwo
23:04:00.219: D/onCreateView()(8240): <!> ... 61<!> FragmentTwo
23:04:00.229: D/onActivityCreated()(8240): <!> ... 73<!> FragmentTwo
23:04:00.239: D/onStart()(8240): <!> ... 88<!> FragmentTwo
23:04:00.259: D/onResume()(8240): <!> ... 94<!> FragmentTwo
23:04:00.479: D/onLoadFinished()(8240): <!> ... 153<!> CursorLoader{405c0e50 id=1}
id=1 的 Loader 不存在,它被创建了。 onCreateLoader() 被调用,然后是 onLoadFinished()。 ListView 已填充并且工作正常。现在让我们旋转到横向:
23:04:26.999: D/onSaveInstanceState()(8240): <!> ... 113<!> MainFragment
23:04:27.009: D/onSaveInstanceState()(8240): <!> ... 105<!> FragmentOne
23:04:27.009: D/onSaveInstanceState()(8240): <!> ... 103<!> FragmentTwo
23:04:27.009: D/onPause()(8240): <!> ... 111<!> FragmentOne
23:04:27.009: D/onPause()(8240): <!> ... 109<!> FragmentTwo
23:04:27.009: D/onPause()(8240): <!> ... 119<!> MainFragment
23:04:27.009: D/onPause()(8240): <!> ... 46<!> MainActivity
23:04:27.019: D/onStop()(8240): <!> ... 117<!> FragmentOne
23:04:27.019: D/onStop()(8240): <!> ... 115<!> FragmentTwo
23:04:27.019: D/onStop()(8240): <!> ... 125<!> MainFragment
23:04:27.019: D/onStop()(8240): <!> ... 51<!> MainActivity
23:04:27.019: D/onDestroyView()(8240): <!> ... 123<!> FragmentOne
23:04:27.019: D/onDestroyView()(8240): <!> ... 121<!> FragmentTwo
23:04:27.029: D/onDestroyView()(8240): <!> ... 131<!> MainFragment
23:04:27.029: D/onDetach()(8240): <!> ... 143<!> MainFragment
23:04:27.039: D/onDestroy()(8240): <!> ... 56<!> MainActivity
23:04:27.069: D/onAttach()(8240): <!> ... 61<!> MainFragment
23:04:27.079: D/onCreate()(8240): <!> ... 21<!> MainActivity
23:04:27.169: D/onCreateView()(8240): <!> ... 75<!> MainFragment
23:04:27.199: D/onActivityCreated()(8240): <!> ... 95<!> MainFragment
23:04:27.199: D/onCreateView()(8240): <!> ... 62<!> FragmentOne
23:04:27.209: D/onActivityCreated()(8240): <!> ... 74<!> FragmentOne
23:04:27.209: D/onActivityCreated()(8240): <!> ... 83<!> CursorLoader{405c0e50 id=1}
23:04:27.219: D/onCreateView()(8240): <!> ... 61<!> FragmentTwo
23:04:27.239: D/onActivityCreated()(8240): <!> ... 73<!> FragmentTwo
23:04:27.239: D/onStart()(8240): <!> ... 101<!> MainFragment
23:04:27.239: D/onStart()(8240): <!> ... 90<!> FragmentOne
23:04:27.249: D/onStart()(8240): <!> ... 88<!> FragmentTwo
23:04:27.249: D/onLoadFinished()(8240): <!> ... 153<!> CursorLoader{405c0e50 id=1}
23:04:27.249: D/onStart()(8240): <!> ... 31<!> MainActivity
23:04:27.259: D/onResume()(8240): <!> ... 41<!> MainActivity
23:04:27.259: D/onResume()(8240): <!> ... 107<!> MainFragment
23:04:27.259: D/onResume()(8240): <!> ... 96<!> FragmentOne
23:04:27.259: D/onResume()(8240): <!> ... 94<!> FragmentTwo
MainActivity 被销毁,Fragment 的 View 层次结构也被取消,但 Fragment 实例保持不变(MainFragment 使用 setRetainInstance(true))。重新创建 MainActivity,MainFragment 附加到它,再次创建 FragmentOne 的 View 层次结构,ListView 填充相同的 Loader id=1,它已经存在,所以只调用 onLoadFinished()。现在让我们按主页按钮停止应用程序:
23:04:59.639: D/onSaveInstanceState()(8240): <!> ... 113<!> MainFragment
23:04:59.649: D/onSaveInstanceState()(8240): <!> ... 105<!> FragmentOne
23:04:59.649: D/onSaveInstanceState()(8240): <!> ... 103<!> FragmentTwo
23:04:59.649: D/onPause()(8240): <!> ... 111<!> FragmentOne
23:04:59.649: D/onPause()(8240): <!> ... 109<!> FragmentTwo
23:04:59.649: D/onPause()(8240): <!> ... 119<!> MainFragment
23:04:59.659: D/onPause()(8240): <!> ... 46<!> MainActivity
23:05:00.059: D/onStop()(8240): <!> ... 117<!> FragmentOne
23:05:00.069: D/onStop()(8240): <!> ... 115<!> FragmentTwo
23:05:00.069: D/onStop()(8240): <!> ... 125<!> MainFragment
23:05:00.069: D/onStop()(8240): <!> ... 51<!> MainActivity
一切都停止了。最后让我们恢复应用程序:
23:05:47.489: D/onDestroyView()(8240): <!> ... 123<!> FragmentOne
23:05:47.489: D/onDestroyView()(8240): <!> ... 121<!> FragmentTwo
23:05:47.499: D/onDestroyView()(8240): <!> ... 131<!> MainFragment
23:05:47.499: D/onDetach()(8240): <!> ... 143<!> MainFragment
23:05:47.499: D/onDestroy()(8240): <!> ... 56<!> MainActivity
23:05:47.509: D/onAttach()(8240): <!> ... 61<!> MainFragment
23:05:47.509: D/onCreate()(8240): <!> ... 21<!> MainActivity
23:05:47.539: D/onCreateView()(8240): <!> ... 75<!> MainFragment
23:05:47.569: D/onActivityCreated()(8240): <!> ... 95<!> MainFragment
23:05:47.569: D/onCreateView()(8240): <!> ... 62<!> FragmentOne
23:05:47.579: D/onActivityCreated()(8240): <!> ... 74<!> FragmentOne
23:05:47.579: D/onCreateLoader()(8240): <!> ... 146<!> CursorLoader{40540548 id=0}
23:05:47.579: D/onActivityCreated()(8240): <!> ... 83<!> CursorLoader{40540548 id=0}
23:05:47.579: D/onCreateView()(8240): <!> ... 61<!> FragmentTwo
23:05:47.589: D/onActivityCreated()(8240): <!> ... 73<!> FragmentTwo
23:05:47.589: D/onStart()(8240): <!> ... 101<!> MainFragment
23:05:47.589: D/onStart()(8240): <!> ... 90<!> FragmentOne
23:05:47.599: D/onStart()(8240): <!> ... 88<!> FragmentTwo
23:05:47.599: D/onStart()(8240): <!> ... 31<!> MainActivity
23:05:47.599: D/onResume()(8240): <!> ... 41<!> MainActivity
23:05:47.599: D/onResume()(8240): <!> ... 107<!> MainFragment
23:05:47.599: D/onResume()(8240): <!> ... 96<!> FragmentOne
23:05:47.599: D/onResume()(8240): <!> ... 94<!> FragmentTwo
Activity 和 Fragments 生命周期完成。 Activity 被重新创建,MainFragment 附加到它。 FragmentOne 的 View 层次结构已创建。但是这一次,LoaderManager 不再包含 id=1 的 Loader。执行 initLoader 时,会调用 onCreateLoader()(它接收到的“id”参数为 1),但不会调用 onLoadFinished(),并且 loadingSpinner 保持可见。
从日志中可以比较应用程序第一次执行时(Loader id=1 不存在)...
onActivityCreated()(8240): <!> ... 83<!> CursorLoader{405c0e50 id=1}
onCreateLoader()(8240): <!> ... 146<!> CursorLoader{405c0e50 id=0}
onLoadFinished()(8240): <!> ... 153<!> CursorLoader{405c0e50 id=1}
...第二次加载程序 id=1 不存在:
onActivityCreated()(8240): <!> ... 83<!> CursorLoader{40540548 id=0}
onCreateLoader()(8240): <!> ... 146<!> CursorLoader{40540548 id=0}
onActivityCreated(initLoader)第一次返回一个id=1的Loader,第二次返回id=0。我只能假设这就是没有第二次调用 onLoadFinished() 的原因。据我所知, LoaderManager 应该在方向改变时保持其状态。对这里发生的事情有什么想法吗?
编辑
我应该提到我正在使用支持库:
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
最佳答案
好的,我发现了类似的情况:Loader restarts on orientation change .
这似乎是 SupportLibrary 中的一个错误,与我对嵌套 fragment 的实现有关。
为了让它工作,我必须更改 LoaderCallbacks
的位置接口(interface)和 initLoader()
,从 FragmentOne 和 FragmentTwo 到 MainActivity。它有点困惑,因为我必须创建一些界面,但它确实有效。
如果有人遇到这种情况,我会解释:
首先,我创建了两个接口(interface):
ListenerFragments
接口(interface),在 MainActivity 中实现,并从 FragmentOne 和 FragmentTwo 中使用以将自己注册到 MainActivity 中作为将要使用加载器的 fragment :
public interface ListenerFragments {
public void setFragmentOne(FragmentsUICallbacks callbacks);
public void setFragmentTwo(FragmentsUICallbacks callbacks);
public void prepareLoader(int id);
}
第二个接口(interface),在FragmentOne和FragmentTwo中实现。并包含将要更改 Fragment 的 UI、交换光标和制作 FrameLayout
的方法。 child ( ListView
, LoadingSpinner
...) 可见与否。此外,这是我们要传递给 MainActivity 的 setFragmentOne()
的接口(interface)。和 setFragmentTwo()
, 因此它可以在 onLoadFinished()
时修改 UI和 onLoaderReset()
被称为:
public interface FragmentsUICallbacks {
public void emptyCursor();
public void assignCursor(Cursor data);
public void clearCursorReferences();
}
MainActivity 正在执行 ListenerFragments
和 LoaderCallbacks<Cursor>
接口(interface):
public class MainActivity extends ActionBarActivity implements LoaderManager.LoaderCallbacks<Cursor>, ListenerFragments {
private FragmentsUICallbacks fragmentOneCallbacks;
private FragmentsUICallbacks fragmentTwoCallbacks;
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri uri;
String selection;
String[] selectionArgs;
switch(id) {
case 1:
uri = ...;
selection = "...";
selectionArgs = new String[] { ... };
return new CursorLoader(this, uri, null, selection, selectionArgs, null);
case 2:
...
}
return null;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
switch(loader.getId()) {
case 1:
if(data.getCount() == 0) {
fragmentOneCallbacks.emptyCursor();
} else {
fragmentOneCallbacks.assignCursor(data);
}
break;
case 2:
...
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
switch(loader.getId()) {
case 1:
fragmentOneCallbacks.clearCursorReferences();
break;
case 2:
...
}
}
@Override
public void setFragmentOne(FragmentsUICallbacks callbacks) {
if(callbacks != null)
this.fragmentOneCallbacks = callbacks;
}
@Override
public void setFragmentTwo(FragmentsUICallbacks callbacks) {
if(callbacks != null)
this.fragmentTwoCallbacks = callbacks;
}
@Override
public void prepareLoader(int id) {
getSupportLoaderManager().initLoader(id, null, this);
}
}
代码非常简单。棘手的部分出现在 FragmentOne 的 onResume()
中。 :
public class FragmentOne extends Fragment implements FragmentsUICallbacks {
...
@Override
public void onResume() {
super.onResume();
MainFragment parentFragment = (MainFragment)
getActivity().getSupportFragmentManager().findFragmentByTag("MAIN_FRAGMENT");
ListenerFragments listenerFragments = (ListenerFragments)parentFragment.getListenerFragments();
listenerFragments.setFragmentOne(this);
listenerFragments.prepareLoader(1);
}
public void emptyCursor() {
loadingSpinner.setVisibility(View.GONE);
listView.setVisibility(View.GONE);
emptyMsgContainer.setVisibility(View.VISIBLE);
}
public void assignCursor(Cursor data) {
mAdapter.swapCursor(data);
myCursor = data;
loadingSpinner.setVisibility(View.GONE);
listView.setVisibility(View.VISIBLE);
emptyMsgContainer.setVisibility(View.GONE);
}
public void clearCursorReferences() {
mAdapter.swapCursor(null);
myCursor = null;
}
}
我们需要获得对 ListenerFragment
的引用MainActivity 正在实现的接口(interface)方法,以便通知它 FragmentOne 将启动加载程序。我们通过 MainFragment 获取该引用,为什么?因为我们不能直接从FragmentOne.onAttach(Activity activity)
得到它,因为它只在应用程序第一次启动时被调用,并且 fragment 既没有被破坏也没有分离,当方向改变时 fragment 从 onDestroyView()
开始。至 onCreateView()
. onAttach()
不被调用。
另一方面,MainFragment 也没有被销毁(setRetainInstance(true)
),但它与旧的 MainActivity 分离,并在方向更改完成时再次附加到新的 MainActivity。我们使用 onAttach()
保存引用,我们创建一个 getter 方法,以便 ViewPager
中的 fragment 可以获得该引用:
public class MainFragment extends Fragment implements OnClickListener {
private ListenerFragments listenerFragments;
@Override
public void onAttach(Activity myActivity) {
super.onAttach(myActivity);
this.listenerFragments = (ListenerFragments)myActivity;
}
public ListenerFragments getListenerFragments() {
return listenerFragments;
}
}
知道了这一点,我们就可以回到FragmentOne.onResume()
,我们在其中获得对 MainFragment 的引用:
MainFragment parentFragment = (MainFragment)
getActivity().getSupportFragmentManager().findFragmentByTag("MAIN_FRAGMENT");
我们使用我们创建的 MainFragment getter 方法来获取对 MainActivity 方法的访问权限:
ListenerFragments listenerFragments = (ListenerFragments)parentFragment.getListenerFragments();
listenerFragments.setFragmentOne(this);
listenerFragments.prepareLoader(1);
基本上就是这样。
关于android - 方向更改时的 LoaderManager 行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23301002/
我想创建一个基于 jQuery 的非常简单的 html 编辑器(不是所见即所得)。 我的问题是如何制作 textarea或 div可能 在上面写一些文字 然后样式即标签(例如 some stuff 将
根据文档 isset 条款“测试此项目中是否已设置给定属性”。我不明白设置属性时 isset 返回 true 还是 false 在下面的代码片段中,当 env.JAVA_HOME 未设置时,java.
我正在尝试取消映射 o这是执行 :only 的默认命令( :help :only ),所以我尝试的第一件事是: nmap o 这种作品,除非我按 ,等待超过timeoutlen ms 然后按 o
我有以下型号: class MetaData(models.Model): created_at = models.DateTimeField(auto_now_add=True, auto_
下面列出了两行代码。两者对日期和时间的期望相同,但只有一个有效。我正在使用 R 3.1。 以下不起作用: DateTime2=strftime("08/13/2010 05:26:24.350", f
我有一个关于 C 代码的问题。 #include void foo(void){ int a; printf("%d\n",a); } void bar(void){
如果文件大小 > 8k,为什么读取的最后一个字节 = 0? private static final int GAP_SIZE = 8 * 1024; public static void main(
我有一个命令 Get-Testdata从不同来源检索测试数据并将这些数据存储到 PSObject以不同的值作为属性。然后将对象总数存储为数组,以便于操作、排序、计算等。 我的问题是我希望能够将这些数据
我正在使用 epoll 将大消息写入使用 HTTP 协议(protocol)的服务器。 fds 都设置为非阻塞,我正在使用边缘触发事件。我知道对于 EPOLLIN,我需要循环读取 fd,直到返回 EA
这对我来说听起来很奇怪: $test_1 = 'string'; $test_2 = '0'; var_dump(intval($test_1)); // Output: int 0 var_dump
这个问题在这里已经有了答案: Java: Integer equals vs. == (7 个回答) 7年前关闭。 请您解释以下行为。 public class EqAndRef { publ
Drupal 的行为到底是什么? 它为模块开发人员提供什么类型的服务层? 它映射到 jQuery.ready 的关系类型是什么? 最佳答案 长版:Drupal.behaviors 不仅仅是 jQuer
以下代码: dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ for (int i=0
人们可以将项目添加到数据库中。我让他们选择在此时添加它,或手动选择日期。 因此我得到了这个 HTML 结构。 (请注意,我将日期和时间选择器妥协为只有一行文本) Selec
创建了一个数据框: simpleDF is.na(simpleDF$vals) [1] TRUE TRUE FALSE > is.nan(simpleDF$vals) [1] FALSE TRU
我有一个大的 docker 镜像 A,我创建了一个新的 Dockerfile FROM A RUN rm /big-folder 我尝试使用以下方法构建图像: docker build --squas
我想知道以下情况下 JVM 的行为是什么: JVM 最小堆大小 = 500MB JVM 最大堆大小 = 2GB 操作系统有 1GB 内存 JVM启动后,程序运行一段时间后,使用内存超过1GB。我想知道
我们正在使用 spikeearrest 策略,但我们不了解其工作原理。峰值逮捕配置如下: 5pm 阅读文档,我们了解到,如果我们在一分钟内调用此流超过 5 次,则该策略将在第 5 次之后
我正在使用 cURL 发送 POST 请求: curl http://tarvos.local:8080/partial_Users/2 -d '{currentPage : 1, firstID :
我的表中有 6442670 条记录,我正在使用以下命令获取它们jdbctemplate 使用行号一次 1000000 个。以下是查询 select * from (select rowNum rn
我是一名优秀的程序员,十分优秀!