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 时,GroupActivityFragmentGroupFrequencyFragment将同时创建。

很遗憾,您不能将此数字设置为小于 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。如果您不希望用户能够在页面之间滑动,为什么不根据需要简单地使用 FragmentTransactionreplace() 您的片段?

由于 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);
        }
    }