Programing

ViewPager의 상위 Fragment가 숨겨져 표시되면 ViewPager의 Fragment의 뷰가 손실 됨

lottogame 2020. 11. 21. 08:21
반응형

ViewPager의 상위 Fragment가 숨겨져 표시되면 ViewPager의 Fragment의 뷰가 손실 됨


내 자신의 FragmentStatePagerAdapter와 함께 ViewPager에서 이상한 동작을 보았습니다.

내보기 계층 구조는 다음과 같습니다.

-> (1) Fragment root view (RelativeLayout)
 -> (2) ViewPager
  -> (3) ViewPager's current fragment view

Fragment 루트 뷰 (1)를 담당하는 Fragment가 숨겨지고 (조각 트랜잭션에서 .hide () 사용) 표시되면 (.show () 사용) 현재 ViewPager에 표시되고 있던 조각보기 (3 )은 조각이 여전히 존재하지만 null이됩니다. 기본적으로 내 ViewPager는 완전히 비어 있거나 투명 해집니다.

이 문제를 해결할 수있는 유일한 방법은

int current = myViewPager.getCurrentItem();
myViewPager.setAdapter(myAdapter);
myViewPager.setCurrentItem(current);

부모 조각이 표시된 후. 이로 인해 뷰가 다시 생성되고 화면에 표시됩니다. 불행히도 이로 인해 unregisterDataSetObserver()이전 관찰자에서 두 번 호출하는 호출기 어댑터를 처리하는 예외가 발생하는 경우가 있습니다.

이 작업을 수행하는 더 좋은 방법이 있습니까? 내가 묻는 것은 다음과 같습니다.

ViewPager의 부모 조각이 숨겨 졌을 때 ViewPager 내부의 조각보기가 파괴되는 이유는 무엇입니까?

업데이트 : 이것은 응용 프로그램이 "최소화"된 다음 "복원"될 때도 발생합니다 (홈 액션 키를 누른 다음 복귀).

요청에 따라 다음은 내 호출기 어댑터 클래스입니다.

public class MyInfoSlidePagerAdapter extends FragmentStatePagerAdapter {

    private ArrayList<MyInfo> infos = new ArrayList<MyInfo>();

    public MyInfoSlidePagerAdapter (FragmentManager fm) {
        super(fm);
    }

    public MyInfoSlidePagerAdapter (FragmentManager fm, MyInfo[] newInfos) {
        super(fm);
        setInfos(newInfos);
    }

    @Override
    public int getItemPosition(Object object) {
        int position = infos.indexOf(((MyInfoDetailsFragment)object).getMyInfo());
        return position > 0 ? position : POSITION_NONE;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return infos.get(position).getName();
    }

    @Override
    public Fragment getItem(int i) {
        return infos.size() > 0 ? MyInfoDetailsFragment.getNewInstance(infos.get(i)) : null;
    }

    @Override
    public int getCount() {
        return infos.size();
    }

    public Location getMyInfoAtPosition(int i) {
        return infos.get(i);
    }

    public void setInfos(MyInfo[] newInfos) {
        infos = new ArrayList<MyInfo>(Arrays.asList(newInfos));
    }

    public int getPositionOfMyInfo(MyInfo info) {
        return infos.indexOf(info);
    }
}

일부 변수의 이름을 변경했지만 그 외에는 정확히 내가 가진 것입니다.


특정 문제에 대한 충분한 정보를 제공하지 않았으므로 문제를 재현하려는 샘플 프로젝트를 만들었습니다. 앱에는 PagerFragment상대 레이아웃 내에 조각 ( ) 을 보유하는 활동 이 있으며이 레이아웃 아래에는 숨겨지는 버튼이 있습니다. & 위에 표시 PagerFragment됩니다. PagerFragmentViewPager이 조각의 이름은 - 호출기 어댑터 내에서 각 조각을 단순히 레이블을 표시합니다 DataFragment. 레이블 목록은 상위 활동에서 생성되고 PagerFragment에 전달 된 다음 해당 어댑터를 통해 각 DataFragment. PagerFragment가시성 변경은 문제없이 수행되며 다시 표시 될 때마다 이전에 표시된 레이블이 표시됩니다.

