如何添加带有片段的新选项卡

How do I add a new tab with fragments

所以我想创建 3 个选项卡,每个选项卡都各不相同。 xml 我已经涵盖了。但不是如何在不使我的应用程序崩溃的情况下创建选项卡。

我已经实现了带有viewpage的滑动标签。

http://developer.android.com/training/implementing-navigation/lateral.html http://www.androidhive.info/2013/10/android-tab-layout-with-swipeable-views-1/

和一部分 http://www.java2s.com/Code/Android/UI/Demonstrateshowfragmentscanparticipateintheoptionsmenu.htm

当我尝试转到下一个选项卡时(滑动确实没问题)我收到错误消息:

    07-08 22:07:57.414: E/AndroidRuntime(6865): FATAL EXCEPTION: main
07-08 22:07:57.414: E/AndroidRuntime(6865): Process: com.cyberdog.magiceasydraft, PID: 6865
07-08 22:07:57.414: E/AndroidRuntime(6865): java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.view.ViewPager.setCurrentItem(int)' on a null object reference
07-08 22:07:57.414: E/AndroidRuntime(6865):     at com.cyberdog.magiceasydraft.addPlayersfragmentTab.doPositiveClick(addPlayersfragmentTab.java:113)
07-08 22:07:57.414: E/AndroidRuntime(6865):     at com.cyberdog.magiceasydraft.addPlayersfragmentTab$MyAlertDialogFragment.onClick(addPlayersfragmentTab.java:165)
07-08 22:07:57.414: E/AndroidRuntime(6865):     at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:160)
07-08 22:07:57.414: E/AndroidRuntime(6865):     at android.os.Handler.dispatchMessage(Handler.java:102)
07-08 22:07:57.414: E/AndroidRuntime(6865):     at android.os.Looper.loop(Looper.java:135)
07-08 22:07:57.414: E/AndroidRuntime(6865):     at android.app.ActivityThread.main(ActivityThread.java:5274)
07-08 22:07:57.414: E/AndroidRuntime(6865):     at java.lang.reflect.Method.invoke(Native Method)
07-08 22:07:57.414: E/AndroidRuntime(6865):     at java.lang.reflect.Method.invoke(Method.java:372)
07-08 22:07:57.414: E/AndroidRuntime(6865):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:909)
07-08 22:07:57.414: E/AndroidRuntime(6865):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:704)

所以..我做错了什么?我可以看到对我创建的圆形片段有一个空引用......但我不知道如何解决这个问题。这是我第一次使用片段和选项卡。

代码: 主要 activity:

    public class CreateDraft extends FragmentActivity implements
        ActionBar.TabListener {

    private ViewPager viewPager;
    private fragmentPageAdapter mAdapter;
    private ActionBar actionBar;
    private Fragment addPlayer, createRound,score;
    private String[] tabs = { "Add players", "Create round", "Score" };
    public static final String CuSToM_FRAGMENT_KEY ="ADD_PLAYER_TAB";



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_create_draft);



        viewPager = (ViewPager) findViewById(R.id.activity_viewpager_main);
        actionBar = getActionBar();
        mAdapter = new fragmentPageAdapter(getSupportFragmentManager());

        viewPager.setAdapter(mAdapter);
        actionBar.setHomeButtonEnabled(false);
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        if(savedInstanceState == null){
            FragmentManager fm = getSupportFragmentManager();
            android.support.v4.app.FragmentTransaction ft = fm.beginTransaction();

            addPlayer = fm.findFragmentByTag("Add Players");
            if(addPlayer == null){
                addPlayer = new addPlayersfragmentTab();
                ft.add(addPlayer, "Add players");
            }

            createRound = fm.findFragmentByTag("Create round");
            if(createRound == null){
                createRound = new createRoundfragmentTab();
                ft.add(createRound, "Create round");
            }


            score = fm.findFragmentByTag("Score");
            if(score == null){
                score = new scoreResultfragmentTab();
                ft.add(score, "Score");
            }

            ft.commit();
        }

//      
        for (String tab_name : tabs) {
            actionBar.addTab(actionBar.newTab().setText(tab_name)
                    .setTabListener(this));
        }



        viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

            @Override
            public void onPageSelected(int position) {
                // on changing the page
                // make respected tab selected
                actionBar.setSelectedNavigationItem(position);
            }

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {
            }

            @Override
            public void onPageScrollStateChanged(int arg0) {
            }
        });

    }

    public void passBundleToFragment(int position){
        if(true){

        }

    }


    @Override
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
        View focus = getCurrentFocus();
        if (focus != null) {
            hiddenKeyboard(focus);
        }

    }

    @Override
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        // on tab selected
        // show respected fragment view
        viewPager.setCurrentItem(tab.getPosition());
        View focus = getCurrentFocus();
        if (focus != null) {
            hiddenKeyboard(focus);
        }

    }

    @Override
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        View focus = getCurrentFocus();
        if (focus != null) {
            hiddenKeyboard(focus);
        }

    }

