Android:替换视图寻呼机中的片段

时间:2014-06-25 11:43:45

标签: android android-fragments android-viewpager

我在名为SearchFragment的活动中的视图寻呼机中有两个片段CreateFragmentTicketManagementActivity。现在,当用户按下SearchFragment中的搜索按钮时,我希望SearchFragment替换为SearchResultFragment。然后,我应该可以在SeachResultFragment中的CreateFragmentViewPager之间滑动。此外,当我从SearchResultFragment按回来时,我应该回到SearchFragment

现在,当我按下按钮时,我得到一个空白屏幕,而不是SearchResultFragment的布局。当我按下时我会到SearchFragment,但现在我必须单击按钮两次才能显示空白屏幕。现在,在双击后出现空白屏幕后,每当我滑动到CreateFragment标签时,我都会得到一个空白屏幕,而不是CreateFragment布局。

我看了很多关于SO的问题,但似乎没有一个问题对我有用。最有用的似乎是this question中的前两个答案,但第一个答案并不处理背压,我也无法实现它。第二个答案似乎非常可行,但我得到的错误是我在下面提到的。

我的主要TicketManagemementActivity

public class TicketManagementActivity extends FragmentActivity implements
ActionBar.TabListener {

    ViewPager viewPager;
    TabsPagerAdapter adapter;
    ActionBar actionBar;

    String[] tabs={"Search", "Create"};

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

        viewPager=(ViewPager)findViewById(R.id.pager);
        actionBar=getActionBar();
        adapter=new TabsPagerAdapter(getSupportFragmentManager());

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

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

    }
  //removed methods for menu creation and filling and placeholder fragment for brevity on SO
    @Override
    public void onTabSelected(Tab tab, FragmentTransaction ft) {

        viewPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {


    }

    @Override
    public void onTabReselected(Tab tab, FragmentTransaction ft) {


    }

}

我的activity_ticket_management.xml是在票务管理活动的onCreate中设置的布局,只包含viewpager

<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</android.support.v4.view.ViewPager>

我的TabsPagerAdapter课程延长了FragmentPagerAdapter

public class TabsPagerAdapter extends FragmentPagerAdapter {

    public TabsPagerAdapter(android.support.v4.app.FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int index) {

        switch (index) {
        case 0:
            // Top Rated fragment activity
            return new SearchFragment();
        case 1:
            // Games fragment activity
            return new CreateFragment();

        }

        return null;
    }

    @Override
    public int getCount() {
        // get item count - equal to number of tabs
        return 2;
    }

}

SearchFragment的相关部分:

public class SearchFragment extends Fragment implements View.OnClickListener    {

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

        View rootView = inflater.inflate(R.layout.fragment_search, container, false);
        .
        .//some widget initializations
        .
        return rootView;
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.ticket_search_btn: searchSigmaTickets();
                                     break;
        }
    }

    public void searchSigmaTickets(){
        .
        .
        .
        .//some operations
        .

        new SearchAsyncTask().execute();

        }
    }

    private class SearchAsyncTask extends AsyncTask<Void, Void, Void>{

        protected Void doInBackground(Void... params){
            .
            .//some more operation
            .
        }
        protected void onPostExecute(Void param){
            Fragment newFragment = new SearchResultFragment();
            //Here I use getFragmentManager and not getChildFragmentManager
            FragmentTransaction transaction = getFragmentManager().beginTransaction();
            //HERE I try to replace the fragment. I'm not sure what id to pass, I pass the id of the main veiwpager in ticketmanagement activity
            transaction.replace(R.id.pager, newFragment);
            transaction.addToBackStack(null);
            transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);

            transaction.commit();
        }

    }

}

如果我使用getChildFragmentManager代替getFragmentManager,我会在第二个回答中提到

06-25 06:55:32.045: E/AndroidRuntime(2797): java.lang.IllegalArgumentException: No view found for id 0x7f06003c (com.amberroad.sigmaticket:id/pager) for fragment SearchResultFragment{b2fed358 #0 id=0x7f06003c}

对于这个冗长的问题,我该怎么解决?

1 个答案:

答案 0 :(得分:1)

Kartik,准备好对你的长篇问题进行冗长的回答。在viewpager中更换片段非常复杂,但是非常有可能并且看起来非常光滑。首先,您需要让viewpager本身处理删除和添加片段。发生的事情是当您替换SearchFragment内部的片段时,您的viewpager会保留其片段视图。所以你最终得到一个空白页面,因为当你尝试替换它时会删除SearchFragment。

解决方案是在viewpager中创建一个侦听器,该侦听器将处理在其外部进行的更改,因此首先将此代码添加到适配器的底部。

public interface nextFragmentListener {
    public void fragment0Changed(String newFragmentIdentification);

}

然后,您需要在viewpager中创建一个私有类,当您想要更改片段时,该私有类变为listener。例如,你可以添加这样的东西。请注意,它实现了刚刚创建的接口。因此,无论何时调用此方法,它都将运行下面类中的代码。

private final class fragmentChangeListener implements nextFragmentListener {


    @Override
    public void fragment0Changed(String fragment) {
        //I will explain the purpose of fragment0 in a moment
                    fragment0 = fragment;
                    manager.beginTransaction().remove(fragAt0).commit();

        switch (fragment){
            case "searchFragment":
                fragAt0 = SearchFragment.newInstance(listener);
                break;
            case "searchResultFragment":
                fragAt0 = Fragment_Table.newInstance(listener);
                break;
                    }            

        notifyDataSetChanged();
    }

这里有两点需要指出:1)fragAt0是一个“灵活”的片段。它可以采用您提供的任何片段类型。这使它成为你最好的朋友,将位置0的片段更改为你想要的片段。 2)注意放在'newInstance(listener)constructor. These are how you will call fragment0Changed(String newFragmentIdentification)`中的监听器。以下代码显示了如何在片段内创建侦听器。

static nextFragmentListener listenerSearch;

    public static Fragment_Journals newInstance(nextFragmentListener listener){
        listenerSearch = listener;
        return new Fragment_Journals();

    }

然后您可以调用onPostExecute

中的更改
private class SearchAsyncTask extends AsyncTask<Void, Void, Void>{

    protected Void doInBackground(Void... params){
        .
        .//some more operation
        .
    }
    protected void onPostExecute(Void param){

        listenerSearch.fragment0Changed("searchResultFragment");
    }

}

这会触发viewpager内部的代码,将片段切换到零fragAt0位置,成为新的searchResultFragment。在viewpager运行之前,还需要添加两个小块。

一个将在viewpager的getItem覆盖方法中。

@Override
public Fragment getItem(int index) {

    switch (index) {
    case 0:
        //this is where it will "remember" which fragment you have just selected. the key is to set a static String fragment at the top of your page that will hold the position that you had just selected.  

        if(fragAt0 == null){

            switch(fragment0){
            case "searchFragment":
                fragAt0 = FragmentSearch.newInstance(listener);
                break;
            case "searchResultsFragment":
                fragAt0 = FragmentSearchResults.newInstance(listener);
                break;
            }
        }
        return fragAt0;
    case 1:
        // Games fragment activity
        return new CreateFragment();

    }

现在没有这个最后一块,你仍然会得到一个空白页面。有点蹩脚,但它是viewPager的重要组成部分。您必须覆盖viewpager的getItemPosition方法。通常,此方法将返回POSITION_UNCHANGED,它告诉viewpager保持所有内容相同,因此永远不会调用getItem将新片段放在页面上。这是你可以做的事情的一个例子

public int getItemPosition(Object object)
    {
        //object is the current fragment displayed at position 0.  
        if(object instanceof SearchFragment && fragAt0 instanceof SearchResultFragment){
            return POSITION_NONE;
        //this condition is for when you press back
         }else if{(object instanceof SearchResultFragment && fragAt0 instanceof SearchFragment){
              return POSITION_NONE;
         }
            return POSITION_UNCHANGED

}

就像我说的,代码非常复杂,但你基本上必须为你的情况创建一个自定义适配器。我提到的东西可以改变片段。浸泡所有东西可能需要很长时间,所以我会耐心等待,但这一切都有意义。完全值得花时间,因为它可以制作一个非常漂亮的应用程序。

这是处理后退按钮的金块。你把它放在你的MainActivity中

 public void onBackPressed() {
    if(mViewPager.getCurrentItem() == 0) {
        if(pagerAdapter.getItem(0) instanceof FragmentSearchResults){
            ((FragmentSearchResults) pagerAdapter.getItem(0)).backPressed();
        }else if (pagerAdapter.getItem(0) instanceof FragmentSearch) {
            finish();
        }
    }



}

您需要在FragmentSearchResults内部创建一个名为backPressed()的方法,该方法调用fragment0changed。这与我之前展示的代码一起将按下后退按钮。祝你的代码更改viewpager好运。它需要做很多工作,据我所知,没有任何快速的改编。就像我说的,你基本上是在创建一个自定义viewpager适配器,让它使用监听器处理所有必要的更改

以下是TabsPagerAdapter的所有代码。

public class TabsPagerAdapter extends FragmentPagerAdapter{

Fragment fragAt0;
fragmentChangeListener listener = new fragmentChangeListener();
FragmentManager manager;

static String fragment0 = "SearchFragment";

//when you declare the viewpager in your adapter, pass it the fragment manager.
public viewPager(FragmentManager fm) {
    super(fm);
    manager = fm;

}

private final class fragmentChangeListener implements nextFragmentListener {


@Override
public void fragment0Changed(String fragment) {
    //I will explain the purpose of fragment0 in a moment
                fragment0 = fragment;
                manager.beginTransaction().remove(fragAt0).commit();

    switch (fragment){
        case "searchFragment":
            fragAt0 = SearchFragment.newInstance(listener);
            break;
        case "searchResultFragment":
            fragAt0 = Fragment_Table.newInstance(listener);
            break;
                }





    notifyDataSetChanged();
}
}

   @Override
public Fragment getItem(int index) {

switch (index) {
case 0:
    //this is where it will "remember" which fragment you have just selected. the key is to set a static String fragment at the top of your page that will hold the position that you had just selected.  

    if(fragAt0 == null){

        switch(fragment0){
        case "searchFragment":
            fragAt0 = FragmentSearch.newInstance(listener);
            break;
        case "searchResultsFragment":
            fragAt0 = FragmentSearchResults.newInstance(listener);
            break;
        }
    }
    return fragAt0;
case 1:
    // Games fragment activity
    return new CreateFragment();

}

@Override
public int getCount() {
    // Show 3 total pages.
    return 3;
}

@Override
public CharSequence getPageTitle(int position) {
    Locale l = Locale.getDefault();
    String[] tab = {"Journals", "Charts", "Website"};
    switch (position) {
        case 0:
            return tab[0].toUpperCase(l);
        case 1:
            return tab[1].toUpperCase(l);
        case 2:
            return tab[2].toUpperCase(l);
    }
    return null;
}

public int getItemPosition(Object object)
{
    //object is the current fragment displayed at position 0.  
    if(object instanceof SearchFragment && fragAt0 instanceof SearchResultFragment){
        return POSITION_NONE;
    //this condition is for when you press back
     }else if{(object instanceof SearchResultFragment && fragAt0 instanceof SearchFragment){
          return POSITION_NONE;
     }
        return POSITION_UNCHANGED

}

public interface nextFragmentListener {
    public void fragment0Changed(String fragment);

}