ViewPager không vẽ lại nội dung, vẫn / để trống


86

Chúng tôi đang gặp phải một vấn đề rất lạ với ViewPager ở đây. Chúng tôi nhúng danh sách trên mỗi trang ViewPager và kích hoạttifyDataSetChanged trên cả bộ điều hợp danh sách và bộ điều hợp máy nhắn tin khi cập nhật dữ liệu danh sách.

Những gì chúng tôi quan sát là đôi khi, trang không cập nhật cây xem của nó, tức là vẫn trống, hoặc đôi khi thậm chí biến mất khi phân trang đến nó. Khi phân trang qua lại một vài lần, nội dung sẽ đột ngột xuất hiện trở lại. Có vẻ như Android đang thiếu bản cập nhật chế độ xem ở đây. Tôi cũng nhận thấy rằng khi gỡ lỗi với trình xem phân cấp, việc chọn một chế độ xem sẽ luôn làm cho nó xuất hiện lại, có vẻ như vì trình xem phân cấp buộc chế độ xem đã chọn tự vẽ lại.

Mặc dù vậy, tôi không thể làm cho việc này hoạt động theo chương trình; làm vô hiệu chế độ xem danh sách, hoặc thậm chí toàn bộ chế độ xem, không có tác dụng.

Đây là với thư viện tương thích-v4_r7. Tôi cũng đã cố gắng sử dụng bản sửa đổi mới nhất, vì nó tuyên bố sẽ khắc phục nhiều sự cố liên quan đến chế độ xem máy nhắn tin, nhưng nó còn khiến vấn đề trở nên tồi tệ hơn (ví dụ: cử chỉ bị hỏng nên đôi khi nó không cho phép tôi lướt qua tất cả các trang nữa).

Có ai khác đang gặp phải những vấn đề này không, hoặc bạn có ý tưởng về điều gì có thể gây ra điều này?

Câu trả lời:


44

Cuối cùng chúng tôi đã tìm ra giải pháp. Rõ ràng việc triển khai của chúng tôi gặp phải hai vấn đề:

  1. bộ điều hợp của chúng tôi đã không xóa chế độ xem trong destroyItem().
  2. chúng tôi đã lưu các chế độ xem vào bộ nhớ đệm để chúng tôi phải tăng cường bố cục của mình chỉ một lần và vì chúng tôi không xóa chế độ xem trong destroyItem(), chúng tôi không thêm nó vào instantiateItem()mà chỉ trả lại chế độ xem được lưu trong bộ nhớ cache tương ứng với vị trí hiện tại.

Tôi chưa xem xét quá sâu về mã nguồn của ViewPager- và không rõ ràng là bạn phải làm điều đó - nhưng tài liệu cho biết:

killItem ()
Xóa một trang cho vị trí đã cho. Bộ điều hợp chịu trách nhiệm xóa chế độ xem khỏi vùng chứa của nó, mặc dù nó chỉ phải đảm bảo điều này được thực hiện vào thời điểm nó trở về từ finishUpdate (ViewGroup).

và:

Một PagerAdapter rất đơn giản có thể chọn sử dụng chính các View của trang làm đối tượng chính, trả lại chúng từ InstantiateItem (ViewGroup, int) sau khi tạo và thêm chúng vào ViewGroup mẹ. Việc triển khai DestItem (ViewGroup, int, Object) phù hợp sẽ xóa View khỏi ViewGroup mẹ và isViewFromObject (View, Object) có thể được triển khai dưới dạng đối tượng return view == ;.

Vì vậy, kết luận của tôi là ViewPagerdựa vào bộ điều hợp cơ bản của nó để thêm / xóa rõ ràng các con của nó trong instantiateItem()/ destroyItem(). Nghĩa là, nếu bộ điều hợp của bạn là một lớp con của PagerAdapter, lớp con của bạn phải triển khai logic này.

Lưu ý phụ: hãy lưu ý điều này nếu bạn sử dụng danh sách bên trong ViewPager.


2
Tôi đang gặp vấn đề tương tự nhưng với FragmentPagerAdapter, xử lý onDestroy cho tôi. Không có giải pháp nào khác ở đây hoạt động.
Greg Ennis

14
Điều gì cũng có thể là vấn đề, là ai đó đang sử dụng getFragmentManger thay vì GetChildFragmentManager
Boy

@Boy GetChildFragmentManager là gì?
lửa trong lỗ

1
@Boy Wooooow .... Tôi đã vò đầu bứt tóc trong suốt hai ngày để cố gắng tìm ra lý do tại sao tôi không thể cập nhật bất cứ điều gì. CẢM ƠN BẠN! (họ thực sự nên đặt thông báo trên tài liệu của Google để sử dụng trình quản lý phân mảnh con, điều đó hoàn toàn không phải là bạn cần một người quản lý khác).
user0721090601

