Android TabLayout с наслагване на фрагменти от android.support.v7.app.ActionBar и android.support.v4.app.Fragment

Аз съм съвсем нов в програмирането с Android, но ще направя всичко възможно, за да дам цялата необходима информация. Използвам новите android.support.v7.app.ActionBarActivity и android.support.v4.app.Fragment, за да покажа оформление на раздел за Android API от 8 до 17. Изправен съм пред проблем с правилното показване на моите два фрагмента в моята дейност, тъй като те се наслагват един върху друг, след като избера един от тях. И така, това е основният ми код на дейност:

package it.koopa.scank;

import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBar.Tab;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;

public class MainActivity extends ActionBarActivity {

    private final static String TAG = "MainActivity";

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

        //have to use getSupportActionBar from android.support.v7.app
        ActionBar actionBar = getSupportActionBar();

        //hello tab
        Tab tab = actionBar.newTab()
                .setText(R.string.tab_hello)
                .setTabListener(new TabListener<HelloFragment>(this, "hello", HelloFragment.class));
        actionBar.addTab(tab);

        //handle content tab
        tab = actionBar.newTab()
                .setText(R.string.tab_send)
                .setTabListener(new TabListener<HandleContentFragment>(this, "handle", HandleContentFragment.class));
        actionBar.addTab(tab);

        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.scank, menu);
        return true;
    }

    public static class TabListener<T extends Fragment> implements ActionBar.TabListener {
        private Fragment mFragment;
        private final ActionBarActivity mActivity;
        private final String mTag;
        private final Class<T> mClass;

        /**
         * Constructor used each time a new tab is created.
         * @param activity The host Activity, used to instantiate the fragment
         * @param tag The identifier tag for the fragment
         * @param clz The fragment's Class, used to instantiate the fragment
         */
        public TabListener(ActionBarActivity activity, String tag, Class<T> clz) {
            mActivity = activity;
            mTag = tag;
            mClass = clz;
        }

        @Override
        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            mFragment = mActivity.getSupportFragmentManager().findFragmentByTag(mTag);

            // Check if the fragment is already initialized
            if (mFragment == null) {
                // If not, instantiate and add it to the activity
                mFragment = Fragment.instantiate(mActivity, mClass.getName());
                ft.add(getCorrectActionBarId(), mFragment, mTag);
                Log.i(TAG, "FragID " + mFragment.getId() + ", FragTAG=" + mFragment.getTag() + " ADDED!!!");
            } else {
                // If it exists, simply attach it in order to show it
                ft.attach(mFragment);
                Log.i(TAG, "FragID " + mFragment.getId() + ", FragTAG=" + mFragment.getTag() + " attached.");
            }
        }

        @Override
        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
            if (mFragment != null) {
                // Detach the fragment, because another one is being attached
                ft.detach(mFragment);
                Log.i(TAG, "FragID " + mFragment.getId() + ", FragTAG=" + mFragment.getTag() + " detached.");
            }
        }

        @Override
        public void onTabReselected(Tab tab, FragmentTransaction ft) {
            // User selected the already selected tab. Usually do nothing.
        }
    }

    /**
     * Returns the correct id of the action bar
     * @return
     */
    public static int getCorrectActionBarId () {
        int androidVersion = Build.VERSION.SDK_INT;
        if (androidVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            return android.R.id.content;
        } else {
            return R.id.action_bar_activity_content;
        }
    }

}

И това са моите два фрагмента (и двата използват собствено xml оформление). Първият:

package it.koopa.scank;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class HelloFragment extends Fragment {
    private int index;

    public void onCreate(Bundle savedInstanceState) {        
        super.onCreate(savedInstanceState);
    }

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

        View v = inflater.inflate(R.layout.hello, container, false);
        Log.i("HelloFragment","I'm " + HelloFragment.class);

        return v;

    }
}

и второ:

package it.koopa.scank;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class HandleContentFragment extends Fragment {
    private int index;

    public void onCreate(Bundle savedInstanceState) {        
        super.onCreate(savedInstanceState);
    }

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

        View v = inflater.inflate(R.layout.handle_content, container, false);
        Log.i("HandleContentFragment","I'm " + HandleContentFragment.class);

        return v;

    }
}