private void hiddenKeyboard(View v) {
    InputMethodManager keyboard = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
    keyboard.hideSoftInputFromWindow(v.getWindowToken(), 0);
}


}

其中一个片段(其他的还没有包含太多代码):

public class addPlayersfragmentTab extends Fragment {

    private Button addPlayer, createDraft;
    private EditText evAddPlayer;
    private ViewPager viewPager;
    private List<Player> players;
    private ListView lvAddedPlayers;
    ListAdapter adapter;
    private boolean buttonCreateDraftPressed= false;

    public boolean isButtonCreateDraftPressed() {
        return buttonCreateDraftPressed;
    }

    public void setButtonCreateDraftPressed(boolean buttonCreateDraftPressed) {
        this.buttonCreateDraftPressed = buttonCreateDraftPressed;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.draft_adding_players,
                container, false);
        initialiseEditAndText(rootView);
        players = new ArrayList<Player>();

        lvAddedPlayers = (ListView) rootView.findViewById(
                R.id.lv_players_adding);
        adapter = new ListAdapter(rootView.getContext(), players);
        lvAddedPlayers.setAdapter(adapter);



        addPlayer = (Button) rootView.findViewById(R.id.button_total_player);
        createDraft = (Button) rootView.findViewById(R.id.button_create_draft);

        addPlayer.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {

                Player player = new Player(evAddPlayer.getText().toString());
                players.add(player);
                adapter.notifyDataSetChanged();
                InputMethodManager imm = (InputMethodManager) getActivity()
                        .getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(getView().getWindowToken(), 0);




                }
        });

        createDraft.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                if (adapter != null && players != null) {
                        createdraft(players.size());

                } else {
                    Toast.makeText(getActivity(),
                            "Add minimal 3 players to start the draft.",
                            Toast.LENGTH_LONG).show();
                }


            }
        });

        return rootView;

    }

    public void showDialog(){
        DialogFragment newFragment = new MyAlertDialogFragment().newInstance(R.string.recreate_draft);
        newFragment.show(getFragmentManager(), "dialog");
    }

    public void doPositiveClick(){
        viewPager.setCurrentItem(1);
    }

    public void doNegativeClick(){

    }


    public int getTotalPlayers() {
        return players.size();
    }

    public List<Player> getPlayersNames() {
        return players;
    }

    public void initialiseEditAndText(View rootView) {
        evAddPlayer = (EditText) rootView.findViewById(R.id.et_player_name);
    }

    public void createdraft(int totalplayers) {

        if (totalplayers >= 3 && totalplayers <= 16) {
            showDialog();

        } else {
            Toast.makeText(getActivity(),
                    "Minimum of 3 and maximum of 16 players.",
                    Toast.LENGTH_LONG).show();
        }

    }

    public class MyAlertDialogFragment extends DialogFragment{
        public MyAlertDialogFragment newInstance(int title){
            MyAlertDialogFragment frag = new MyAlertDialogFragment();
            Bundle args = new Bundle();
            args.putInt("title", title);
            frag.setArguments(args);
            return frag;
        }

         @Override
            public Dialog onCreateDialog(Bundle savedInstanceState) {
                int title = getArguments().getInt("title");

                return new AlertDialog.Builder(getActivity())
                        .setIcon(R.drawable.abc_ic_search)
                        .setTitle(title)
                        .setPositiveButton("R.string.alert_dialog_ok",
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int whichButton) {
                                     addPlayersfragmentTab.this.doPositiveClick();
                                }
                            }
                        )
                        .setNegativeButton("R.string.alert_dialog_cancel",
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int whichButton) {
                                       addPlayersfragmentTab.this.doNegativeClick();
                                }
                            }
                        )
                        .create();
            }
        }

}

编辑:

页面适配器:

public fragmentPageAdapter(FragmentManager fm) {
        super(fm);
        // TODO Auto-generated constructor stub
    }

    @Override
    public Fragment getItem(int index) {
        switch (index) {
        case 0:
            // Top Rated fragment activity
            return new addPlayersfragmentTab();
        case 1:
            // Games fragment activity
            return new createRoundfragmentTab();
        case 2:
            // Movies fragment activity
            return new scoreResultfragmentTab();
        }
        return null;
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return 3;
    }

}

编辑 2:

我跟随数据传递到 activity,这确实有效,但是如果我想从 activity 传递数据(从 addPlayerTab 获取数据)我得到又是一个空引用,这次我认为是因为 id 是错误的。但我的布局文件不使用 .那我怎么找到片段呢?

