ViewPager가 콘텐츠를 다시 그리지 않고 그대로 유지 / 비어 있음


86

우리는 여기 ViewPager와 관련하여 매우 이상한 문제로 고통 받고 있습니다. 각 ViewPager 페이지에 목록을 포함하고 목록 데이터를 업데이트 할 때 목록 어댑터와 뷰 페이저 어댑터 모두에서 notifyDataSetChanged를 트리거합니다.

우리가 관찰 한 것은 때때로 페이지가 뷰 트리를 업데이트하지 않는다는 것입니다. 즉, 빈 상태로 유지되거나, 페이지를 페이징 할 때 사라지기도합니다. 몇 번 앞뒤로 페이징하면 콘텐츠가 갑자기 다시 나타납니다. Android에보기 업데이트가 누락 된 것 같습니다. 또한 계층 뷰어로 디버깅 할 때 뷰를 선택하면 뷰가 항상 다시 나타납니다. 계층 뷰어가 선택한 뷰를 강제로 다시 그리도록하기 때문입니다.

그래도이 작업을 프로그래밍 방식으로 만들 수는 없습니다. 목록보기 또는 전체보기 호출기를 무효화해도 효과가 없었습니다.

이것은 compatibility-v4_r7 라이브러리입니다. 나는 또한 뷰 페이저와 관련된 많은 문제를 해결한다고 주장하기 때문에 최신 개정판을 사용하려고 시도했지만 문제를 더 악화 시켰습니다.

다른 사람도 이러한 문제를 겪고 있습니까? 아니면이 문제의 원인이 무엇인지 알고 있습니까?

답변:


44

마침내 해결책을 찾았습니다. 분명히 우리의 구현에는 두 가지 문제가 있습니다.

  1. 어댑터가에서보기를 제거하지 않았습니다 destroyItem().
  2. 레이아웃을 한 번만 확장해야하도록 뷰를 캐싱하고에서 뷰를 제거하지 않았기 때문에 뷰를 destroyItem()추가하지 않고 instantiateItem()현재 위치에 해당하는 캐시 된 뷰를 반환했습니다.

나는 소스 코드를 너무 깊이 들여다 보지 않았고 ViewPager-당신이 그것을해야한다는 것이 명확하지 않지만 문서에는 다음과 같이 말합니다.

destroyItem ()
주어진 위치에 대한 페이지를 제거합니다. 어댑터는 해당 컨테이너에서 뷰를 제거해야하지만 finishUpdate (ViewGroup)에서 반환 될 때까지이 작업이 수행되도록해야합니다.

과:

매우 간단한 PagerAdapter는 페이지 뷰 자체를 키 개체로 사용하도록 선택할 수 있으며, 생성 후 instantiateItem (ViewGroup, int)에서 반환하고 부모 ViewGroup에 추가 할 수 있습니다. 일치하는 destroyItem (ViewGroup, int, Object) 구현은 상위 ViewGroup에서 View를 제거하고 isViewFromObject (View, Object)는 return view == object;로 구현 될 수 있습니다.

내 결론은 그래서 ViewPager명시 적으로 추가 /에서 아이를 제거하기 위해 기본 어댑터에 의존 instantiateItem()/ destroyItem(). 즉, 어댑터가의 하위 클래스 인 PagerAdapter경우 하위 클래스는이 논리를 구현해야합니다.

참고 : 내부 목록을 사용 하는 경우이 점에 유의하세요 ViewPager.


2
나는 같은 문제가 있지만 나를 위해 onDestroy를 처리하는 FragmentPagerAdapter를 사용합니다. 여기에있는 다른 솔루션 중 어느 것도 작동하지 않습니다.
Greg Ennis 2014

14
또한 문제가 될 수있는 것은 누군가가 GetChildFragmentManager 대신 getFragmentManger를 사용하고 있다는 것입니다
Boy

@Boy GetChildFragmentManager는 무엇입니까?
구멍에 불

1
@Boy Wooooow .... 나는 왜 내가 아무것도 업데이트 할 수 없는지 알아 내려고 이틀 동안 머리를 뽑았다. 감사합니다! (그들은 실제로 childfragmentmanager를 사용하기 위해 Google 문서에 알림을 게시해야합니다. 이는 전적으로 다른 관리자가 필요하다는 것이 아닙니다).
user0721090601

@futtetennista 저도 똑같이하고 있습니다 ..이 문제에 직면 해 있습니다 .. 확인할 수 있습니다 .. stackoverflow.com/questions/61727835/…
AskQ

55

이 경우 ViewPager기호가있는 조각 내부 설정 FragmentPagerAdapter, 사용하는 getChildFragmentManager()대신에 getSupportFragmentManager()당신의 초기화 매개 변수로 FragmentPagerAdapter.

mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());

대신에

mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());

2
FragmentPagerAdapter를 사용하면 좋은 캐치, 내 문제를 해결하는 것
Tobliug

