简体   繁体   English

Android ViewPager没有显示正确的片段

[英]Android ViewPager Not showing the right fragment

I am currently trying to implement a simple ViewPager that switches through fragments which are triggered in various ways (not by swiping). 我目前正在尝试实现一个简单的ViewPager,它可以切换以各种方式(而不是通过滑动)触发的片段。 My app crashes when it tries to go from the 2nd fragment to the 3rd because it is actually skipping the GroupActivityFragment and tries to go to the GroupFrequencyFragment for some odd reason. 我的应用程序尝试从第二个片段转到第三个片段时崩溃,因为它实际上跳过了GroupActivityFragment并由于某种奇怪的原因而尝试转到GroupFrequencyFragment I've tried many different tactics but this ViewPager systems seems to be so convoluted and buggy if don't want to use the swiping traversal. 我尝试了许多不同的策略,但是如果不想使用滑动遍历,则此ViewPager系统似乎非常复杂且有问题。

Here is the activity holding the 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);
        }
    }
}

Here is the custom pager adapter used to create the different fragments 这是用于创建不同片段的自定义寻呼机适配器

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;
    }
}

Also all the Fragments inherit from an abstract class which has the method used to tell the viewpager to switch fragments. 同样,所有Fragments都从一个抽象类继承,该抽象类具有用于告诉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);
    }

}

Here is the GroupActivityFragment class which is being skipped. 这是被跳过的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()));
    }

}

And also the GroupCreationRecyclerViewAdapter class that handles the fragment change in the first 3 fragments. 还有用于处理前3个片段中的片段更改的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;
    }
}

Since the ViewPager creates a fragment ahead of the one being viewed, the GroupFrequencyFragment was being created at the same time as the one preceding it. 由于ViewPager在被查看的片段之前创建了一个片段,因此GroupFrequencyFragment正在GroupFrequencyFragment之前的片段同时创建。 This prosed a problem since a variable needed to be populated in order to showcase it properly on that fragment. 这产生了一个问题,因为需要填充变量才能在该片段上正确显示它。 So I implemented a way to delay the full initialization of the particular view until the fragment was being viewed by the user. 因此,我实现了一种方法,可以延迟特定视图的完全初始化,直到用户查看该片段为止。

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);
        ...
    }

Here is the onPageListener for the ViewPager to wait for the fragment to be viewed. 这是供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);
        }
    }

ViewPager 's default configuration includes an optimization to eagerly create "off-screen" pages. ViewPager的默认配置包括优化,以急于创建“屏幕外”页面。 The idea is that inflating one page to "either side" of the currently-visible page will help with smooth transitions as the user swipes between pages. 这个想法是,当用户在页面之间滑动时,将页面扩展到当前可见页面的“任一侧”将有助于平滑过渡。

https://developer.android.com/reference/android/support/v4/view/ViewPager#setoffscreenpagelimit 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. [...] 此设置默认为1。

I see from your code that GroupActivityFragment is at index 2 and GroupFrequencyFragment is at index 3. So when you navigate to index 2, both GroupActivityFragment and GroupFrequencyFragment will be created at the same time. 我从您的代码中看到GroupActivityFragment位于索引2, GroupFrequencyFragment位于索引3。因此,当您导航至索引2时, GroupActivityFragmentGroupFrequencyFragment将同时创建。

Unfortunately, you can't set this number to less than 1. From the source code : 不幸的是,您不能将此数字设置为小于1。从源代码中

 if (limit < DEFAULT_OFFSCREEN_PAGES) { Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + DEFAULT_OFFSCREEN_PAGES); limit = DEFAULT_OFFSCREEN_PAGES; } 

This means that you will have to either move GroupFrequencyFragment "far enough" away from GroupActivityFragment so that it isn't created at the same time, or re-work your implementation so that being created at the same time isn't a problem. 这意味着您必须将GroupFrequencyFragment移到远离GroupActivityFragment足够远的位置,以便不能同时创建它,或者必须对实现进行重新设计,以便同时创建这不是问题。

Alternatively, you could stop using ViewPager altogether. 或者,您可以完全停止使用ViewPager If you don't want the user to be able to swipe between pages, why not simply use a FragmentTransaction and replace() your fragments as necessary? 如果您不希望用户在页面之间滑动,为什么不简单地使用FragmentTransaction并根据需要replace()您的片段呢?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM