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(). ; совершить.setAccessible (истина); совершить.вызов (вкладка); } поймать (RuntimeException e) { бросить e; } поймать (исключение 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