我目前正在尝试实现一个简单的ViewPager,它可以切换以各种方式(而不是通过刷卡)触发的片段。当我的应用尝试从第二个片段转到第三个片段时,我的应用程序崩溃了,因为它实际上跳过了GroupActivityFragment
,并出于某种奇怪的原因而尝试转到GroupFrequencyFragment
。我已经尝试了许多不同的策略,但是如果不想使用滑动遍历,则此ViewPager系统似乎是如此令人费解和麻烦。
这里是保存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;
}
}
所有Fragment都继承自一个抽象类,该抽象类具有用于告诉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
类,正在被跳过。
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
类。
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;
}
}
答案 0 :(得分:1)
由于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的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);
}
}
答案 1 :(得分:0)
ViewPager
的默认配置包括一项优化,以急于创建“屏幕外”页面。这个想法是,当用户在页面之间滑动时,将页面扩大到当前可见页面的“任一侧”将有助于平滑过渡。
https://developer.android.com/reference/android/support/v4/view/ViewPager#setoffscreenpagelimit
设置处于空闲状态的视图层次结构中应保留到当前页面任一侧的页面数。 [...] 此设置默认为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
移到距离GroupActivityFragment
足够远的位置,以使其不会同时创建,或者必须重新执行实现以使其被创建同时不是问题。
或者,您可以完全停止使用ViewPager
。如果您不希望用户在页面之间滑动,为什么不根据需要简单地使用FragmentTransaction
和replace()
片段?