문제의 핵심 : 뷰 페이저 어댑터를 만들 때 getFragmentManager가 아닌 Fragment # getChildFragmentManager ()를 사용하십시오 !

이 간단한 프로젝트를 가지고있는 것과 비교하여 차이점이 어디에 있는지 확인할 수 있습니다. 그래서 여기에 간다 (하향식) :

PagerActivity (프로젝트의 유일한 활동) :

public class PagerActivity extends FragmentActivity {

    private static final String PAGER_TAG = "PagerActivity.PAGER_TAG";

    @Override
    protected void onCreate(Bundle savedInstance) {
        super.onCreate(savedInstance);
        setContentView(R.layout.pager_activity);
        if (savedInstance == null) {
            PagerFragment frag = PagerFragment.newInstance(buildPagerData());
            FragmentManager fm = getSupportFragmentManager();
            fm.beginTransaction().add(R.id.layout_fragments, frag, PAGER_TAG).commit();
        }
        findViewById(R.id.btnFragments).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                changeFragmentVisibility();
            }
        });
    }

    private List<String> buildPagerData() {
        ArrayList<String> pagerData = new ArrayList<String>();
        pagerData.add("Robert de Niro");
        pagerData.add("John Smith");
        pagerData.add("Valerie Irons");
        pagerData.add("Metallica");
        pagerData.add("Rammstein");
        pagerData.add("Zinedine Zidane");
        pagerData.add("Ronaldo da Lima");
        return pagerData;
    }

    protected void changeFragmentVisibility() {
        Fragment frag = getSupportFragmentManager().findFragmentByTag(PAGER_TAG);
        if (frag == null) {
            Toast.makeText(this, "No PAGER fragment found", Toast.LENGTH_SHORT).show();
            return;
        }
        boolean visible = frag.isVisible();
        Log.d("APSampler", "Pager fragment visibility: " + visible);
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        if (visible) {
            ft.hide(frag);
        } else {
            ft.show(frag);
        }
        ft.commit();
        getSupportFragmentManager().executePendingTransactions();
    }
}

레이아웃 파일 pager_activity.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="4dp" >

    <Button
        android:id="@+id/btnFragments"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="Hide/Show fragments" />

    <RelativeLayout
        android:id="@+id/layout_fragments"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/btnFragments"
        android:layout_marginBottom="4dp" >
    </RelativeLayout>

</RelativeLayout>

PagerFragment활동이 처음 표시 되는 시기와 PagerFragment클래스를 추가하고 있는지 확인 합니다 .

public class PagerFragment extends Fragment {

    private static final String DATA_ARGS_KEY = "PagerFragment.DATA_ARGS_KEY";

    private List<String> data;

    private ViewPager pagerData;

    public static PagerFragment newInstance(List<String> data) {
        PagerFragment pagerFragment = new PagerFragment();
        Bundle args = new Bundle();
        ArrayList<String> argsValue = new ArrayList<String>(data);
        args.putStringArrayList(DATA_ARGS_KEY, argsValue);
        pagerFragment.setArguments(args);
        return pagerFragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        data = getArguments().getStringArrayList(DATA_ARGS_KEY);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.pager_fragment, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        pagerData = (ViewPager) view.findViewById(R.id.pager_data);
        setupPagerData();
    }

    private void setupPagerData() {
        PagerAdapter adapter = new LocalPagerAdapter(getChildFragmentManager(), data);
        pagerData.setAdapter(adapter);
    }

}

레이아웃 (전체 크기를 사용하는 ViewPager 만 해당) :

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

및 해당 어댑터 :