1
감사합니다. 나는 강제로하려고했던 getItem()A의 FragmentPagerAdapter재생성 된 조각에 중첩 된.
kosiara-Bartosz Kosarzycki

와우이 작품은 매력적입니다. 나는 문제가 조각에서
뷰 페이저를 부풀리기

이럴 때 나는 우리가 미디엄처럼 박수를 쳤 으면 좋겠다. 그래서 나는 이것에 대해 1000+ 박수를 줄 수 있었다. 좋은 일, 이것은 허용 대답해야한다
Codelicious

21

나는 똑같은 문제가 있었지만 실제로 destroyItem에서 뷰를 파괴했습니다 (생각했습니다). 그러나 문제는 내가 viewPager.removeViewAt(index);insted ofviewPager.removeView((View) object);

잘못된:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeViewAt(position);
}

권리:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeView((View) object);
}

힌트 주셔서 감사합니다. 꽤 오랫동안 문제를 조사했습니다.
Moritz

2
이것은 나를 위해 일했지만 첫 번째 문제가 왜 문제인지 아십니까?
HannahMitt 2014-07-01

@HannahMitt removeViewAt은 ViewPager의 현재 자식 목록 내에서 특정 위치에있는 모든 뷰를 제거하고 페이지는 임의의 순서로 ViewPager에 추가 될 수 있습니다 (따라서 페이지 0은 실제로 인덱스 1의 ViewPager에있을 수 있습니다). removeView는 자식 목록을 반복하고 지정된 개체를 정확히 제거합니다.

2
나를 위해 작동하지 않음 : 뷰를 조각으로 캐스팅 할 수 없습니다. 여기서 개체는 조각입니다.
FRK

viewpager는 정적이어야합니까?
Kanagalingam

10

ViewPager는 항목 재사용과 관련하여 영리한 작업을 시도하지만 변경된 경우 새 항목 위치를 반환해야합니다. PagerAdapter에 다음을 추가해보십시오.

public int getItemPosition (Object object) { return POSITION_NONE; }

기본적으로 ViewPager에 모든 것이 변경되었음을 알리고 모든 것을 다시 인스턴스화하도록합니다. 그게 내 머릿속에서 생각할 수있는 유일한 것입니다.


1
예,이 스레드에서이 옵션에 대해 읽었습니다. stackoverflow.com/questions/7263291/…- 그러나 이것은 큰 망치 접근 방식처럼 보입니다. 확실히 더 우아한 방법이 있어야할까요? 목록을 호출기 페이지로 사용하는 것과 관련이 있는지 궁금합니다.
Matthias

약간의 쇠 망치 접근 방식이지만 그로부터 다시 작업 할 수 있습니다. 즉, 메서드에서 올바른 값을 반환합니다.
Chris Banes

@ChrisBanes 당신이 전화라고 말했듯이 return POSITION_NONE;, forces it to re-instantiate everything내 경우에는 모든 것을 다시 인스턴스화 하고 싶지 않으므로 view기존 항목을 지우지 않고 제거 할 수 있습니까? 알려주세요
Ritesh Adulkar에게

당신은 삶의 맛이다
영역 8

2

너무 많은 솔루션을 시도했지만 예기치 않게 viewPager.post()작동했습니다.

 mAdapter = new NewsVPAdapter(getContext(), articles);
    viewPager.post(new Runnable() {
        @Override
        public void run() {
            viewPager.setAdapter(mAdapter);
        }
    });

1
맙소사. 왜 아무도 이것을 찬성하지 않았습니까? 조각을 다시 입력하고 내부 뷰 페이저가 자체적으로 렌더링되지 않을 때 이상한 동작이 발생했으며 이와 같이 약간 지연하면 실제로 문제가 해결되었습니다!
Fugogugo

0

Android 지원 라이브러리에는 모든 페이지에 ListView가있는 ViewPager가 포함 된 데모 활동이 있습니다. 당신은 아마 그것을보고 그것이 무엇을하는지보아야 할 것입니다.

Eclipse에서 (Android Dev Tools r20 사용) :

  1. 고르다 New > Android Sample Project
  2. 대상 API 레벨을 선택하십시오 (사용 가능한 최신 API를 제안합니다).
  3. 고르다 Support4Demos
  4. 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 Android Tools > Add Support Library
  5. 앱을 실행하고 선택 FragmentPager

이에 대한 코드는 src/com.example.android.supportv4.app/FragmentPagerSupport.java. 행운을 빕니다!


고마워요. 제가 살펴 볼게요! 아마도 나는 우리가 잘못하고있는 것을 발견 할 것입니다.
Matthias

0

나는 이것에 부딪 쳤고 매우 유사한 문제가 있었다. 나는 심지어 스택 오버플로에 대해 물었다 .

나를 위해, 내 부모부모 에서 누군가 를 호출하지 않고 하위 클래스를 지정 LinearLayout하고 덮어 썼습니다 . 이것은 내 ViewPager에서 호출되는 것을 방지 하고 호출 하지 못했습니다 (hierarchyviewer가 수동으로 호출하지만). 측정되지 않으면 ViewPager에서 공백으로 표시됩니다.requestLayout()super.requestLayout()onMeasureonLayout