@futtetennista Tôi cũng đang làm vậy .. đang đối mặt với vấn đề này .. bạn có thể kiểm tra .. stackoverflow.com/questions/61727835/…
AskQ

55

Nếu ViewPagerđược đặt bên trong Fragment với a FragmentPagerAdapter, hãy sử dụng getChildFragmentManager()thay vì getSupportFragmentManager()làm tham số để khởi tạo của bạn FragmentPagerAdapter.

mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());

Thay vì

mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());

2
Tốt bắt, có vẻ như để giải quyết vấn đề của tôi khi sử dụng một FragmentPagerAdapter
Tobliug

1
Cảm ơn - điều này đã hiệu quả. Tôi đang cố gắng ép getItem()vào một FragmentPagerAdaptercái được lồng trong một Fragment được tạo lại.
kosiara - Bartosz Kosarzycki

wow công việc này giống như một sự quyến rũ. tôi đoán vấn đề là do người xem bị thổi phồng trong một phân đoạn
Thecarisma

Những lúc như thế này, tôi ước chúng ta có những cái vỗ tay như Medium để tôi có thể cho bạn hơn 1000 lần vỗ tay vì điều này. Đẹp công việc, điều này sẽ là câu trả lời được chấp nhận
Codelicious

21

Tôi cũng gặp phải vấn đề tương tự nhưng tôi đã thực sự phá hủy chế độ xem trong killItem (tôi nghĩ vậy). Tuy nhiên Vấn đề là tôi đã phá hủy nó bằng cách sử dụng viewPager.removeViewAt(index);insted củaviewPager.removeView((View) object);

Sai lầm:

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

Đúng:

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

Cảm ơn cho gợi ý. Đã xem xét vấn đề trong một thời gian khá dài.
Moritz

2
Điều này đã làm việc cho tôi, nhưng bạn có biết tại sao cái đầu tiên lại là một vấn đề không?
HannahMitt

@HannahMitt removeViewAt sẽ xóa bất kỳ chế độ xem nào ở vị trí cụ thể đó trong danh sách con hiện tại của ViewPager và các trang có thể được thêm vào ViewPager theo bất kỳ thứ tự nào (vì vậy trang 0 thực sự có thể nằm trong ViewPager ở chỉ mục 1 hoặc bất kỳ thứ gì). removeView sẽ lặp qua danh sách con và loại bỏ chính xác đối tượng được chỉ định.

2
Không hoạt động đối với tôi: Không thể truyền chế độ xem thành phân mảnh. đối tượng ở đây là một mảnh.
FRK

viewpager phải tĩnh?
Kanagalingam

10

ViewPager cố gắng thực hiện những điều thông minh xung quanh việc tái sử dụng các mục, nhưng nó yêu cầu bạn trả lại vị trí mục mới khi mọi thứ đã thay đổi. Hãy thử thêm cái này vào PagerAdapter của bạn:

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

Về cơ bản, nó cho ViewPager biết rằng mọi thứ đã thay đổi (và buộc nó phải khởi tạo lại mọi thứ). Đó là điều duy nhất tôi có thể nghĩ ra ngoài đầu mình.


1
Có, tôi đã đọc về tùy chọn này trong chủ đề này: stackoverflow.com/questions/7263291/… - tuy nhiên, điều này có vẻ giống như cách tiếp cận búa tạ. Chắc chắn phải có một cách thanh lịch hơn? Tôi tự hỏi liệu nó có liên quan đến việc sử dụng danh sách làm trang máy nhắn tin không?
Matthias,

Đó là một cách tiếp cận hơi giống búa tạ, nhưng bạn có thể làm việc trở lại từ đó. tức là trả về một giá trị đúng từ phương thức.
Chris Banes,

@ChrisBanes Như bạn đã nói là đang gọi điện return POSITION_NONE;, forces it to re-instantiate everythingnhưng trong trường hợp của tôi, tôi không muốn khởi tạo lại mọi thứ , vì vậy có thể xóa viewmà không xóa nội dung hiện có không? Vui lòng cho tôi biết
Ritesh Adulkar

bạn là người
cứu rỗi

2

Cố gắng quá nhiều giải pháp nhưng không ngờ viewPager.post()làm việc

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

1
Chúa tôi. tại sao không ai ủng hộ cái này? Tôi có hành vi kỳ lạ khi tôi nhập lại một phân đoạn và khung nhìn bên trong không tự hiển thị và việc trì hoãn nó một chút như thế này thực sự đã giải quyết được vấn đề!
Fugogugo

0

Thư viện hỗ trợ Android có Hoạt động demo bao gồm ViewPager với ListView trên mọi trang. Bạn có lẽ nên có một cái nhìn và xem những gì nó làm.