и в резултат на това, ако избера втория раздел, изгледите се поставят един върху друг! Намерих подобен въпрос тук Раздели, използващи android.support.v7.app.ActionBar, но приетото решение изглежда не работи за мен. Всъщност в основната ми дейност можете да видите, че получавам идентификатора на съдържанието

    int androidVersion = Build.VERSION.SDK_INT;
    if (androidVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        return android.R.id.content;
    } else {
        return R.id.action_bar_activity_content;
    }

но съдържанието на фрагменти все още е насложено (не мога да качвам изображения поради моята почти нулева репутация). Къде греша?

Актуализация: Моята дейност използва android:theme="@style/Theme.AppCompat.Light". Тествам на Nexus i9250 с Android 4.2.1


comment
Опитах да заменя ft.detach(mFragment) с ft.remove(mFragment), но това не реши. Когато щракна върху втория раздел, той се наслагва първо, когато щракна обратно върху първия, той скрива първия и запазва втория и когато щракна отново върху втория, скрива и този! Мисля, че проблемът е как се справям с FragmentTransaction. :(   -  person koopa    schedule 13.08.2013
comment
Реших по съвсем различен начин и ми харесва още повече, защото сега мога да използвам и плъзгане. Следвам навигация с плъзгащи се раздели и я адаптирам малко към моите нужди сега работи перфектно! Това доведе до малък проблем със свързването между моите фрагменти и основната ми дейност, но поне работи гладко! Така че мога да потвърдя, че новите библиотеки могат да свършат работата за оформление на раздели с плъзгане от Android 2.2 до 4.3! хубаво   -  person koopa    schedule 18.08.2013
comment
Знаете ли, да поставите кода на вашето решение би било хубаво...   -  person Paolo Rovelli    schedule 17.10.2013
comment
@koopa Ако актуализирате appcompat-v7 до ревизия 19.0.0 вече не е необходимо да превключвате между android.R.id.content и R.id.action_bar_activity_content. Прочетете проблем 59077 за повече информация.   -  person JJD    schedule 12.11.2013


Отговори (3)


Това е известен бъг. Можете да намерите решение тук:

https://code.google.com/p/android/issues/detail?id=58602

person csname1910    schedule 15.09.2013
comment
+1 Добро заобиколно решение Долен ред поставете това след последния ред на вашия метод „onTabUnselected“: if (Build.VERSION.SDK_INT ›= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { try { Method commit = tab.getClass().getDeclaredMethod(commitActiveTransaction) ; commit.setAccessible(true); commit.invoke(tab); } catch (RuntimeException e) { throw e; } catch (Изключение e) { хвърля ново RuntimeException(e); }} - person Michael Edgar; 18.10.2013

Нека всеки фрагмент внедри ActionBar.TabListener. Добавете тези методи по-долу заедно с другия код във вашия фрагмент

@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
     mFragment = new MyFragment();
     ft.add(android.R.id.content, mFragment);
     ft.attach(mFragment);      
}

@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    ft.remove(mFragment);
}

@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
    // TODO Auto-generated method stub

}

Можете да премахнете слушателя на раздели от основната си дейност. Това се справя с всичко.

person BigT    schedule 12.08.2013
comment
Защо трябва да правя това? Не е ли същото (с много повече код)? Бихте ли обяснили? Благодаря - person koopa; 13.08.2013
comment
Вярвам, че транзакцията с фрагмент е грешката за вас по-горе. Този начин е по-чист начин да разберете в кой раздел се намирате и какво е добавила и премахнала транзакцията с фрагменти. - person BigT; 13.08.2013

Просто извикайте ft.show(mfragment) метод в onTabSelected и ft.hide(mfragment) метод в onTabUnselected.

Вижте по-долу

public void onTabReselected(Tab tab, FragmentTransaction ft) {
  // TODO Auto-generated method stub
}

public void onTabSelected(Tab tab, FragmentTransaction ft) {
  ft.show(mfragment);
}

public void onTabUnselected(Tab tab, FragmentTransaction ft) {
  ft.hide(mfragment);       
}
person RAJA    schedule 21.01.2014