Android ViewPager 未显示正确的片段
Android ViewPager Not showing the right fragment
我目前正在尝试实现一个简单的 ViewPager,它可以切换以各种方式(而不是通过滑动)触发的片段。我的应用程序在尝试从第二个片段转到第三个片段时崩溃,因为它实际上跳过 GroupActivityFragment
并出于某种奇怪的原因尝试转到 GroupFrequencyFragment
。我尝试了很多不同的策略,但如果不想使用滑动遍历,这个 ViewPager 系统似乎太复杂和错误了。
这是 activity 持有 ViewPager
public class CreateGroupActivity extends FragmentActivity {
public static ViewPager viewPager;
private CreateGroupPagerAdapter pagerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_group);
viewPager = findViewById(R.id.create_group_view_pager);
pagerAdapter = new CreateGroupPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pagerAdapter);
//Prevents swiping to next step.
viewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
}
@Override
public void onBackPressed() {
if (viewPager.getCurrentItem() == 0) {
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
} else {
// Otherwise, select the previous step.
viewPager.setCurrentItem(viewPager.getCurrentItem() - 1);
}
}
}
这是用于创建不同片段的自定义寻呼机适配器
public class CreateGroupPagerAdapter extends FragmentStatePagerAdapter {
private static final String TAG = "CreateGroupPagerAdapter";
private static final int NUM_PAGES = 6;
public CreateGroupPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
Log.i(TAG, "getItem: pos = " + position);
Fragment fragment = null;
switch (position) {
case 0:
fragment = new GroupFormatFragment();
break;
case 1:
fragment = new GroupStyleFragment();
break;
case 2:
fragment = new GroupActivityFragment();
break;
case 3:
fragment = new GroupFrequencyFragment();
break;
case 4:
fragment = new GroupSettingsFragment();
break;
case 5:
fragment = new GroupInviteFriendsFragment();
break;
}
// position = position + 1;
return fragment;
}
// this counts total number of tabs
@Override
public int getCount() {
return NUM_PAGES;
}
}
此外,所有片段都继承自一个抽象 class,它具有用于告诉 viewpager 切换片段的方法。
public abstract class GroupCreationFragment extends Fragment {
public ArrayList<String> itemTitles;
public ArrayList<String> itemDescriptions;
private static final String TAG = "GroupCreationFragment";
public GroupCreationFragment() {
// Required empty public constructor
}
@Override
public abstract View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
/**
* Sets the title of the fragment and then the filling content views.
*
* @param view
*/
public abstract void setFragmentViews(View view);
/**
* Tell the parent activity's ViewPager to set the current item.
*
*/
public void notifyPageChange() {
CreateGroupActivity.viewPager.setCurrentItem(CreateGroupActivity.viewPager.getCurrentItem()+1,true);
}
}
这是被跳过的 GroupActivityFragment
class。
public class GroupActivityFragment extends GroupCreationFragment {
public GroupActivityFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_group_activity, container, false);
itemTitles = new ArrayList<>();
itemDescriptions = new ArrayList<>();
setFragmentViews(view);
return view;
}
@Override
public void setFragmentViews(View view){
itemTitles.add(getResources().getString(R.string.exercise_regularly));
itemDescriptions.add(getResources().getString(R.string.exercise_regularly_desc));
initRecyclerView(view);
}
private void initRecyclerView(View view) {
RecyclerView recyclerView = view.findViewById(R.id.group_activity_recycler_view);
GroupCreationRecyclerViewAdapter adapter = new GroupCreationRecyclerViewAdapter(getContext(), this, itemTitles, itemDescriptions, 2);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
}
}
还有 GroupCreationRecyclerViewAdapter
class 处理前 3 个片段中的片段变化。
public class GroupCreationRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final String TAG = "GCRecyclerViewAdapter";
private GroupCreationFragment parentFragment;
private int pagePosition;
public ArrayList<String> itemTitles = new ArrayList<>();
public ArrayList<String> itemDescriptions = new ArrayList<>();
private Context context;
public GroupCreationRecyclerViewAdapter(Context context, GroupCreationFragment parentFragment, ArrayList<String> itemTitles, ArrayList<String> itemDescriptions, int pagePosition) {
this.itemTitles = itemTitles;
this.itemDescriptions = itemDescriptions;
this.context = context;
this.parentFragment = parentFragment;
this.pagePosition = pagePosition;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_group_creation_item, viewGroup, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
((ViewHolder) viewHolder).itemTitle.setText(itemTitles.get(i));
((ViewHolder) viewHolder).itemDescription.setText(itemDescriptions.get(i));
final int pos = i;
((ViewHolder) viewHolder).parentLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Handle option choice.
handleItemOnClick(itemTitles.get(pos));
}
});
}
@Override
public int getItemCount() {
return itemTitles.size();
}
public void handleItemOnClick(String title) {
switch (title) {
case "Private Group":
GroupBuilder.getInstance().setFormat("private");
break;
case "Public Group":
break;
case "Accountability":
GroupBuilder.getInstance().setStyle("accountability");
break;
case "Competition":
break;
case "Exercise Regularly":
GroupBuilder.getInstance().setActivityType("exercise regularly");
break;
}
Log.i("GroupCreationRV", "handleItemOnClick: " + GroupBuilder.getInstance().toString());
Log.i(TAG, "notifyPageChange: getPagePosition = " + getPagePosition());
// Tell ViewPager to move to next page.
parentFragment.notifyPageChange();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView itemTitle;
public TextView itemDescription;
public ConstraintLayout parentLayout;
public ViewHolder(@NonNull View itemView) {
super(itemView);
itemTitle = itemView.findViewById(R.id.item_main_title);
itemDescription = itemView.findViewById(R.id.item_description);
parentLayout = itemView.findViewById(R.id.group_creation_item_layout);
}
}
public int getPagePosition() {
return pagePosition;
}
}
ViewPager
的默认配置包括对急切创建 "off-screen" 页面的优化。这个想法是,将 currently-visible 页面的一个页面膨胀到 "either side" 将有助于用户在页面之间滑动时的平滑过渡。
https://developer.android.com/reference/android/support/v4/view/ViewPager#setoffscreenpagelimit
Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. [...] This setting defaults to 1.
我从你的代码中看到 GroupActivityFragment
位于索引 2,GroupFrequencyFragment
位于索引 3。因此,当你导航到索引 2 时,GroupActivityFragment
和 GroupFrequencyFragment
将同时创建。
很遗憾,您不能将此数字设置为小于 1。来自 the source code:
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
这意味着您必须将 GroupFrequencyFragment
"far enough" 从 GroupActivityFragment
移开,这样它就不会同时创建,或者 re-work 您的实施,以便同时创建不是问题。
或者,您可以完全停止使用 ViewPager
。如果您不希望用户能够在页面之间滑动,为什么不根据需要简单地使用 FragmentTransaction
和 replace()
您的片段?
由于 ViewPager
在正在查看的片段之前创建了一个片段,因此 GroupFrequencyFragment
与其前面的片段同时创建。这提出了一个问题,因为需要填充变量才能在该片段上正确展示它。所以我实现了一种方法来延迟特定视图的完全初始化,直到片段被用户查看。
public class GroupFrequencyFragment extends GroupCreationFragment {
private static final String TAG = "GroupFrequencyFragment";
private static TextView activityLabel;
public GroupFrequencyFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_group_frequency, container, false);
setFragmentViews(view);
return view;
}
@Override
public void setFragmentViews(View view) {
initFrequencyViews(view);
setActivityLabel();
}
public void setActivityLabel() {
if (GroupBuilder.getInstance().getActivityType() != null) {
String type = GroupBuilder.getInstance().getActivityType();
switch (type) {
case "exercise regularly":
// Set the right label
Log.i(TAG, "setActivityLabel: activityLabel = " + activityLabel);
activityLabel.setText("workouts every");
break;
default:
break;
}
}
}
private void initFrequencyViews(View view) {
activityLabel = view.findViewById(R.id.activity_label);
...
}
这里是ViewPager等待fragment被查看的onPageListener。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_group);
viewPager = findViewById(R.id.create_group_view_pager);
pagerAdapter = new CreateGroupPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pagerAdapter);
//Prevents swiping to next step.
viewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
if (i == 3) {
CreateGroupPagerAdapter adapter = (CreateGroupPagerAdapter) viewPager.getAdapter();
GroupFrequencyFragment frequencyFragment = (GroupFrequencyFragment) adapter.getItem(3);
frequencyFragment.setActivityLabel();
}
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
}
@Override
public void onBackPressed() {
if (viewPager.getCurrentItem() == 0) {
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
} else {
// Otherwise, select the previous step.
viewPager.setCurrentItem(viewPager.getCurrentItem() - 1);
}
}
我目前正在尝试实现一个简单的 ViewPager,它可以切换以各种方式(而不是通过滑动)触发的片段。我的应用程序在尝试从第二个片段转到第三个片段时崩溃,因为它实际上跳过 GroupActivityFragment
并出于某种奇怪的原因尝试转到 GroupFrequencyFragment
。我尝试了很多不同的策略,但如果不想使用滑动遍历,这个 ViewPager 系统似乎太复杂和错误了。
这是 activity 持有 ViewPager
public class CreateGroupActivity extends FragmentActivity {
public static ViewPager viewPager;
private CreateGroupPagerAdapter pagerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_group);
viewPager = findViewById(R.id.create_group_view_pager);
pagerAdapter = new CreateGroupPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pagerAdapter);
//Prevents swiping to next step.
viewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
}
@Override
public void onBackPressed() {
if (viewPager.getCurrentItem() == 0) {
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
} else {
// Otherwise, select the previous step.
viewPager.setCurrentItem(viewPager.getCurrentItem() - 1);
}
}
}
这是用于创建不同片段的自定义寻呼机适配器
public class CreateGroupPagerAdapter extends FragmentStatePagerAdapter {
private static final String TAG = "CreateGroupPagerAdapter";
private static final int NUM_PAGES = 6;
public CreateGroupPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
Log.i(TAG, "getItem: pos = " + position);
Fragment fragment = null;
switch (position) {
case 0:
fragment = new GroupFormatFragment();
break;
case 1:
fragment = new GroupStyleFragment();
break;
case 2:
fragment = new GroupActivityFragment();
break;
case 3:
fragment = new GroupFrequencyFragment();
break;
case 4:
fragment = new GroupSettingsFragment();
break;
case 5:
fragment = new GroupInviteFriendsFragment();
break;
}
// position = position + 1;
return fragment;
}
// this counts total number of tabs
@Override
public int getCount() {
return NUM_PAGES;
}
}
此外,所有片段都继承自一个抽象 class,它具有用于告诉 viewpager 切换片段的方法。
public abstract class GroupCreationFragment extends Fragment {
public ArrayList<String> itemTitles;
public ArrayList<String> itemDescriptions;
private static final String TAG = "GroupCreationFragment";
public GroupCreationFragment() {
// Required empty public constructor
}
@Override
public abstract View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
/**
* Sets the title of the fragment and then the filling content views.
*
* @param view
*/
public abstract void setFragmentViews(View view);
/**
* Tell the parent activity's ViewPager to set the current item.
*
*/
public void notifyPageChange() {
CreateGroupActivity.viewPager.setCurrentItem(CreateGroupActivity.viewPager.getCurrentItem()+1,true);
}
}
这是被跳过的 GroupActivityFragment
class。
public class GroupActivityFragment extends GroupCreationFragment {
public GroupActivityFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_group_activity, container, false);
itemTitles = new ArrayList<>();
itemDescriptions = new ArrayList<>();
setFragmentViews(view);
return view;
}
@Override
public void setFragmentViews(View view){
itemTitles.add(getResources().getString(R.string.exercise_regularly));
itemDescriptions.add(getResources().getString(R.string.exercise_regularly_desc));
initRecyclerView(view);
}
private void initRecyclerView(View view) {
RecyclerView recyclerView = view.findViewById(R.id.group_activity_recycler_view);
GroupCreationRecyclerViewAdapter adapter = new GroupCreationRecyclerViewAdapter(getContext(), this, itemTitles, itemDescriptions, 2);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
}
}
还有 GroupCreationRecyclerViewAdapter
class 处理前 3 个片段中的片段变化。
public class GroupCreationRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final String TAG = "GCRecyclerViewAdapter";
private GroupCreationFragment parentFragment;
private int pagePosition;
public ArrayList<String> itemTitles = new ArrayList<>();
public ArrayList<String> itemDescriptions = new ArrayList<>();
private Context context;
public GroupCreationRecyclerViewAdapter(Context context, GroupCreationFragment parentFragment, ArrayList<String> itemTitles, ArrayList<String> itemDescriptions, int pagePosition) {
this.itemTitles = itemTitles;
this.itemDescriptions = itemDescriptions;
this.context = context;
this.parentFragment = parentFragment;
this.pagePosition = pagePosition;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_group_creation_item, viewGroup, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
((ViewHolder) viewHolder).itemTitle.setText(itemTitles.get(i));
((ViewHolder) viewHolder).itemDescription.setText(itemDescriptions.get(i));
final int pos = i;
((ViewHolder) viewHolder).parentLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Handle option choice.
handleItemOnClick(itemTitles.get(pos));
}
});
}
@Override
public int getItemCount() {
return itemTitles.size();
}
public void handleItemOnClick(String title) {
switch (title) {
case "Private Group":
GroupBuilder.getInstance().setFormat("private");
break;
case "Public Group":
break;
case "Accountability":
GroupBuilder.getInstance().setStyle("accountability");
break;
case "Competition":
break;
case "Exercise Regularly":
GroupBuilder.getInstance().setActivityType("exercise regularly");
break;
}
Log.i("GroupCreationRV", "handleItemOnClick: " + GroupBuilder.getInstance().toString());
Log.i(TAG, "notifyPageChange: getPagePosition = " + getPagePosition());
// Tell ViewPager to move to next page.
parentFragment.notifyPageChange();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView itemTitle;
public TextView itemDescription;
public ConstraintLayout parentLayout;
public ViewHolder(@NonNull View itemView) {
super(itemView);
itemTitle = itemView.findViewById(R.id.item_main_title);
itemDescription = itemView.findViewById(R.id.item_description);
parentLayout = itemView.findViewById(R.id.group_creation_item_layout);
}
}
public int getPagePosition() {
return pagePosition;
}
}
ViewPager
的默认配置包括对急切创建 "off-screen" 页面的优化。这个想法是,将 currently-visible 页面的一个页面膨胀到 "either side" 将有助于用户在页面之间滑动时的平滑过渡。
https://developer.android.com/reference/android/support/v4/view/ViewPager#setoffscreenpagelimit
Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. [...] This setting defaults to 1.
我从你的代码中看到 GroupActivityFragment
位于索引 2,GroupFrequencyFragment
位于索引 3。因此,当你导航到索引 2 时,GroupActivityFragment
和 GroupFrequencyFragment
将同时创建。
很遗憾,您不能将此数字设置为小于 1。来自 the source code:
if (limit < DEFAULT_OFFSCREEN_PAGES) { Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + DEFAULT_OFFSCREEN_PAGES); limit = DEFAULT_OFFSCREEN_PAGES; }
这意味着您必须将 GroupFrequencyFragment
"far enough" 从 GroupActivityFragment
移开,这样它就不会同时创建,或者 re-work 您的实施,以便同时创建不是问题。
或者,您可以完全停止使用 ViewPager
。如果您不希望用户能够在页面之间滑动,为什么不根据需要简单地使用 FragmentTransaction
和 replace()
您的片段?
由于 ViewPager
在正在查看的片段之前创建了一个片段,因此 GroupFrequencyFragment
与其前面的片段同时创建。这提出了一个问题,因为需要填充变量才能在该片段上正确展示它。所以我实现了一种方法来延迟特定视图的完全初始化,直到片段被用户查看。
public class GroupFrequencyFragment extends GroupCreationFragment {
private static final String TAG = "GroupFrequencyFragment";
private static TextView activityLabel;
public GroupFrequencyFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_group_frequency, container, false);
setFragmentViews(view);
return view;
}
@Override
public void setFragmentViews(View view) {
initFrequencyViews(view);
setActivityLabel();
}
public void setActivityLabel() {
if (GroupBuilder.getInstance().getActivityType() != null) {
String type = GroupBuilder.getInstance().getActivityType();
switch (type) {
case "exercise regularly":
// Set the right label
Log.i(TAG, "setActivityLabel: activityLabel = " + activityLabel);
activityLabel.setText("workouts every");
break;
default:
break;
}
}
}
private void initFrequencyViews(View view) {
activityLabel = view.findViewById(R.id.activity_label);
...
}
这里是ViewPager等待fragment被查看的onPageListener。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_group);
viewPager = findViewById(R.id.create_group_view_pager);
pagerAdapter = new CreateGroupPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pagerAdapter);
//Prevents swiping to next step.
viewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
if (i == 3) {
CreateGroupPagerAdapter adapter = (CreateGroupPagerAdapter) viewPager.getAdapter();
GroupFrequencyFragment frequencyFragment = (GroupFrequencyFragment) adapter.getItem(3);
frequencyFragment.setActivityLabel();
}
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
}
@Override
public void onBackPressed() {
if (viewPager.getCurrentItem() == 0) {
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
} else {
// Otherwise, select the previous step.
viewPager.setCurrentItem(viewPager.getCurrentItem() - 1);
}
}