代码: addPlayerTab的接口实现:

    @Override
public void onButtonClickCreateRound(int position, List<Player> players) {
    viewPager.setCurrentItem(position);
    this.players = players;
    GetPlayer();

}

addPlayerTab 中的代码:

public interface GoToCreateRoundListener{
        public void onButtonClickCreateRound(int position, List<Player> players);
    }

    public void onAttach(Activity activity){
        super.onAttach(activity);

        try{
            goCreateRound = (GoToCreateRoundListener)activity;
        }catch(ClassCastException e){
            throw new ClassCastException(activity.toString()+" must implement GoToRoundListener");
        }
    }

    public void createdraft(int totalplayers) {

        if (totalplayers >= 3 && totalplayers <= 16) {
            goCreateRound.onButtonClickCreateRound(1, players);

        } else {
            Toast.makeText(getActivity(),
                    "Minimum of 3 and maximum of 16 players.",
                    Toast.LENGTH_LONG).show();
        }

    }

这是将数据传递给createroundtab的接口实现:

@Override
public void GetPlayer() {
    createRoundfragmentTab createRoundfragmentTab = (createRoundfragmentTab)getSupportFragmentManager().findFragmentById(R.layout.create_round);

    createRoundfragmentTab.makeRandomFirstRound(players);
}

createroundTab 中的代码:

public interface GetPlayersFromAddPlayersListener{
        public void GetPlayer();
    }

    public void onAttach(Activity activity){
        super.onAttach(activity);

        try{
            getPlayers = (GetPlayersFromAddPlayersListener)activity;
        }catch(ClassCastException e){
            throw new ClassCastException(activity.toString()+" must implement GetPlayersFromAddPlayersListener");
        }
    }

您没有初始化片段的 viewPager 字段 (addPlayersfragmentTab.viewPager)。这就是你得到 NullPointerException 的原因。

您可以在 activity 中执行 getViewPager() 方法,然后从片段中调用它,即 ((CreateDraft) getActivity()).getViewPager().setCurrentItem(1)

我已经在上面分享了关于 Fragment 实例化的观点,所以我将再次引用我自己的话。

That whole if (savedInstance == null) block is not required because Fragment instantiation is the job of your PagerAdapter. The ViewPager will call PagerAdapter#getItem() when it sees fit to.

虽然,很明显 NullPointerException 是由 addPlayersfragmentTab 中未初始化的 ViewPager 引用引起的,在创建时将视图寻呼机传递给片段的整个方法或者通过 getter 访问它的片段是错误的。

原因是片段是您的 UI 一部分的独立可重用实现,因此它不应与托管它的 activity 耦合。一旦你将它与你的视图寻呼机结合起来,你就再也不能在任何其他正常的 activity 中托管这个片段了。简而言之,该片段不再保持可重用状态。

那么,片段应该如何与其宿主通信 activity?好吧,使用 回调接口 。这是来自 Android 开发者官方网站的简短教程:Communicating with Other Fragments 但它同样适用于请求 activity 代表片段执行任何操作。

在您的情况下,您会通知主要 activity 用户在警报对话框中按下了“确定”,activity 将通过使用查看寻呼机移至下一个选项卡来对此做出响应。


通常当您想再次抓住 Fragment 时,您可以使用 findFragmentById() 或它的带有 findFragmentByTag() 的自定义标签。但是,整个 ViewPager 及其 PagerAdapter 不会向我们公开这些属性。

因此,要启用片段查找,您需要在 PagerAdapter 中保存对它们的引用。大致如下。

Map<String, Fragment> mFragments = new HashMap<>(3);

...

@Override
public Fragment getItem(int index) {
    ...
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    // super calls getItem() if fragment is not already instantiated
    Fragment fragment = (Fragment) super.instantiateItem(container, position);
    mFragments.put(fragment.getClass().getSimpleName(), fragment);
    return fragment;
}

public Fragment getFragment(String name) {
    return mFragments.get(name);
}

@Override
public int getCount() {
    return 3;
}

通过上述更改,您的 GetPlayer() 实施现在将变为

@Override
public void GetPlayer() {
    createRoundfragmentTab createRoundfragmentTab = (createRoundfragmentTab)
            ((fragmentPageAdapter) mAdapter).getFragment("createRoundfragmentTab");

    createRoundfragmentTab.makeRandomFirstRound(players);
}

要更详细地了解此方法,请阅读 ViewPager and fragments — what's the right way to store fragment's state?

顺便说一句,也请看看Oracle推荐的Java naming conventions。长话短说,您的 class 名称需要以大写字母开头,方法名称则相反。遵循这些约定将使您的代码更易于阅读,从而更易于理解。目前,您的所有 class 名称对经验丰富的 Java 开发人员都显示为变量名称。