Viewpager 的并发症
Complications with Viewpager
我正在使用 ViewPager,其中有 3 个片段。
我知道当 ViewPager 第一次加载时它默认加载所有片段,
最少3个fragment,viewpager执行fragment的所有生命周期方法。
我注意到的问题:
每当我滑动 viewpager 时,所选片段不会再次调用其任何生命周期方法。
因此我无法直接访问全局变量。
例如:如果我有一个在 OnCreateView() 中初始化的首选项,当我尝试通过初始化该片段的实例并调用该片段中的方法从 activity 访问它时,我会得到 NPE。
而且在第一次加载后甚至没有为任何片段调用 onResume。
我想知道的:
如何在 onCreateView() 中初始化视图和首选项后访问它们?
但对于在 onCreateView() 中初始化的按钮,单击它会调用 Web 服务并按我的要求完美运行。如何?
过去 3 天我一直在处理这些问题,并且在谷歌上搜索了很多但没有找到我的答案。
任何帮助将不胜感激。
片段代码:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_dth, container, false);
preferences=
PreferenceManager.getDefaultSharedPreferences(getActivity());
editor = preferences.edit();
editAmt = (EditText) v.findViewById(R.id.editAmt);
browsplan = (Button) v.findViewById(R.id.browsplan);
Log.e("DTH onCreateView ",""+page);
String fontpath = "fonts/OpenSans-Regular.ttf";
Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),
fontpath);
editCustomerid.setTypeface(tf);
editAmt.setTypeface(tf);
token=preferences.getString("Token","-1");
mobileNo=preferences.getString("userMobileNo","0");
browsplan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//new DTHOperator(getActivity()).execute(); HERE IT WORKS PROPERLY
}
});
return v;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser && isResumed()){
new DTHOperator(getActivity()).execute();
Toast.makeText(getActivity(),"setUserVisibleHint DTH "
,Toast.LENGTH_SHORT).show();
}
}
public class DTHOperator extends AsyncTask<Context, Integer, String> {
Context ctx;
DTHOperator(Context ctx) {
this.ctx = ctx;
}
@Override
protected String doInBackground(Context... params) {
List<NameValuePair> telecomdata = new ArrayList<NameValuePair>();
String result;
Log.d("Register Activity Token", /*preferences.getString("Token", "")*/token);
telecomdata.add(new BasicNameValuePair("mobNo", /*preferences.getString("userMobileNo", "")*/mobileNo));
telecomdata.add(new BasicNameValuePair("requestDate", Utilities.getApiCallTimeFormat()));
telecomdata.add(new BasicNameValuePair("reqFrom", "APP"));
Log.v("PostParameters", "telecomdata" + telecomdata);
if (Connectivity.checkNetwork(ctx)) {
result = rechargeUrl.queryRESTurlONline(ctx, "/GetDTHOperatorsService", "post", telecomdata, GenericConstants.PiPay_root);
} else {
result = GenericConstants.NETWORKNOTFOUND;
displayToast("The Internet Connection appears to be offline");
}
return result;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
MyProgress.show(ctx, "", "");
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
try {
if (result != null) {
if (result.equalsIgnoreCase(GenericConstants.NETWORKNOTFOUND)) {
MyProgress.CancelDialog();
Toast.makeText(ctx,"The Internet Connection appears to be offline",Toast.LENGTH_SHORT).show();
return;
}
String[] resArr = result.split("delimiter_");
if (!resArr[0].equals("500")) {
MyProgress.CancelDialog();
Log.d("DTHOperator Response ::", result);
JSONArray jsonArray = null;
JSONObject object = new JSONObject(result);
jsonArray = object.getJSONArray("data");
for (int i = 0; i < jsonArray.length(); i++) {
if (jsonArray.getJSONObject(i).has("opName")) {
operator.add(jsonArray.getJSONObject(i).getString("opName"));
Log.d("", " Question size " + operator.size());
}
}
if(MyProgress.isShowingProgress())
MyProgress.CancelDialog();
editAmt.setText("2000"); // NPE HERE Also setting the value
} else {
MyProgress.CancelDialog();
displayToast("Failure");
}
}
} catch (Exception e) {
e.printStackTrace();
MyProgress.CancelDialog();
}finally {
if(MyProgress.isShowingProgress())
MyProgress.CancelDialog();
}
}
您可以保留在 ViewPager
中使用的 Fragments
实例。这是一个例子:
在您的 ViewPagerAdapter
中定义一个 SparseArray
并覆盖如下方法:
SparseArray<Fragment> registeredFragments = new SparseArray<>();
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
并在 ViewPager
中添加一个 PageChangeListener
:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// Here's your fragment instance
// You can access it's methods, variables, views etc.
YourFragment fragment =(YourFragment)yourPagerAdapter.getRegisteredFragment(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
希望对您有所帮助。祝你好运!
编辑:
您可以定义片段的视图 public 或为视图定义 getter 方法并从片段实例进行访问,例如:
public TextView mTextView;
或
private TextView mTextVieW;
public TextView getTextView(){
return this.mTextView;
}
您可以像下面这样访问它:
TextView textView = yourFragmentInstance.mTetxView;
或
TextView textview = yourFragmentInstance.getTextView();
使用以下覆盖方法
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && isResumed()) {
}
}
这将使您可以选择在用户当前查看片段时调用任何内容。
- 向系统设置有关此片段的 UI 当前是否对用户可见 * 的提示。此提示默认为 true 并且在片段实例*状态保存和恢复中持久存在。
应用程序可以将此设置为 false 以指示片段的 UI 是
- 滚出可见性或用户无法直接看到。
- 系统可能会使用它来确定片段生命周期更新等操作的优先级
- 或加载程序排序行为。
*
- @param isVisibleToUser 如果此片段的 UI 当前对用户可见(默认),则为真,
- 如果不是则为假。
这在我的上一个项目中对我有用。我也喜欢这种方法,因为我不需要做任何奇怪的事情。
基本上,在我的 viewpager 适配器中,我添加了一个方法来跟踪片段标签和相关位置。
public class TabAdapter extends FragmentPagerAdapter {
private Map<Integer, String> tagMap = new HashMap<>(2); // matches tab count
public TabAdapter(FragmentManager fMgr) {
super(fMgr);
}
// !!!!!!!!!!!!!!!!!!!!!!!!!!!
// Capturing the tag that is auto-generated
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment frag = (Fragment) super.instantiateItem(container, position);
tagMap.put(position, frag.getTag());
return frag;
}
/**
* Return the Fragment associated with a specified position.
*
* @param position
*/
@Override
public Fragment getItem(int position) {
Fragment frag = null;
...
return frag;
}
/**
* Return the number of views available.
*/
@Override
public int getCount() {
return 2;
}
@Override
public CharSequence getPageTitle(int position) {
String title = null;
...
return title;
}
// !!!!!!!!!!!!!!!!!!!!!!!!!!!
public String getFragmentTag(int position) {
return tagMap.get(position);
}
} // end of class TabAdapter
然后在我的 activity(事件方法)中访问:
String fragTag = tabAdapter.getFragmentTag(tabPosition);
Fragment frag = getSupportFragmentManager().findFragmentByTag(fragTag);
当然这意味着您必须在包含 activity.
中保留适配器的引用
从这里开始,我的片段将具有简单的回调和显式方法来更新我需要更新的任何视图。
我正在使用 ViewPager,其中有 3 个片段。
我知道当 ViewPager 第一次加载时它默认加载所有片段,
最少3个fragment,viewpager执行fragment的所有生命周期方法。
我注意到的问题:
每当我滑动 viewpager 时,所选片段不会再次调用其任何生命周期方法。
因此我无法直接访问全局变量。
例如:如果我有一个在 OnCreateView() 中初始化的首选项,当我尝试通过初始化该片段的实例并调用该片段中的方法从 activity 访问它时,我会得到 NPE。
而且在第一次加载后甚至没有为任何片段调用 onResume。
我想知道的:
如何在 onCreateView() 中初始化视图和首选项后访问它们?
但对于在 onCreateView() 中初始化的按钮,单击它会调用 Web 服务并按我的要求完美运行。如何?
过去 3 天我一直在处理这些问题,并且在谷歌上搜索了很多但没有找到我的答案。
任何帮助将不胜感激。
片段代码:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_dth, container, false);
preferences=
PreferenceManager.getDefaultSharedPreferences(getActivity());
editor = preferences.edit();
editAmt = (EditText) v.findViewById(R.id.editAmt);
browsplan = (Button) v.findViewById(R.id.browsplan);
Log.e("DTH onCreateView ",""+page);
String fontpath = "fonts/OpenSans-Regular.ttf";
Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),
fontpath);
editCustomerid.setTypeface(tf);
editAmt.setTypeface(tf);
token=preferences.getString("Token","-1");
mobileNo=preferences.getString("userMobileNo","0");
browsplan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//new DTHOperator(getActivity()).execute(); HERE IT WORKS PROPERLY
}
});
return v;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser && isResumed()){
new DTHOperator(getActivity()).execute();
Toast.makeText(getActivity(),"setUserVisibleHint DTH "
,Toast.LENGTH_SHORT).show();
}
}
public class DTHOperator extends AsyncTask<Context, Integer, String> {
Context ctx;
DTHOperator(Context ctx) {
this.ctx = ctx;
}
@Override
protected String doInBackground(Context... params) {
List<NameValuePair> telecomdata = new ArrayList<NameValuePair>();
String result;
Log.d("Register Activity Token", /*preferences.getString("Token", "")*/token);
telecomdata.add(new BasicNameValuePair("mobNo", /*preferences.getString("userMobileNo", "")*/mobileNo));
telecomdata.add(new BasicNameValuePair("requestDate", Utilities.getApiCallTimeFormat()));
telecomdata.add(new BasicNameValuePair("reqFrom", "APP"));
Log.v("PostParameters", "telecomdata" + telecomdata);
if (Connectivity.checkNetwork(ctx)) {
result = rechargeUrl.queryRESTurlONline(ctx, "/GetDTHOperatorsService", "post", telecomdata, GenericConstants.PiPay_root);
} else {
result = GenericConstants.NETWORKNOTFOUND;
displayToast("The Internet Connection appears to be offline");
}
return result;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
MyProgress.show(ctx, "", "");
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
try {
if (result != null) {
if (result.equalsIgnoreCase(GenericConstants.NETWORKNOTFOUND)) {
MyProgress.CancelDialog();
Toast.makeText(ctx,"The Internet Connection appears to be offline",Toast.LENGTH_SHORT).show();
return;
}
String[] resArr = result.split("delimiter_");
if (!resArr[0].equals("500")) {
MyProgress.CancelDialog();
Log.d("DTHOperator Response ::", result);
JSONArray jsonArray = null;
JSONObject object = new JSONObject(result);
jsonArray = object.getJSONArray("data");
for (int i = 0; i < jsonArray.length(); i++) {
if (jsonArray.getJSONObject(i).has("opName")) {
operator.add(jsonArray.getJSONObject(i).getString("opName"));
Log.d("", " Question size " + operator.size());
}
}
if(MyProgress.isShowingProgress())
MyProgress.CancelDialog();
editAmt.setText("2000"); // NPE HERE Also setting the value
} else {
MyProgress.CancelDialog();
displayToast("Failure");
}
}
} catch (Exception e) {
e.printStackTrace();
MyProgress.CancelDialog();
}finally {
if(MyProgress.isShowingProgress())
MyProgress.CancelDialog();
}
}
您可以保留在 ViewPager
中使用的 Fragments
实例。这是一个例子:
在您的 ViewPagerAdapter
中定义一个 SparseArray
并覆盖如下方法:
SparseArray<Fragment> registeredFragments = new SparseArray<>();
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
并在 ViewPager
中添加一个 PageChangeListener
:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// Here's your fragment instance
// You can access it's methods, variables, views etc.
YourFragment fragment =(YourFragment)yourPagerAdapter.getRegisteredFragment(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
希望对您有所帮助。祝你好运!
编辑:
您可以定义片段的视图 public 或为视图定义 getter 方法并从片段实例进行访问,例如:
public TextView mTextView;
或
private TextView mTextVieW;
public TextView getTextView(){
return this.mTextView;
}
您可以像下面这样访问它:
TextView textView = yourFragmentInstance.mTetxView;
或
TextView textview = yourFragmentInstance.getTextView();
使用以下覆盖方法
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && isResumed()) {
}
}
这将使您可以选择在用户当前查看片段时调用任何内容。
- 向系统设置有关此片段的 UI 当前是否对用户可见 * 的提示。此提示默认为 true 并且在片段实例*状态保存和恢复中持久存在。
应用程序可以将此设置为 false 以指示片段的 UI 是
- 滚出可见性或用户无法直接看到。
- 系统可能会使用它来确定片段生命周期更新等操作的优先级
- 或加载程序排序行为。 *
- @param isVisibleToUser 如果此片段的 UI 当前对用户可见(默认),则为真,
- 如果不是则为假。
这在我的上一个项目中对我有用。我也喜欢这种方法,因为我不需要做任何奇怪的事情。
基本上,在我的 viewpager 适配器中,我添加了一个方法来跟踪片段标签和相关位置。
public class TabAdapter extends FragmentPagerAdapter {
private Map<Integer, String> tagMap = new HashMap<>(2); // matches tab count
public TabAdapter(FragmentManager fMgr) {
super(fMgr);
}
// !!!!!!!!!!!!!!!!!!!!!!!!!!!
// Capturing the tag that is auto-generated
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment frag = (Fragment) super.instantiateItem(container, position);
tagMap.put(position, frag.getTag());
return frag;
}
/**
* Return the Fragment associated with a specified position.
*
* @param position
*/
@Override
public Fragment getItem(int position) {
Fragment frag = null;
...
return frag;
}
/**
* Return the number of views available.
*/
@Override
public int getCount() {
return 2;
}
@Override
public CharSequence getPageTitle(int position) {
String title = null;
...
return title;
}
// !!!!!!!!!!!!!!!!!!!!!!!!!!!
public String getFragmentTag(int position) {
return tagMap.get(position);
}
} // end of class TabAdapter
然后在我的 activity(事件方法)中访问:
String fragTag = tabAdapter.getFragmentTag(tabPosition);
Fragment frag = getSupportFragmentManager().findFragmentByTag(fragTag);
当然这意味着您必须在包含 activity.
中保留适配器的引用从这里开始,我的片段将具有简单的回调和显式方法来更新我需要更新的任何视图。