따라서 포함하는 뷰를 확인하십시오. View에서 하위 클래스인지 확인하고 requestLayout 또는 이와 유사한 것을 맹목적으로 재정의하지 마십시오.


0

같은 문제가 있었는데, 이는 ListView목록이 비어 있으면 내 빈 뷰가 제대로 표시되기 때문에 관련이 있습니다. 방금 requestLayout()문제가있는 전화 를 걸었 습니다 ListView. 이제 잘 그려집니다!


0

ViewPager 및 FragmentStatePagerAdapter를 사용할 때 이와 동일한 문제가 발생했습니다. 3 초 지연된 핸들러를 사용하여 invalidate () 및 requestLayout ()을 호출하려고 시도했지만 작동하지 않았습니다. 작업 한 것은 다음과 같이 viewPager의 배경색을 재설정하는 것입니다.

MyFragment.java

    private Handler mHandler;
    private Runnable mBugUpdater;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = new ViewPager(getActivity());
        //...Create your adapter and set it here...

        mHandler = new Handler();
        mBugUpdater = new Runnable(){
            @Override
            public void run() {
                mVp.setBackgroundColor(mItem.getBackgroundColor());
                mHandler = null;
                mBugUpdater = null;
            }           
        };
        mHandler.postDelayed(mBugUpdater,50);

        return rootView;
    }

    @Override
    public void onPause() {
        if(mHandler != null){
            //Remove the callback if it hasn't triggered yet
            mHandler.removeCallbacks(mBugUpdater);
            mHandler = null;
            mBugUpdater = null;
        }
        super.onPause();
     }

0

나는 같은 증상으로 문제가 있었지만 다른 원인으로 인해 어리석은 실수로 판명되었습니다. 누군가에게 도움이 될 경우를 대비하여 여기에 추가 할 것이라고 생각했습니다.

두 개의 조각이 있던 FragmentStatePagerAdapter를 사용하는 ViewPager가 있었지만 나중에 세 번째 조각을 추가했습니다. 그러나 기본 오프 스크린 페이지 제한이 1이라는 것을 잊었으므로 새 세 번째 조각으로 전환하면 첫 번째 조각이 파괴되고 다시 전환 한 후 다시 생성됩니다. 문제는 내 활동이 이러한 조각에 UI 상태를 초기화하도록 알리는 일을 담당한다는 것입니다. 이것은 액티비티와 프래그먼트 라이프 사이클이 동일 할 때 작동했지만이를 수정하려면 프래그먼트를 변경하여 시작 라이프 사이클 동안 자체 UI를 초기화해야했습니다. 결국 나는 setOffscreenPageLimit을 2로 변경하여 세 조각 모두 항상 살아 있도록했습니다 (이 경우 메모리 집약적이지 않았기 때문에 안전함).


-1

비슷한 문제가있었습니다. 에서 3 개의 뷰만 필요하기 때문에 뷰를 캐시합니다 ViewPager. 앞으로 슬라이드하면 모든 것이 괜찮지 만 뒤로 슬라이드를 시작하면 오류가 발생하고 "내 뷰에 이미 부모가 있습니다"라는 메시지가 표시됩니다. 해결책은 불필요한 항목을 수동으로 삭제하는 것입니다.

@Override
    public Object instantiateItem(ViewGroup container, int position) {
        int localPos = position % SIZE;
        TouchImageView view;
        if (touchImageViews[localPos] != null) {
            view = touchImageViews[localPos];
        } else {
            view = new TouchImageView(container.getContext());
            view.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            touchImageViews[localPos] = view;
        }
        view.setImageDrawable(mDataModel.getPhoto(position));
        Log.i(IRViewPagerAdpt.class.toString(), "Add view " + view.toString() + " at pos: " + position + " " + localPos);
        if (view.getParent() == null) {
        ((ViewPager) container).addView(view);
    }
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object view) {
        //      ((ViewPager) container).removeView((View) view);
        Log.i(IRViewPagerAdpt.class.toString(), "remove view " + view.toString() + " at pos: " + position);
    }

..................

private static final int SIZE = 3;
private TouchImageView[] touchImageViews = new TouchImageView[SIZE];

-1

나에게 문제는 앱 프로세스가 종료 된 후 활동으로 돌아 오는 것이었다. Android 소스에서 수정 된 사용자 정의보기 호출기 어댑터를 사용하고 있습니다.보기 호출기는 활동에 직접 포함됩니다.

부름 viewPager.setCurrentItem(position, true);

(애니메이션 포함) 데이터 및 notifyDataSetChanged () 설정 후 작동하는 것처럼 보이지만 매개 변수가 false로 설정되면 작동하지 않고 조각이 비어 있습니다. 이것은 누군가에게 도움이 될 수있는 엣지 케이스입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.