public class LocalPagerAdapter extends FragmentStatePagerAdapter {
    private List<String> pagerData;

    public LocalPagerAdapter(FragmentManager fm, List<String> pagerData) {
        super(fm);
        this.pagerData = pagerData;
    }

    @Override
    public Fragment getItem(int position) {
        return DataFragment.newInstance(pagerData.get(position));
    }

    @Override
    public int getCount() {
        return pagerData.size();
    }
}

이 어댑터 DataFragment는 각 페이지에 대해 다음을 작성합니다 .

public class DataFragment extends Fragment {
    private static final String DATA_ARG_KEY = "DataFragment.DATA_ARG_KEY";

    private String localData;

    public static DataFragment newInstance(String data) {
        DataFragment df = new DataFragment();
        Bundle args = new Bundle();
        args.putString(DATA_ARG_KEY, data);
        df.setArguments(args);
        return df;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        localData = getArguments().getString(DATA_ARG_KEY);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.data_fragment, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        view.findViewById(R.id.btn_page_action).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Toast.makeText(getActivity(), localData, Toast.LENGTH_SHORT).show();
            }
        });
        ((TextView) view.findViewById(R.id.txt_label)).setText(localData);
    }
}

DataFragment의 레이아웃 :

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="4dp" >

    <Button
        android:id="@+id/btn_page_action"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="Interogate" />

    <TextView
        android:id="@+id/txt_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</RelativeLayout>

코딩을 즐기십시오!


아마도 mViewPager.setOffscreenPageLimit (5)에 도움이 될 것입니다.

Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. Pages beyond this limit will be recreated from the adapter when needed.

This is offered as an optimization. If you know in advance the number of pages you will need to support or have lazy-loading mechanisms in place on your pages, tweaking this setting can have benefits in perceived smoothness of paging animations and interaction. If you have a small number of pages (3-4) that you can keep active all at once, less time will be spent in layout for newly created view subtrees as the user pages back and forth.

You should keep this limit low, especially if your pages have complex layouts. This setting defaults to 1.


View Pager is pretty adamant in keeping keeping its Fragments fresh always and thus optimizing the performance by freeing up memory when a fragment is not used. Clearly that is a valid useful trait in a mobile system. But due to this persistent deallocation of resources the fragment is created everytime it gains focus.

mViewPager.setOffscreenPageLimit(NUMBEROFFRAGMENTSCREENS);

Here is the documentation.

this Old Post has an interesting Solution for your problem.. Please Refer


For me i changed to getChildFragmentManager() instead of getFragmentManager() and works good. Ex:

pagerAdapt = new PagerAdapt(getChildFragmentManager());

I had the same problem. My app (FragmentActivity) has a pager (ViewPager) with 3 framgents. While swiping between the fragments they are destroyed and recreated all the time. Actually it makes no problem in functionality (expect unclosed Cursors), but I was also wondering about this question.

I do not know if there is a workaround to change the behavior of the ViewPager, but I suggest to have a configuration object (maybe a static on) and before destroy save your myViewPager object at the config object.

public class App extends FragmentActivity {

    static MyData data;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
         data = (MyData) getLastCustomNonConfigurationInstance();
         if (data == null) {
             data = new MyData();
             data.savedViewPager = myViewPager;
         } else {
             myViewPager = data.savedViewPager;
        }
    }

    @Override
    public Object onRetainCustomNonConfigurationInstance() {
        Log.d("onRetainCustomNonConfigurationInstance", "Configuration call");
        return data;
    }
}

public class MyData {
    public ViewPager savedViewPager;
}

With this way, you can save the reference to the an object which won't be destroyed hence there is reference to it and you can reload all your crucial objects.

I hope you find my suggestion useful!

참고URL : https://stackoverflow.com/questions/18386254/viewpagers-fragments-view-lost-when-viewpagers-parent-fragment-hidden-then-sh

반응형