Trong Eclipse (với Android Dev Tools r20):

  1. Lựa chọn New > Android Sample Project
  2. Chọn cấp API mục tiêu của bạn (tôi đề nghị cấp mới nhất hiện có)
  3. Lựa chọn Support4Demos
  4. Nhấp chuột phải vào dự án và chọn Android Tools > Add Support Library
  5. Chạy ứng dụng và chọn Fragment, sau đóPager

Mã cho điều này là trong src/com.example.android.supportv4.app/FragmentPagerSupport.java. Chúc may mắn!


cảm ơn - Tôi sẽ xem qua! Có lẽ tôi sẽ phát hiện ra điều gì đó mà chúng tôi đang làm sai.
Matthias,

0

Tôi gặp phải vấn đề này và có những vấn đề tương tự. Tôi thậm chí đã hỏi nó về tràn ngăn xếp.

Đối với tôi, trong cha mẹ của cha mẹ của chế độ xem của tôi, ai đó đã phân lớp con LinearLayoutvà ghi đè requestLayout()mà không gọi super.requestLayout(). Điều này đã ngăn onMeasureonLayoutkhông được gọi trên ViewPager của tôi (mặc dù trình xem phân cấp gọi chúng theo cách thủ công). Nếu không được đo lường, chúng sẽ hiển thị dưới dạng trống trong ViewPager.

Vì vậy, hãy kiểm tra các chế độ xem có chứa của bạn. Đảm bảo rằng chúng phân lớp từ View và không ghi đè một cách mù quáng requestLayout hoặc bất kỳ thứ gì tương tự.


0

Có cùng một vấn đề, đó là điều phải làm với ListView(vì chế độ xem trống của tôi hiển thị tốt nếu danh sách trống). Tôi vừa gọi requestLayout()về vấn đề ListView. Bây giờ nó vẽ tốt!


0

Tôi đã gặp phải vấn đề tương tự này khi sử dụng ViewPager và FragmentStatePagerAdapter. Tôi đã thử sử dụng một trình xử lý có độ trễ 3 giây để gọi voidate () và requestLayout () nhưng nó không hoạt động. Những gì đã làm là đặt lại màu nền của viewPager như sau:

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

Tôi đã gặp vấn đề với các triệu chứng tương tự, nhưng một nguyên nhân khác mà hóa ra là một sai lầm ngớ ngẩn của tôi. Tôi nghĩ tôi sẽ thêm nó vào đây để phòng khi nó giúp ích cho bất kỳ ai.

Tôi đã có một ViewPager sử dụng FragmentStatePagerAdapter trước đây có hai đoạn, nhưng sau đó tôi đã thêm một đoạn thứ ba. Tuy nhiên, tôi quên rằng giới hạn trang ngoài màn hình mặc định là 1 - vì vậy, khi tôi chuyển sang phân đoạn thứ ba mới, phân đoạn đầu tiên sẽ bị phá hủy, sau đó được tạo lại sau khi chuyển trở lại. Vấn đề là hoạt động của tôi chịu trách nhiệm thông báo cho các phân đoạn này để khởi tạo trạng thái giao diện người dùng của chúng. Điều này xảy ra khi vòng đời hoạt động và phân đoạn giống nhau, nhưng để khắc phục, tôi phải thay đổi các phân đoạn để khởi tạo giao diện người dùng của riêng chúng trong vòng đời khởi động của chúng. Cuối cùng, tôi cũng đã thay đổi setOffscreenPageLimit thành 2 để cả ba mảnh vỡ đều được giữ nguyên (an toàn trong trường hợp này vì chúng không tốn nhiều bộ nhớ).


-1

Tôi đã có vấn đề tương tự. Tôi lưu các lượt xem vào bộ nhớ cache vì tôi chỉ cần 3 lượt xem trong ViewPager. Khi tôi trượt về phía trước thì mọi thứ vẫn ổn nhưng khi tôi bắt đầu trượt về phía sau thì xảy ra lỗi, nó báo rằng "chế độ xem của tôi đã có cha". Giải pháp là xóa các mục không cần thiết theo cách thủ công.

@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

Đối với tôi, vấn đề là quay trở lại hoạt động sau khi quy trình ứng dụng bị giết. Tôi đang sử dụng bộ điều hợp máy nhắn tin chế độ xem tùy chỉnh được sửa đổi từ các nguồn Android. Máy nhắn tin chế độ xem được nhúng trực tiếp vào hoạt động.

Kêu gọi viewPager.setCurrentItem(position, true);

(với hoạt ảnh) sau khi thiết lập dữ liệu và thông báoDataSetChanged () dường như hoạt động, nhưng nếu tham số được đặt thành false thì không và phân đoạn bị trống. Đây là một trường hợp phức tạp có thể giúp ích cho ai đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.