即使在 Fragment 分离后也显示 Toast 消息?
Show Toast message even after Fragment has been detached?
我的应用程序使用带有几个菜单项的导航抽屉,基本上可以打开不同的片段。每个片段可能 运行 之后显示 Toast 消息的 AsyncTask。但是,当用户尝试打开一个片段而另一个片段正在 运行ning 时,我得到一个空指针错误,这是可以理解的,因为原始片段已被分离。
不过,到时候还有Toast节目吗?当在导航器上单击一个项目时,我基本上有这段代码。
public void setFragment(Fragment fragment) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content, fragment)
.commit();
}
然后片段上只是一个简单的异步任务,它在 PostExecute 上显示 Toast 消息。
Toast.makeText(getContext(), t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
感谢任何帮助。
更新(2018 年 3 月 28 日)
我尝试使用侦听器,它似乎有效。
我基本上有一个 BaseFragment,我的所有片段都在其中延伸。我刚刚在其中添加了一个回调。
public class BaseFragment extends Fragment {
private OnToast callback;
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
callback = (BaseFragment.OnToast) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement OnAuthenticateListener");
}
}
public OnToast getCallback() {
return callback;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
}
public interface OnToast {
void toast(String message);
}
}
在我的实际片段中,我有:
public class JailsFragment extends BaseFragment {
private List<Jail> jailList = new ArrayList<>();
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setTitle("Jails");
setAdapter(new JailAdapter());
if (savedInstanceState == null) {
onSwipeRefresh();
}
}
@Override
public void onSwipeRefresh() {
super.onSwipeRefresh();
setRefreshing(true);
jailList.clear();
FreeNAS.getInstance().getJails().getJails(new Callback<List<Jail>>() {
@Override
public void onResponse(Call<List<Jail>> call, Response<List<Jail>> response) {
if (response.code() == 200) {
jailList.addAll(response.body());
notifyDatasetChanged();
} else {
//Toast.makeText(getContext(), response.message(), Toast.LENGTH_LONG).show();
getCallback().toast(response.message());
}
setRefreshing(false);
}
@Override
public void onFailure(Call<List<Jail>> call, Throwable t) {
//Toast.makeText(getContext(), t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
getCallback().toast(t.getLocalizedMessage());
setRefreshing(false);
}
});
}
}
然后在我的 MainActivity 中:
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, BaseFragment.OnToast {
private SharedPreferences pref;
private Toolbar toolbar;
private DrawerLayout drawer;
private ActionBarDrawerToggle toggle;
private NavigationView navigationView;
@Override
public void toast(String message) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pref = PreferenceManager.getDefaultSharedPreferences(this);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
if (savedInstanceState == null) {
setFragment(new AuthenticateFragment());
}
}
@Override
public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
final int id = item.getItemId();
drawer.closeDrawer(GravityCompat.START);
drawer.postDelayed(new Runnable() {
@Override
public void run() {
if (id == R.id.nav_plugins) {
setFragment(new PluginsFragment());
} else if (id == R.id.nav_jails) {
setFragment(new JailsFragment());
} else if (id == R.id.nav_jails_configuration) {
setFragment(new ConfigurationFragment());
} else if (id == R.id.nav_services) {
setFragment(new ServicesFragment());
} else if (id == R.id.nav_alerts) {
setFragment(new AlertsFragment());
} else if (id == R.id.nav_updates) {
setFragment(new UpdatesFragment());
}
}
}, 300);
return true;
}
public void setFragment(Fragment fragment) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content, fragment)
.commit();
}
}
我假设您在此处收到 nullPointer 异常
Toast.makeText(getContext(), t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
我会说你接收到空指针是因为你没有上下文了。
可能的解决方案是:
1- 将您的应用程序声明为单例,然后您可以使用 MyApplication.getInstance() 并将其用作 Toast 消息的上下文。选中此 to see how to create this singleton. Also it is recommended 以使用 applicationContext 而不是 activity 用于 toast 消息
据我了解 getContext() 和 getActivity() 将 return 同一个对象,即 activity 所以这无济于事。
编辑 1:
您提出的解决方案(具有对 Activity 的引用,在本例中作为接口)的问题是您可能存在内存泄漏。当我说 "may have" 是因为你在做一些特别的事情时你把这条线:
setRetainInstance(true);
您可能想阅读这篇文章 post。
当您不再需要 Activity 时(假设您移动到另一个 activity)但片段(对象)尚未被销毁时,将产生内存泄漏原因。另一个例子是互联网连接较低,然后用户可能会离开屏幕移动到另一个屏幕,但您的片段仍然存在,因此您的 activity 无法被垃圾收集。这就是为什么我不推荐你的方法 :).
PS:抱歉回答晚了,我正在休假:P.
我的应用程序使用带有几个菜单项的导航抽屉,基本上可以打开不同的片段。每个片段可能 运行 之后显示 Toast 消息的 AsyncTask。但是,当用户尝试打开一个片段而另一个片段正在 运行ning 时,我得到一个空指针错误,这是可以理解的,因为原始片段已被分离。
不过,到时候还有Toast节目吗?当在导航器上单击一个项目时,我基本上有这段代码。
public void setFragment(Fragment fragment) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content, fragment)
.commit();
}
然后片段上只是一个简单的异步任务,它在 PostExecute 上显示 Toast 消息。
Toast.makeText(getContext(), t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
感谢任何帮助。
更新(2018 年 3 月 28 日)
我尝试使用侦听器,它似乎有效。
我基本上有一个 BaseFragment,我的所有片段都在其中延伸。我刚刚在其中添加了一个回调。
public class BaseFragment extends Fragment {
private OnToast callback;
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
callback = (BaseFragment.OnToast) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement OnAuthenticateListener");
}
}
public OnToast getCallback() {
return callback;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
}
public interface OnToast {
void toast(String message);
}
}
在我的实际片段中,我有:
public class JailsFragment extends BaseFragment {
private List<Jail> jailList = new ArrayList<>();
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setTitle("Jails");
setAdapter(new JailAdapter());
if (savedInstanceState == null) {
onSwipeRefresh();
}
}
@Override
public void onSwipeRefresh() {
super.onSwipeRefresh();
setRefreshing(true);
jailList.clear();
FreeNAS.getInstance().getJails().getJails(new Callback<List<Jail>>() {
@Override
public void onResponse(Call<List<Jail>> call, Response<List<Jail>> response) {
if (response.code() == 200) {
jailList.addAll(response.body());
notifyDatasetChanged();
} else {
//Toast.makeText(getContext(), response.message(), Toast.LENGTH_LONG).show();
getCallback().toast(response.message());
}
setRefreshing(false);
}
@Override
public void onFailure(Call<List<Jail>> call, Throwable t) {
//Toast.makeText(getContext(), t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
getCallback().toast(t.getLocalizedMessage());
setRefreshing(false);
}
});
}
}
然后在我的 MainActivity 中:
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, BaseFragment.OnToast {
private SharedPreferences pref;
private Toolbar toolbar;
private DrawerLayout drawer;
private ActionBarDrawerToggle toggle;
private NavigationView navigationView;
@Override
public void toast(String message) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pref = PreferenceManager.getDefaultSharedPreferences(this);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
if (savedInstanceState == null) {
setFragment(new AuthenticateFragment());
}
}
@Override
public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
final int id = item.getItemId();
drawer.closeDrawer(GravityCompat.START);
drawer.postDelayed(new Runnable() {
@Override
public void run() {
if (id == R.id.nav_plugins) {
setFragment(new PluginsFragment());
} else if (id == R.id.nav_jails) {
setFragment(new JailsFragment());
} else if (id == R.id.nav_jails_configuration) {
setFragment(new ConfigurationFragment());
} else if (id == R.id.nav_services) {
setFragment(new ServicesFragment());
} else if (id == R.id.nav_alerts) {
setFragment(new AlertsFragment());
} else if (id == R.id.nav_updates) {
setFragment(new UpdatesFragment());
}
}
}, 300);
return true;
}
public void setFragment(Fragment fragment) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content, fragment)
.commit();
}
}
我假设您在此处收到 nullPointer 异常
Toast.makeText(getContext(), t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
我会说你接收到空指针是因为你没有上下文了。
可能的解决方案是:
1- 将您的应用程序声明为单例,然后您可以使用 MyApplication.getInstance() 并将其用作 Toast 消息的上下文。选中此
据我了解 getContext() 和 getActivity() 将 return 同一个对象,即 activity 所以这无济于事。
编辑 1:
您提出的解决方案(具有对 Activity 的引用,在本例中作为接口)的问题是您可能存在内存泄漏。当我说 "may have" 是因为你在做一些特别的事情时你把这条线:
setRetainInstance(true);
您可能想阅读这篇文章 post。
当您不再需要 Activity 时(假设您移动到另一个 activity)但片段(对象)尚未被销毁时,将产生内存泄漏原因。另一个例子是互联网连接较低,然后用户可能会离开屏幕移动到另一个屏幕,但您的片段仍然存在,因此您的 activity 无法被垃圾收集。这就是为什么我不推荐你的方法 :).
PS:抱歉回答晚了,我正在休假:P.