ViewPager 내부의 조각 교체
를 사용하여 Fragment를 사용하려고 ViewPager
합니다 FragmentPagerAdapter
. 내가 달성하고자하는 것은의 첫 페이지에있는 조각 ViewPager
을 다른 조각으로 바꾸는 것입니다.
호출기는 두 페이지로 구성됩니다. 첫 번째 FirstPagerFragment
는 SecondPagerFragment
입니다. 두 번째는 입니다. 첫 페이지의 버튼을 클릭하십시오. 를 FirstPagerFragment
NextFragment 로 바꾸고 싶습니다 .
아래 코드가 있습니다.
public class FragmentPagerActivity extends FragmentActivity {
static final int NUM_ITEMS = 2;
MyAdapter mAdapter;
ViewPager mPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_pager);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
}
/**
* Pager Adapter
*/
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return NUM_ITEMS;
}
@Override
public Fragment getItem(int position) {
if(position == 0) {
return FirstPageFragment.newInstance();
} else {
return SecondPageFragment.newInstance();
}
}
}
/**
* Second Page FRAGMENT
*/
public static class SecondPageFragment extends Fragment {
public static SecondPageFragment newInstance() {
SecondPageFragment f = new SecondPageFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
return inflater.inflate(R.layout.second, container, false);
}
}
/**
* FIRST PAGE FRAGMENT
*/
public static class FirstPageFragment extends Fragment {
Button button;
public static FirstPageFragment newInstance() {
FirstPageFragment f = new FirstPageFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
View root = inflater.inflate(R.layout.first, container, false);
button = (Button) root.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
trans.addToBackStack(null);
trans.commit();
}
});
return root;
}
/**
* Next Page FRAGMENT in the First Page
*/
public static class NextFragment extends Fragment {
public static NextFragment newInstance() {
NextFragment f = new NextFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
return inflater.inflate(R.layout.next, container, false);
}
}
}
... 그리고 여기 XML 파일
fragment_pager.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
</android.support.v4.view.ViewPager>
</LinearLayout>
first.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/first_fragment_root_id"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button android:id="@+id/button"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="to next"/>
</LinearLayout>
이제 문제는 ... 어떤 ID를 사용해야합니까?
trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
?
를 사용 R.id.first_fragment_root_id
하면 교체가 작동하지만 계층 뷰어는 아래와 같이 이상한 동작을 보여줍니다.
처음에는 상황이
교체 후 상황은
보시다시피 문제가 있음을 알 수 있듯이 조각을 교체 한 후 첫 번째 그림과 동일한 상태를 찾을 것으로 예상됩니다.
ViewPager
and의 소스 코드를 수정할 필요가없는 또 다른 솔루션이 있으며 작성자가 사용 FragmentStatePagerAdapter
하는 FragmentPagerAdapter
기본 클래스 와 함께 작동합니다 .
어떤 ID를 사용해야하는지 저자의 질문에 답하고 싶습니다. 컨테이너의 ID, 즉 뷰 호출기 자체의 ID입니다. 그러나 자신도 알겠지만 코드에서 해당 ID를 사용하면 아무 일도 일어나지 않습니다. 이유를 설명하겠습니다.
우선 ViewPager
, 페이지를 다시 채우 notifyDataSetChanged()
려면 어댑터의 기본 클래스에 있는 페이지를 호출 해야합니다.
둘째, 추상 방법을 ViewPager
사용하여 getItemPosition()
어떤 페이지를 삭제하고 유지해야하는지 확인합니다. 항상이 기능 반환의 기본 구현 POSITION_UNCHANGED
발생, ViewPager
현재의 모든 페이지를 유지, 따라서 새 페이지를 부착하지. 따라서 프래그먼트 교체 작업을 수행 getItemPosition()
하려면 어댑터에서 대체해야 POSITION_NONE
하며, 오래되고 숨겨져있는 프래그먼트를 인수로 호출하면 반환해야합니다 .
이것은 또한 어댑터는 항상 위치 0에 표시되어야하는 단편을 인식 할 필요가 있다는 것을 의미 FirstPageFragment
나 NextFragment
. 이를 수행하는 한 가지 방법은을 만들 때 리스너를 제공하는 FirstPageFragment
것으로 조각을 전환 할 때 호출됩니다. 나는 당신의 조각 어댑터 핸들을 수 있도록 모든 단편 스위치 및 호출하지만 이것이 좋은 일이라고 생각 ViewPager
하고 FragmentManager
.
셋째, FragmentPagerAdapter
사용 된 조각을 위치에서 파생 된 이름으로 캐시하므로 위치 0에 조각이 있으면 클래스가 새로운 경우에도 대체되지 않습니다. 두 가지 해결책이 있지만 가장 간단한 방법은의 remove()
기능 을 사용하여 FragmentTransaction
태그를 제거하는 것입니다.
그것은 많은 텍스트였습니다, 다음은 귀하의 경우에 작동 해야하는 코드입니다 :
public class MyAdapter extends FragmentPagerAdapter
{
static final int NUM_ITEMS = 2;
private final FragmentManager mFragmentManager;
private Fragment mFragmentAtPos0;
public MyAdapter(FragmentManager fm)
{
super(fm);
mFragmentManager = fm;
}
@Override
public Fragment getItem(int position)
{
if (position == 0)
{
if (mFragmentAtPos0 == null)
{
mFragmentAtPos0 = FirstPageFragment.newInstance(new FirstPageFragmentListener()
{
public void onSwitchToNextFragment()
{
mFragmentManager.beginTransaction().remove(mFragmentAtPos0).commit();
mFragmentAtPos0 = NextFragment.newInstance();
notifyDataSetChanged();
}
});
}
return mFragmentAtPos0;
}
else
return SecondPageFragment.newInstance();
}
@Override
public int getCount()
{
return NUM_ITEMS;
}
@Override
public int getItemPosition(Object object)
{
if (object instanceof FirstPageFragment && mFragmentAtPos0 instanceof NextFragment)
return POSITION_NONE;
return POSITION_UNCHANGED;
}
}
public interface FirstPageFragmentListener
{
void onSwitchToNextFragment();
}
이것이 누군가를 돕기를 바랍니다!
2012 년 11 월 13 일부터 ViewPager에서 조각을 repacing하는 것이 훨씬 쉬워졌습니다. Google은 중첩 조각을 지원하는 Android 4.2를 출시했으며 새로운 Android 지원 라이브러리 v11 에서도 지원 되므로 1.6까지 계속 작동합니다.
getChildFragmentManager를 사용하는 것을 제외하고는 프래그먼트를 교체하는 일반적인 방법과 매우 유사합니다. 사용자가 뒤로 버튼을 클릭 할 때 중첩 된 조각 백 스택이 팝업되지 않는 것을 제외하고는 작동하는 것 같습니다 . 연결된 질문의 솔루션에 따라 조각의 하위 관리자에서 popBackStackImmediate ()를 수동으로 호출해야합니다. 따라서 ViewPager의 현재 조각을 가져오고 getChildFragmentManager (). popBackStackImmediate ()를 호출하는 ViewPager 활동의 onBackPressed ()를 재정의해야합니다.
현재 표시되는 조각을 얻는 것도 약간 해킹입니다. 이 더러운 "android : switcher : VIEWPAGER_ID : INDEX"솔루션을 사용 했지만 이 페이지 의 두 번째 솔루션 에 설명 된대로 ViewPager의 모든 조각을 직접 추적 할 수도 있습니다 .
여기 사용자가 행을 클릭하고 뒤로 버튼이 작동 할 때 ViewPager에 상세보기가 표시된 4 개의 ListView가있는 ViewPager에 대한 코드가 있습니다. 간결성을 위해 관련 코드 만 포함하려고 했으므로 전체 앱을 GitHub에 업로드하려면 의견을 남겨주십시오.
HomeActivity.java
public class HomeActivity extends SherlockFragmentActivity {
FragmentAdapter mAdapter;
ViewPager mPager;
TabPageIndicator mIndicator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mAdapter = new FragmentAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mIndicator = (TabPageIndicator)findViewById(R.id.indicator);
mIndicator.setViewPager(mPager);
}
// This the important bit to make sure the back button works when you're nesting fragments. Very hacky, all it takes is some Google engineer to change that ViewPager view tag to break this in a future Android update.
@Override
public void onBackPressed() {
Fragment fragment = (Fragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":"+mPager.getCurrentItem());
if (fragment != null) // could be null if not instantiated yet
{
if (fragment.getView() != null) {
// Pop the backstack on the ChildManager if there is any. If not, close this activity as normal.
if (!fragment.getChildFragmentManager().popBackStackImmediate()) {
finish();
}
}
}
}
class FragmentAdapter extends FragmentPagerAdapter {
public FragmentAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return ListProductsFragment.newInstance();
case 1:
return ListActiveSubstancesFragment.newInstance();
case 2:
return ListProductFunctionsFragment.newInstance();
case 3:
return ListCropsFragment.newInstance();
default:
return null;
}
}
@Override
public int getCount() {
return 4;
}
}
}
ListProductsFragment.java
public class ListProductsFragment extends SherlockFragment {
private ListView list;
public static ListProductsFragment newInstance() {
ListProductsFragment f = new ListProductsFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View V = inflater.inflate(R.layout.list, container, false);
list = (ListView)V.findViewById(android.R.id.list);
list.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// This is important bit
Fragment productDetailFragment = FragmentProductDetail.newInstance();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.addToBackStack(null);
transaction.replace(R.id.products_list_linear, productDetailFragment).commit();
}
});
return V;
}
}
도움이되고 우아하다고 생각한 @wize의 답변을 바탕으로 부분적으로 원하는 것을 달성 할 수 있었기 때문에 일단 교체 된 첫 번째 조각으로 가능성을 되돌릴 수있었습니다. 나는 그의 코드를 조금 수정하는 것을 달성했다.
이것은 FragmentPagerAdapter입니다.
public static class MyAdapter extends FragmentPagerAdapter {
private final class CalendarPageListener implements
CalendarPageFragmentListener {
public void onSwitchToNextFragment() {
mFragmentManager.beginTransaction().remove(mFragmentAtPos0)
.commit();
if (mFragmentAtPos0 instanceof FirstFragment){
mFragmentAtPos0 = NextFragment.newInstance(listener);
}else{ // Instance of NextFragment
mFragmentAtPos0 = FirstFragment.newInstance(listener);
}
notifyDataSetChanged();
}
}
CalendarPageListener listener = new CalendarPageListener();;
private Fragment mFragmentAtPos0;
private FragmentManager mFragmentManager;
public MyAdapter(FragmentManager fm) {
super(fm);
mFragmentManager = fm;
}
@Override
public int getCount() {
return NUM_ITEMS;
}
@Override
public int getItemPosition(Object object) {
if (object instanceof FirstFragment && mFragmentAtPos0 instanceof NextFragment)
return POSITION_NONE;
if (object instanceof NextFragment && mFragmentAtPos0 instanceof FirstFragment)
return POSITION_NONE;
return POSITION_UNCHANGED;
}
@Override
public Fragment getItem(int position) {
if (position == 0)
return Portada.newInstance();
if (position == 1) { // Position where you want to replace fragments
if (mFragmentAtPos0 == null) {
mFragmentAtPos0 = FirstFragment.newInstance(listener);
}
return mFragmentAtPos0;
}
if (position == 2)
return Clasificacion.newInstance();
if (position == 3)
return Informacion.newInstance();
return null;
}
}
public interface CalendarPageFragmentListener {
void onSwitchToNextFragment();
}
대체를 수행하려면, 단순히 유형의 정적 필드를 정의 하고 해당 프래그먼트 CalendarPageFragmentListener
의 newInstance
메소드를 통해 초기화 한 후 호출 FirstFragment.pageListener.onSwitchToNextFragment()
하거나 NextFragment.pageListener.onSwitchToNextFragment()
눈에 띄게 호출 하십시오.
나는 다음에 대한 솔루션을 구현했다.
- 탭 내부의 동적 조각 교체
- 탭당 히스토리 유지 보수
- 방향 변경 작업
이를 달성하기위한 요령은 다음과 같습니다.
- notifyDataSetChanged () 메소드를 사용하여 단편 대체를 적용하십시오.
- 조각 모음 관리자는 백 스테이지에만 사용하고 조각 모음 교체에는 사용하지 마십시오.
- 메멘토 패턴 (스택)을 사용하여 기록 유지
어댑터 코드는 다음과 같습니다.
public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
/** The sherlock fragment activity. */
private final SherlockFragmentActivity mActivity;
/** The action bar. */
private final ActionBar mActionBar;
/** The pager. */
private final ViewPager mPager;
/** The tabs. */
private List<TabInfo> mTabs = new LinkedList<TabInfo>();
/** The total number of tabs. */
private int TOTAL_TABS;
private Map<Integer, Stack<TabInfo>> history = new HashMap<Integer, Stack<TabInfo>>();
/**
* Creates a new instance.
*
* @param activity the activity
* @param pager the pager
*/
public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
super(activity.getSupportFragmentManager());
activity.getSupportFragmentManager();
this.mActivity = activity;
this.mActionBar = activity.getSupportActionBar();
this.mPager = pager;
mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
}
/**
* Adds the tab.
*
* @param image the image
* @param fragmentClass the class
* @param args the arguments
*/
public void addTab(final Drawable image, final Class fragmentClass, final Bundle args) {
final TabInfo tabInfo = new TabInfo(fragmentClass, args);
final ActionBar.Tab tab = mActionBar.newTab();
tab.setTabListener(this);
tab.setTag(tabInfo);
tab.setIcon(image);
mTabs.add(tabInfo);
mActionBar.addTab(tab);
notifyDataSetChanged();
}
@Override
public Fragment getItem(final int position) {
final TabInfo tabInfo = mTabs.get(position);
return Fragment.instantiate(mActivity, tabInfo.fragmentClass.getName(), tabInfo.args);
}
@Override
public int getItemPosition(final Object object) {
/* Get the current position. */
int position = mActionBar.getSelectedTab().getPosition();
/* The default value. */
int pos = POSITION_NONE;
if (history.get(position).isEmpty()) {
return POSITION_NONE;
}
/* Checks if the object exists in current history. */
for (Stack<TabInfo> stack : history.values()) {
TabInfo c = stack.peek();
if (c.fragmentClass.getName().equals(object.getClass().getName())) {
pos = POSITION_UNCHANGED;
break;
}
}
return pos;
}
@Override
public int getCount() {
return mTabs.size();
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageSelected(int position) {
mActionBar.setSelectedNavigationItem(position);
}
@Override
public void onTabSelected(final ActionBar.Tab tab, final FragmentTransaction ft) {
TabInfo tabInfo = (TabInfo) tab.getTag();
for (int i = 0; i < mTabs.size(); i++) {
if (mTabs.get(i).equals(tabInfo)) {
mPager.setCurrentItem(i);
}
}
}
@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
public void replace(final int position, final Class fragmentClass, final Bundle args) {
/* Save the fragment to the history. */
mActivity.getSupportFragmentManager().beginTransaction().addToBackStack(null).commit();
/* Update the tabs. */
updateTabs(new TabInfo(fragmentClass, args), position);
/* Updates the history. */
history.get(position).push(new TabInfo(mTabs.get(position).fragmentClass, mTabs.get(position).args));
notifyDataSetChanged();
}
/**
* Updates the tabs.
*
* @param tabInfo
* the new tab info
* @param position
* the position
*/
private void updateTabs(final TabInfo tabInfo, final int position) {
mTabs.remove(position);
mTabs.add(position, tabInfo);
mActionBar.getTabAt(position).setTag(tabInfo);
}
/**
* Creates the history using the current state.
*/
public void createHistory() {
int position = 0;
TOTAL_TABS = mTabs.size();
for (TabInfo mTab : mTabs) {
if (history.get(position) == null) {
history.put(position, new Stack<TabInfo>());
}
history.get(position).push(new TabInfo(mTab.fragmentClass, mTab.args));
position++;
}
}
/**
* Called on back
*/
public void back() {
int position = mActionBar.getSelectedTab().getPosition();
if (!historyIsEmpty(position)) {
/* In case there is not any other item in the history, then finalize the activity. */
if (isLastItemInHistory(position)) {
mActivity.finish();
}
final TabInfo currentTabInfo = getPrevious(position);
mTabs.clear();
for (int i = 0; i < TOTAL_TABS; i++) {
if (i == position) {
mTabs.add(new TabInfo(currentTabInfo.fragmentClass, currentTabInfo.args));
} else {
TabInfo otherTabInfo = history.get(i).peek();
mTabs.add(new TabInfo(otherTabInfo.fragmentClass, otherTabInfo.args));
}
}
}
mActionBar.selectTab(mActionBar.getTabAt(position));
notifyDataSetChanged();
}
/**
* Returns if the history is empty.
*
* @param position
* the position
* @return the flag if empty
*/
private boolean historyIsEmpty(final int position) {
return history == null || history.isEmpty() || history.get(position).isEmpty();
}
private boolean isLastItemInHistory(final int position) {
return history.get(position).size() == 1;
}
/**
* Returns the previous state by the position provided.
*
* @param position
* the position
* @return the tab info
*/
private TabInfo getPrevious(final int position) {
TabInfo currentTabInfo = history.get(position).pop();
if (!history.get(position).isEmpty()) {
currentTabInfo = history.get(position).peek();
}
return currentTabInfo;
}
/** The tab info class */
private static class TabInfo {
/** The fragment class. */
public Class fragmentClass;
/** The args.*/
public Bundle args;
/**
* Creates a new instance.
*
* @param fragmentClass
* the fragment class
* @param args
* the args
*/
public TabInfo(Class fragmentClass, Bundle args) {
this.fragmentClass = fragmentClass;
this.args = args;
}
@Override
public boolean equals(final Object o) {
return this.fragmentClass.getName().equals(o.getClass().getName());
}
@Override
public int hashCode() {
return fragmentClass.getName() != null ? fragmentClass.getName().hashCode() : 0;
}
@Override
public String toString() {
return "TabInfo{" +
"fragmentClass=" + fragmentClass +
'}';
}
}
모든 탭을 처음 추가 할 때 createHistory () 메소드를 호출하여 초기 히스토리를 작성해야합니다.
public void createHistory() {
int position = 0;
TOTAL_TABS = mTabs.size();
for (TabInfo mTab : mTabs) {
if (history.get(position) == null) {
history.put(position, new Stack<TabInfo>());
}
history.get(position).push(new TabInfo(mTab.fragmentClass, mTab.args));
position++;
}
}
조각을 특정 탭으로 바꿀 때마다 replace (final int position, final Class fragmentClass, final Bundle args)
/* Save the fragment to the history. */
mActivity.getSupportFragmentManager().beginTransaction().addToBackStack(null).commit();
/* Update the tabs. */
updateTabs(new TabInfo(fragmentClass, args), position);
/* Updates the history. */
history.get(position).push(new TabInfo(mTabs.get(position).fragmentClass, mTabs.get(position).args));
notifyDataSetChanged();
다시 누르면 back () 메소드를 호출해야합니다.
public void back() {
int position = mActionBar.getSelectedTab().getPosition();
if (!historyIsEmpty(position)) {
/* In case there is not any other item in the history, then finalize the activity. */
if (isLastItemInHistory(position)) {
mActivity.finish();
}
final TabInfo currentTabInfo = getPrevious(position);
mTabs.clear();
for (int i = 0; i < TOTAL_TABS; i++) {
if (i == position) {
mTabs.add(new TabInfo(currentTabInfo.fragmentClass, currentTabInfo.args));
} else {
TabInfo otherTabInfo = history.get(i).peek();
mTabs.add(new TabInfo(otherTabInfo.fragmentClass, otherTabInfo.args));
}
}
}
mActionBar.selectTab(mActionBar.getTabAt(position));
notifyDataSetChanged();
}
이 솔루션은 셜록 작업 표시 줄 및 슬쩍 제스처와 함께 작동합니다.
tl; dr : 호스팅 된 컨텐츠를 교체하고 브라우저에서와 같이 뒤로 탐색 히스토리를 추적하는 호스트 단편을 사용하십시오.
유스 케이스가 고정 된 양의 탭으로 구성되어 있으므로 솔루션이 잘 작동합니다 .ViewPager를 사용자 정의 클래스의 인스턴스로 채우는 것이 HostFragment
호스팅 된 컨텐츠를 대체하고 자체 탐색 내역을 유지할 수 있습니다. 호스팅 된 조각을 바꾸려면 메소드를 호출하십시오 hostfragment.replaceFragment()
.
public void replaceFragment(Fragment fragment, boolean addToBackstack) {
if (addToBackstack) {
getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).addToBackStack(null).commit();
} else {
getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).commit();
}
}
이 방법은 프레임 레이아웃을 id로 바꾸고 R.id.hosted_fragment
메서드에 제공된 조각을 사용하는 것입니다.
자세한 내용과 GitHub에 대한 완전한 작업 예제는 이 주제에 대한 튜토리얼을 확인하십시오 !
제시된 솔루션 중 일부는 문제를 부분적으로 해결하는 데 많은 도움이되었지만 여전히 일부 경우 조각 콘텐츠 대신 예기치 않은 예외와 블랙 페이지 콘텐츠를 생성 한 솔루션에서 누락 된 중요한 사항이 하나 있습니다.
문제는이다 FragmentPagerAdapter의 클래스에 캐시 조각을 저장하는 항목 ID를 사용 FragmentManager . 이러한 이유로, 당신은 또한 오버라이드 (override) 할 필요가 주는 getItemId (INT 위치) 은 예를 들어 반환하도록 방법을 위치 최상위 페이지와에 대한 100 + 위치 정보 페이지를. 그렇지 않으면 이전에 생성 된 최상위 조각이 세부 수준 조각 대신 캐시에서 반환됩니다.
또한 ViewPager를 사용하여 Fragment 페이지로 탭과 같은 활동을 구현하는 방법 과 RadioGroup 을 사용하여 탭 페이지를 세부 페이지로 대체하고 뒤로 버튼을 지원 하는 탭 예제를 공유 합니다. 이 구현은 한 수준의 백 스태킹 (항목 목록-항목 세부 사항) 만 지원하지만 다중 레벨 백 스태킹 구현은 간단합니다. 이 예제는 예를 들어 두 번째 페이지로 전환하고 첫 번째 페이지의 조각을 변경하고 (보이지 않는 동안) 첫 번째 페이지로 돌아 오는 경우를 제외하고는 NullPointerException 을 발생 시키는 것을 제외하고는 정상적인 경우에 아주 잘 작동 합니다. 알아 내면이 문제에 대한 해결책을 게시 할 것입니다.
public class TabsActivity extends FragmentActivity {
public static final int PAGE_COUNT = 3;
public static final int FIRST_PAGE = 0;
public static final int SECOND_PAGE = 1;
public static final int THIRD_PAGE = 2;
/**
* Opens a new inferior page at specified tab position and adds the current page into back
* stack.
*/
public void startPage(int position, Fragment content) {
// Replace page adapter fragment at position.
mPagerAdapter.start(position, content);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initialize basic layout.
this.setContentView(R.layout.tabs_activity);
// Add tab fragments to view pager.
{
// Create fragments adapter.
mPagerAdapter = new PagerAdapter(pager);
ViewPager pager = (ViewPager) super.findViewById(R.id.tabs_view_pager);
pager.setAdapter(mPagerAdapter);
// Update active tab in tab bar when page changes.
pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int index, float value, int nextIndex) {
// Not used.
}
@Override
public void onPageSelected(int index) {
RadioGroup tabs_radio_group = (RadioGroup) TabsActivity.this.findViewById(
R.id.tabs_radio_group);
switch (index) {
case 0: {
tabs_radio_group.check(R.id.first_radio_button);
}
break;
case 1: {
tabs_radio_group.check(R.id.second_radio_button);
}
break;
case 2: {
tabs_radio_group.check(R.id.third_radio_button);
}
break;
}
}
@Override
public void onPageScrollStateChanged(int index) {
// Not used.
}
});
}
// Set "tabs" radio group on checked change listener that changes the displayed page.
RadioGroup radio_group = (RadioGroup) this.findViewById(R.id.tabs_radio_group);
radio_group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int id) {
// Get view pager representing tabs.
ViewPager view_pager = (ViewPager) TabsActivity.this.findViewById(R.id.tabs_view_pager);
if (view_pager == null) {
return;
}
// Change the active page.
switch (id) {
case R.id.first_radio_button: {
view_pager.setCurrentItem(FIRST_PAGE);
}
break;
case R.id.second_radio_button: {
view_pager.setCurrentItem(SECOND_PAGE);
}
break;
case R.id.third_radio_button: {
view_pager.setCurrentItem(THIRD_PAGE);
}
break;
}
});
}
}
@Override
public void onBackPressed() {
if (!mPagerAdapter.back()) {
super.onBackPressed();
}
}
/**
* Serves the fragments when paging.
*/
private class PagerAdapter extends FragmentPagerAdapter {
public PagerAdapter(ViewPager container) {
super(TabsActivity.this.getSupportFragmentManager());
mContainer = container;
mFragmentManager = TabsActivity.this.getSupportFragmentManager();
// Prepare "empty" list of fragments.
mFragments = new ArrayList<Fragment>(){};
mBackFragments = new ArrayList<Fragment>(){};
for (int i = 0; i < PAGE_COUNT; i++) {
mFragments.add(null);
mBackFragments.add(null);
}
}
/**
* Replaces the view pager fragment at specified position.
*/
public void replace(int position, Fragment fragment) {
// Get currently active fragment.
Fragment old_fragment = mFragments.get(position);
if (old_fragment == null) {
return;
}
// Replace the fragment using transaction and in underlaying array list.
// NOTE .addToBackStack(null) doesn't work
this.startUpdate(mContainer);
mFragmentManager.beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.remove(old_fragment).add(mContainer.getId(), fragment)
.commit();
mFragments.set(position, fragment);
this.notifyDataSetChanged();
this.finishUpdate(mContainer);
}
/**
* Replaces the fragment at specified position and stores the current fragment to back stack
* so it can be restored by #back().
*/
public void start(int position, Fragment fragment) {
// Remember current fragment.
mBackFragments.set(position, mFragments.get(position));
// Replace the displayed fragment.
this.replace(position, fragment);
}
/**
* Replaces the current fragment by fragment stored in back stack. Does nothing and returns
* false if no fragment is back-stacked.
*/
public boolean back() {
int position = mContainer.getCurrentItem();
Fragment fragment = mBackFragments.get(position);
if (fragment == null) {
// Nothing to go back.
return false;
}
// Restore the remembered fragment and remove it from back fragments.
this.replace(position, fragment);
mBackFragments.set(position, null);
return true;
}
/**
* Returns fragment of a page at specified position.
*/
@Override
public Fragment getItem(int position) {
// If fragment not yet initialized, create its instance.
if (mFragments.get(position) == null) {
switch (position) {
case FIRST_PAGE: {
mFragments.set(FIRST_PAGE, new DefaultFirstFragment());
}
break;
case SECOND_PAGE: {
mFragments.set(SECOND_PAGE, new DefaultSecondFragment());
}
break;
case THIRD_PAGE: {
mFragments.set(THIRD_PAGE, new DefaultThirdFragment());
}
break;
}
}
// Return fragment instance at requested position.
return mFragments.get(position);
}
/**
* Custom item ID resolution. Needed for proper page fragment caching.
* @see FragmentPagerAdapter#getItemId(int).
*/
@Override
public long getItemId(int position) {
// Fragments from second level page hierarchy have their ID raised above 100. This is
// important to FragmentPagerAdapter because it is caching fragments to FragmentManager with
// this item ID key.
Fragment item = mFragments.get(position);
if (item != null) {
if ((item instanceof NewFirstFragment) || (item instanceof NewSecondFragment) ||
(item instanceof NewThirdFragment)) {
return 100 + position;
}
}
return position;
}
/**
* Returns number of pages.
*/
@Override
public int getCount() {
return mFragments.size();
}
@Override
public int getItemPosition(Object object)
{
int position = POSITION_UNCHANGED;
if ((object instanceof DefaultFirstFragment) || (object instanceof NewFirstFragment)) {
if (object.getClass() != mFragments.get(FIRST_PAGE).getClass()) {
position = POSITION_NONE;
}
}
if ((object instanceof DefaultSecondragment) || (object instanceof NewSecondFragment)) {
if (object.getClass() != mFragments.get(SECOND_PAGE).getClass()) {
position = POSITION_NONE;
}
}
if ((object instanceof DefaultThirdFragment) || (object instanceof NewThirdFragment)) {
if (object.getClass() != mFragments.get(THIRD_PAGE).getClass()) {
position = POSITION_NONE;
}
}
return position;
}
private ViewPager mContainer;
private FragmentManager mFragmentManager;
/**
* List of page fragments.
*/
private List<Fragment> mFragments;
/**
* List of page fragments to return to in onBack();
*/
private List<Fragment> mBackFragments;
}
/**
* Tab fragments adapter.
*/
private PagerAdapter mPagerAdapter;
}
인덱스 2와 3에 3 개의 요소와 2 개의 하위 요소가있는 ViewPager를 만들었습니다.
StackOverFlow의 이전 질문과 답변의 도움으로 이것을 구현했으며 여기에 링크가 있습니다.
돌며 조각을 교체하려면 ViewPager
소스의 코드로 이동 할 수 있습니다 ViewPager
, PagerAdapter
그리고 FragmentStatePagerAdapter
당신의 프로젝트에 클래스를 코드에 다음을 추가.
로 ViewPager
:
public void notifyItemChanged(Object oldItem, Object newItem) {
if (mItems != null) {
for (ItemInfo itemInfo : mItems) {
if (itemInfo.object.equals(oldItem)) {
itemInfo.object = newItem;
}
}
}
invalidate();
}
FragmentStatePagerAdapter로 :
public void replaceFragmetns(ViewPager container, Fragment oldFragment, Fragment newFragment) {
startUpdate(container);
// remove old fragment
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
int position = getFragmentPosition(oldFragment);
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, null);
mFragments.set(position, null);
mCurTransaction.remove(oldFragment);
// add new fragment
while (mFragments.size() <= position) {
mFragments.add(null);
}
mFragments.set(position, newFragment);
mCurTransaction.add(container.getId(), newFragment);
finishUpdate(container);
// ensure getItem returns newFragemtn after calling handleGetItemInbalidated()
handleGetItemInbalidated(container, oldFragment, newFragment);
container.notifyItemChanged(oldFragment, newFragment);
}
protected abstract void handleGetItemInbalidated(View container, Fragment oldFragment, Fragment newFragment);
protected abstract int getFragmentPosition(Fragment fragment);
handleGetItemInvalidated()
다음 번 호출 후 getItem()
newFragment getFragmentPosition()
가 어댑터에서 프래그먼트의 위치를 리턴하도록합니다.
이제 조각 호출을 대체하려면
mAdapter.replaceFragmetns(mViewPager, oldFragment, newFragment);
예제 프로젝트에 관심이 있다면 소스를 요청하십시오.
AndroidTeam의 솔루션으로 훌륭하게 작동하지만, 다시 돌아가는 기능이 필요하다는 것을 알았습니다. 그러나 이것을 FrgmentTransaction.addToBackStack(null)
추가하면 ViewPager에 알리지 않고 조각 만 교체됩니다. 제공된 솔루션과이 작은 향상 기능을 결합하면 단순히 활동 onBackPressed()
방법을 재정 의하여 이전 상태로 돌아갈 수 있습니다 . 가장 큰 단점은 한 번에 한 번만 되돌아 가서 여러 번의 클릭이 발생할 수 있다는 것입니다
private ArrayList<Fragment> bFragments = new ArrayList<Fragment>();
private ArrayList<Integer> bPosition = new ArrayList<Integer>();
public void replaceFragmentsWithBackOut(ViewPager container, Fragment oldFragment, Fragment newFragment) {
startUpdate(container);
// remove old fragment
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
int position = getFragmentPosition(oldFragment);
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
//Add Fragment to Back List
bFragments.add(oldFragment);
//Add Pager Position to Back List
bPosition.add(position);
mSavedState.set(position, null);
mFragments.set(position, null);
mCurTransaction.remove(oldFragment);
// add new fragment
while (mFragments.size() <= position) {
mFragments.add(null);
}
mFragments.set(position, newFragment);
mCurTransaction.add(container.getId(), newFragment);
finishUpdate(container);
// ensure getItem returns newFragemtn after calling handleGetItemInbalidated()
handleGetItemInvalidated(container, oldFragment, newFragment);
container.notifyItemChanged(oldFragment, newFragment);
}
public boolean popBackImmediate(ViewPager container){
int bFragSize = bFragments.size();
int bPosSize = bPosition.size();
if(bFragSize>0 && bPosSize>0){
if(bFragSize==bPosSize){
int last = bFragSize-1;
int position = bPosition.get(last);
//Returns Fragment Currently at this position
Fragment replacedFragment = mFragments.get(position);
Fragment originalFragment = bFragments.get(last);
this.replaceFragments(container, replacedFragment, originalFragment);
bPosition.remove(last);
bFragments.remove(last);
return true;
}
}
return false;
}
이것이 누군가를 돕기를 바랍니다.
또한 지금까지는 getFragmentPosition()
거의 getItem()
반대입니다. 어느 프래그먼트가 어디로 갈지 알고 있습니다. 올바른 위치로 돌아와야합니다. 예를 들면 다음과 같습니다.
@Override
protected int getFragmentPosition(Fragment fragment) {
if(fragment.equals(originalFragment1)){
return 0;
}
if(fragment.equals(replacementFragment1)){
return 0;
}
if(fragment.equals(Fragment2)){
return 1;
}
return -1;
}
귀하의 onCreateView
방법에서 container
실제로는 ViewPager
인스턴스입니다.
그래서 그냥 전화
ViewPager vpViewPager = (ViewPager) container;
vpViewPager.setCurrentItem(1);
의 현재 조각이 변경됩니다 ViewPager
.
또한 Stacks 와 함께 작동하는 솔루션을 만들었습니다 . 더 모듈 방식이므로 u에서 각 조각 및 세부 조각을 지정할 필요가 없습니다 FragmentPagerAdapter
. ActionbarSherlock의 예제 위에 빌드되어 Google 데모 앱에서 올바른지 여부를 알 수 있습니다.
/**
* This is a helper class that implements the management of tabs and all
* details of connecting a ViewPager with associated TabHost. It relies on a
* trick. Normally a tab host has a simple API for supplying a View or
* Intent that each tab will show. This is not sufficient for switching
* between pages. So instead we make the content part of the tab host
* 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
* view to show as the tab content. It listens to changes in tabs, and takes
* care of switch to the correct paged in the ViewPager whenever the selected
* tab changes.
*
* Changed to support more Layers of fragments on each Tab.
* by sebnapi (2012)
*
*/
public class TabsAdapter extends FragmentPagerAdapter
implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
private final Context mContext;
private final TabHost mTabHost;
private final ViewPager mViewPager;
private ArrayList<String> mTabTags = new ArrayList<String>();
private HashMap<String, Stack<TabInfo>> mTabStackMap = new HashMap<String, Stack<TabInfo>>();
static final class TabInfo {
public final String tag;
public final Class<?> clss;
public Bundle args;
TabInfo(String _tag, Class<?> _class, Bundle _args) {
tag = _tag;
clss = _class;
args = _args;
}
}
static class DummyTabFactory implements TabHost.TabContentFactory {
private final Context mContext;
public DummyTabFactory(Context context) {
mContext = context;
}
@Override
public View createTabContent(String tag) {
View v = new View(mContext);
v.setMinimumWidth(0);
v.setMinimumHeight(0);
return v;
}
}
public interface SaveStateBundle{
public Bundle onRemoveFragment(Bundle outState);
}
public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
super(activity.getSupportFragmentManager());
mContext = activity;
mTabHost = tabHost;
mViewPager = pager;
mTabHost.setOnTabChangedListener(this);
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
/**
* Add a Tab which will have Fragment Stack. Add Fragments on this Stack by using
* addFragment(FragmentManager fm, String _tag, Class<?> _class, Bundle _args)
* The Stack will hold always the default Fragment u add here.
*
* DON'T ADD Tabs with same tag, it's not beeing checked and results in unexpected
* beahvior.
*
* @param tabSpec
* @param clss
* @param args
*/
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args){
Stack<TabInfo> tabStack = new Stack<TabInfo>();
tabSpec.setContent(new DummyTabFactory(mContext));
mTabHost.addTab(tabSpec);
String tag = tabSpec.getTag();
TabInfo info = new TabInfo(tag, clss, args);
mTabTags.add(tag); // to know the position of the tab tag
tabStack.add(info);
mTabStackMap.put(tag, tabStack);
notifyDataSetChanged();
}
/**
* Will add the Fragment to Tab with the Tag _tag. Provide the Class of the Fragment
* it will be instantiated by this object. Proivde _args for your Fragment.
*
* @param fm
* @param _tag
* @param _class
* @param _args
*/
public void addFragment(FragmentManager fm, String _tag, Class<?> _class, Bundle _args){
TabInfo info = new TabInfo(_tag, _class, _args);
Stack<TabInfo> tabStack = mTabStackMap.get(_tag);
Fragment frag = fm.findFragmentByTag("android:switcher:" + mViewPager.getId() + ":" + mTabTags.indexOf(_tag));
if(frag instanceof SaveStateBundle){
Bundle b = new Bundle();
((SaveStateBundle) frag).onRemoveFragment(b);
tabStack.peek().args = b;
}
tabStack.add(info);
FragmentTransaction ft = fm.beginTransaction();
ft.remove(frag).commit();
notifyDataSetChanged();
}
/**
* Will pop the Fragment added to the Tab with the Tag _tag
*
* @param fm
* @param _tag
* @return
*/
public boolean popFragment(FragmentManager fm, String _tag){
Stack<TabInfo> tabStack = mTabStackMap.get(_tag);
if(tabStack.size()>1){
tabStack.pop();
Fragment frag = fm.findFragmentByTag("android:switcher:" + mViewPager.getId() + ":" + mTabTags.indexOf(_tag));
FragmentTransaction ft = fm.beginTransaction();
ft.remove(frag).commit();
notifyDataSetChanged();
return true;
}
return false;
}
public boolean back(FragmentManager fm) {
int position = mViewPager.getCurrentItem();
return popFragment(fm, mTabTags.get(position));
}
@Override
public int getCount() {
return mTabStackMap.size();
}
@Override
public int getItemPosition(Object object) {
ArrayList<Class<?>> positionNoneHack = new ArrayList<Class<?>>();
for(Stack<TabInfo> tabStack: mTabStackMap.values()){
positionNoneHack.add(tabStack.peek().clss);
} // if the object class lies on top of our stacks, we return default
if(positionNoneHack.contains(object.getClass())){
return POSITION_UNCHANGED;
}
return POSITION_NONE;
}
@Override
public Fragment getItem(int position) {
Stack<TabInfo> tabStack = mTabStackMap.get(mTabTags.get(position));
TabInfo info = tabStack.peek();
return Fragment.instantiate(mContext, info.clss.getName(), info.args);
}
@Override
public void onTabChanged(String tabId) {
int position = mTabHost.getCurrentTab();
mViewPager.setCurrentItem(position);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// Unfortunately when TabHost changes the current tab, it kindly
// also takes care of putting focus on it when not in touch mode.
// The jerk.
// This hack tries to prevent this from pulling focus out of our
// ViewPager.
TabWidget widget = mTabHost.getTabWidget();
int oldFocusability = widget.getDescendantFocusability();
widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mTabHost.setCurrentTab(position);
widget.setDescendantFocusability(oldFocusability);
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
MainActivity에서 뒤로 버튼 기능을 위해 이것을 추가하십시오 .
@Override
public void onBackPressed() {
if (!mTabsAdapter.back(getSupportFragmentManager())) {
super.onBackPressed();
}
}
제거 할 때 조각 상태 를 저장하고 싶다면 . 프래그먼트 SaveStateBundle
가 함수에 저장 상태의 번들을 반환 하여 인터페이스를 구현하도록 하십시오. 로 인스턴스화 후 번들을 가져옵니다 this.getArguments()
.
다음 과 같이 탭을 인스턴스화 할 수 있습니다 .
mTabsAdapter.addTab(mTabHost.newTabSpec("firstTabTag").setIndicator("First Tab Title"),
FirstFragmentActivity.FirstFragmentFragment.class, null);
탭 스택 위에 조각을 추가하려는 경우 비슷하게 작동합니다. 중요 : 두 탭 위에 동일한 클래스의 인스턴스 2 개를 원하면 작동하지 않습니다. 이 솔루션을 빠르게 함께 사용했기 때문에 경험이 없어도 공유 할 수 있습니다.
viewpager에서 프래그먼트를 교체하는 것은 매우 복잡하지만 매우 가능하며 매우 매끄럽게 보일 수 있습니다. 먼저 viewpager 자체가 프래그먼트 제거 및 추가를 처리하도록해야합니다. SearchFragment 내부의 조각을 교체하면 viewpager가 조각보기를 유지합니다. 따라서 SearchFragment는 교체하려고 할 때 제거되기 때문에 빈 페이지로 끝납니다.
해결 방법은 viewpager 내부에서 리스너를 만들어 외부에서 변경 한 내용을 처리하므로 먼저이 코드를 어댑터 하단에 추가하십시오.
public interface nextFragmentListener {
public void fragment0Changed(String newFragmentIdentification);
}
그런 다음 조각을 변경하려고 할 때 리스너가되는 viewpager에 개인 클래스를 만들어야합니다. 예를 들어 이와 같은 것을 추가 할 수 있습니다. 방금 작성된 인터페이스를 구현합니다. 따라서이 메소드를 호출 할 때마다 아래 클래스 내부에서 코드가 실행됩니다.
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();
}
여기서 지적해야 할 두 가지 주요 사항이 있습니다.
- fragAt0은 "유연한"단편입니다. 당신이 제공하는 조각 유형을 취할 수 있습니다. 이를 통해 위치 0의 조각을 원하는 조각으로 변경할 때 가장 친한 친구가 될 수 있습니다.
'newInstance (listener) 생성자에 배치 된 리스너를 확인하십시오. 이것들은 조각 0 변경 (문자열 newFragmentIdentification)`을 호출하는 방법입니다. 다음 코드는 프래그먼트 안에 리스너를 만드는 방법을 보여줍니다.
정적 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의 getItem override 메소드에 있습니다.
@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){
((Fragment_Table) pagerAdapter.getItem(0)).backPressed();
}else if (pagerAdapter.getItem(0) instanceof FragmentSearch) {
finish();
}
}
fragment0changed를 호출하는 FragmentSearchResults 내에 backPressed ()라는 메소드를 작성해야합니다. 이것은 이전에 보여준 코드와 함께 뒤로 버튼을 누르는 것을 처리합니다. viewpager를 변경하는 코드로 행운을 빕니다. 많은 작업이 필요하며 내가 찾은 한 빠른 적응이 없습니다. 내가 말했듯이 기본적으로 사용자 정의 viewpager 어댑터를 만들고 리스너를 사용하여 필요한 모든 변경 사항을 처리하도록합니다.
이 문제에 대한 비교적 간단한 해결책이 있습니다. 이 솔루션의 핵심 은 전자가 FragmentStatePagerAdapter
대신 FragmentPagerAdapter
사용하지 않는 프래그먼트를 제거하고 후자는 여전히 인스턴스를 유지 하므로 대신 사용하는 것입니다 . 두 번째는 POSITION_NONE
getItem () 의 사용입니다 . 간단한 List를 사용하여 조각을 추적했습니다. 필자의 요구 사항은 전체 조각 목록을 한 번에 새 목록으로 바꾸는 것이었지만 개별 조각을 대체하기 위해 아래를 쉽게 수정할 수 있습니다.
public class MyFragmentAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragmentList = new ArrayList<Fragment>();
private List<String> tabTitleList = new ArrayList<String>();
public MyFragmentAdapter(FragmentManager fm) {
super(fm);
}
public void addFragments(List<Fragment> fragments, List<String> titles) {
fragmentList.clear();
tabTitleList.clear();
fragmentList.addAll(fragments);
tabTitleList.addAll(titles);
notifyDataSetChanged();
}
@Override
public int getItemPosition(Object object) {
if (fragmentList.contains(object)) {
return POSITION_UNCHANGED;
}
return POSITION_NONE;
}
@Override
public Fragment getItem(int item) {
if (item >= fragmentList.size()) {
return null;
}
return fragmentList.get(item);
}
@Override
public int getCount() {
return fragmentList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return tabTitleList.get(position);
}
}
중간에 새 조각을 추가하거나 현재 조각을 바꾸려는 경우에도 잘 작동하는 간단한 솔루션을 찾았습니다. 내 솔루션 getItemId()
에서 각 조각에 대해 고유 ID를 반환 해야하는 것을 재정의 해야합니다. 기본적으로 배치되지 않습니다.
있습니다 :
public class DynamicPagerAdapter extends FragmentPagerAdapter {
private ArrayList<Page> mPages = new ArrayList<Page>();
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
public DynamicPagerAdapter(FragmentManager fm) {
super(fm);
}
public void replacePage(int position, Page page) {
mPages.set(position, page);
notifyDataSetChanged();
}
public void setPages(ArrayList<Page> pages) {
mPages = pages;
notifyDataSetChanged();
}
@Override
public Fragment getItem(int position) {
if (mPages.get(position).mPageType == PageType.FIRST) {
return FirstFragment.newInstance(mPages.get(position));
} else {
return SecondFragment.newInstance(mPages.get(position));
}
}
@Override
public int getCount() {
return mPages.size();
}
@Override
public long getItemId(int position) {
// return unique id
return mPages.get(position).getId();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
while (mFragments.size() <= position) {
mFragments.add(null);
}
mFragments.set(position, fragment);
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
mFragments.set(position, null);
}
@Override
public int getItemPosition(Object object) {
PagerFragment pagerFragment = (PagerFragment) object;
Page page = pagerFragment.getPage();
int position = mFragments.indexOf(pagerFragment);
if (page.equals(mPages.get(position))) {
return POSITION_UNCHANGED;
} else {
return POSITION_NONE;
}
}
}
주의 사항 :이 예에서 FirstFragment
와 SecondFragment
메소드가 추상 클래스 PageFragment를 확장 getPage()
.
나는 wize와 비슷한 것을하고 있지만 내 대답에 따르면 언제든지 두 조각 사이를 바꿀 수 있습니다. Wize 답변을 사용하면 화면 방향을 변경할 때 몇 가지 문제가 있습니다. 이것은 PagerAdapter의 모습입니다 :
public class MyAdapter extends FragmentPagerAdapter
{
static final int NUM_ITEMS = 2;
private final FragmentManager mFragmentManager;
private Fragment mFragmentAtPos0;
private Map<Integer, String> mFragmentTags;
private boolean isNextFragment=false;
public MyAdapter(FragmentManager fm)
{
super(fm);
mFragmentManager = fm;
mFragmentTags = new HashMap<Integer, String>();
}
@Override
public Fragment getItem(int position)
{
if (position == 0)
{
if (isPager) {
mFragmentAtPos0 = new FirstPageFragment();
} else {
mFragmentAtPos0 = new NextFragment();
}
return mFragmentAtPos0;
}
else
return SecondPageFragment.newInstance();
}
@Override
public int getCount()
{
return NUM_ITEMS;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Object obj = super.instantiateItem(container, position);
if (obj instanceof Fragment) {
// record the fragment tag here.
Fragment f = (Fragment) obj;
String tag = f.getTag();
mFragmentTags.put(position, tag);
}
return obj;
}
public void onChange(boolean isNextFragment) {
if (mFragmentAtPos0 == null)
mFragmentAtPos0 = getFragment(0);
if (mFragmentAtPos0 != null)
mFragmentManager.beginTransaction().remove(mFragmentAtPos0).commit();
if (!isNextFragment) {
mFragmentAtFlashcards = new FirstPageFragment();
} else {
mFragmentAtFlashcards = new NextFragment();
}
notifyDataSetChanged();
}
@Override
public int getItemPosition(Object object)
{
if (object instanceof FirstPageFragment && mFragmentAtPos0 instanceof NextFragment)
return POSITION_NONE;
if (object instanceof NextFragment && mFragmentAtPos0 instanceof FirstPageFragment)
return POSITION_NONE;
return POSITION_UNCHANGED;
}
public Fragment getFragment(int position) {
String tag = mFragmentTags.get(position);
if (tag == null)
return null;
return mFragmentManager.findFragmentByTag(tag);
}
}
어댑터 컨테이너 활동에서 구현할 리스너를 첨부 할 때 프래그먼트에 넣습니다.
public class PagerContainerActivity extends AppCompatActivity implements ChangeFragmentListener {
//...
@Override
public void onChange(boolean isNextFragment) {
if (pagerAdapter != null)
pagerAdapter.onChange(isNextFragment);
}
//...
}
그런 다음 조각에서 리스너를 호출 할 때
public class FirstPageFragment extends Fragment{
private ChangeFragmentListener changeFragmentListener;
//...
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
changeFragmentListener = ((PagerContainerActivity) activity);
}
@Override
public void onDetach() {
super.onDetach();
changeFragmentListener = null;
}
//...
//in the on click to change the fragment
changeFragmentListener.onChange(true);
//...
}
그리고 마지막으로 청취자 :
public interface changeFragmentListener {
void onChange(boolean isNextFragment);
}
나는 @wize 와 @mdelolmo 의 대답을 따랐고 해결책을 얻었습니다. 고마워 톤. 그러나 메모리 소비를 개선하기 위해 이러한 솔루션을 약간 조정했습니다.
내가 관찰 한 문제 :
Fragment
교체 된 인스턴스를 저장합니다 . 내 경우에는 파편이 들어서 MapView
비용이 많이 든다고 생각했습니다. 그래서 FragmentPagerPositionChanged (POSITION_NONE or POSITION_UNCHANGED)
대신 Fragment
자체를 유지하고 있습니다.
여기 내 구현이 있습니다.
public static class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {
private SwitchFragListener mSwitchFragListener;
private Switch mToggle;
private int pagerAdapterPosChanged = POSITION_UNCHANGED;
private static final int TOGGLE_ENABLE_POS = 2;
public DemoCollectionPagerAdapter(FragmentManager fm, Switch toggle) {
super(fm);
mToggle = toggle;
mSwitchFragListener = new SwitchFragListener();
mToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mSwitchFragListener.onSwitchToNextFragment();
}
});
}
@Override
public Fragment getItem(int i) {
switch (i)
{
case TOGGLE_ENABLE_POS:
if(mToggle.isChecked())
{
return TabReplaceFragment.getInstance();
}else
{
return DemoTab2Fragment.getInstance(i);
}
default:
return DemoTabFragment.getInstance(i);
}
}
@Override
public int getCount() {
return 5;
}
@Override
public CharSequence getPageTitle(int position) {
return "Tab " + (position + 1);
}
@Override
public int getItemPosition(Object object) {
// This check make sures getItem() is called only for the required Fragment
if (object instanceof TabReplaceFragment
|| object instanceof DemoTab2Fragment)
return pagerAdapterPosChanged;
return POSITION_UNCHANGED;
}
/**
* Switch fragments Interface implementation
*/
private final class SwitchFragListener implements
SwitchFragInterface {
SwitchFragListener() {}
public void onSwitchToNextFragment() {
pagerAdapterPosChanged = POSITION_NONE;
notifyDataSetChanged();
}
}
/**
* Interface to switch frags
*/
private interface SwitchFragInterface{
void onSwitchToNextFragment();
}
}
데모 링크는 여기. https://youtu.be/l_62uhKkLyM
데모 목적으로 2 개의 프래그먼트 TabReplaceFragment
와 2 DemoTab2Fragment
번 위치를 사용했습니다 . 다른 모든 경우에는 DemoTabFragment
인스턴스를 사용하고 있습니다.
설명:
Switch
Activity에서으로 전달 하고 DemoCollectionPagerAdapter
있습니다. 이 스위치의 상태에 따라 올바른 조각이 표시됩니다. 스위치 검사가 변경되면 변수 의 값을로 변경하는 SwitchFragListener
의 onSwitchToNextFragment
메소드를 호출합니다 . POSITION_NONE 에 대해 자세히 알아보십시오 . 이것은 getItem을 무효화하고 거기에 올바른 조각을 인스턴스화하는 논리가 있습니다. 설명이 약간 지저분하면 죄송합니다.pagerAdapterPosChanged
POSITION_NONE
원래 아이디어에 대한 @wize와 @mdelolmo에 다시 한번 감사드립니다.
이것이 도움이 되길 바랍니다. :)
이 구현에 결함이 있는지 알려주세요. 그것은 내 프로젝트에 큰 도움이 될 것입니다.
연구 후 짧은 코드로 솔루션을 찾았습니다. 우선 프래그먼트에서 퍼블릭 인스턴스를 만들고 방향 변경시 프래그먼트가 다시 생성되지 않으면 onSaveInstanceState에서 프래그먼트를 제거하십시오.
@Override
public void onSaveInstanceState(Bundle outState) {
if (null != mCalFragment) {
FragmentTransaction bt = getChildFragmentManager().beginTransaction();
bt.remove(mFragment);
bt.commit();
}
super.onSaveInstanceState(outState);
}
이것이 나의 목표입니다.
먼저 버튼 클릭 이벤트 를 구현하려는 Root_fragment
내부 viewPager
탭을 추가 하십시오 fragment
. 예;
@Override
public Fragment getItem(int position) {
if(position==0)
return RootTabFragment.newInstance();
else
return SecondPagerFragment.newInstance();
}
우선 조각 변경에 RootTabFragment
포함시켜야합니다 FragmentLayout
.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_frame"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
그런 다음, 내부 RootTabFragment
onCreateView
구현 fragmentChange
을 위해FirstPagerFragment
getChildFragmentManager().beginTransaction().replace(R.id.root_frame, FirstPagerFragment.newInstance()).commit();
그런 onClick
다음 버튼 내부의 이벤트를 구현 FirstPagerFragment
하고 조각을 다시 변경하십시오.
getChildFragmentManager().beginTransaction().replace(R.id.root_frame, NextFragment.newInstance()).commit();
이것이 당신에게 도움이되기를 바랍니다.
참고 URL : https://stackoverflow.com/questions/7723964/replace-fragment-inside-a-viewpager
'Programing' 카테고리의 다른 글
Android 및 iOS 용 2D 플랫폼 간 게임 엔진? (0) | 2020.04.01 |
---|---|
AsyncTask가 실제로 개념적으로 결함이 있습니까? 아니면 뭔가 빠졌습니까? (0) | 2020.04.01 |
Spring의 ApplicationContext.getBean이 왜 나쁜 것으로 간주됩니까? (0) | 2020.04.01 |
maven-shade-plugin은 무엇이며 Java 패키지를 재배치하려는 이유는 무엇입니까? (0) | 2020.04.01 |
단어 / 문자열을 제외하는 정규식 (0) | 2020.04.01 |