Cách xác định khi Fragment hiển thị trong ViewPager


754

Vấn đề: Fragment onResume()trong ViewPagerlà bắn trước khi đoạn trở nên thực sự nhìn thấy được.

Ví dụ, tôi có 2 mảnh với ViewPagerFragmentPagerAdapter. Đoạn thứ hai chỉ khả dụng cho người dùng được ủy quyền và tôi cần yêu cầu người dùng đăng nhập khi đoạn đó hiển thị (sử dụng hộp thoại cảnh báo).

NHƯNG ViewPagertạo ra đoạn thứ hai khi phần thứ nhất hiển thị để lưu trữ đoạn thứ hai và làm cho nó hiển thị khi người dùng bắt đầu vuốt.

Vì vậy, onResume()sự kiện này được thực hiện trong đoạn thứ hai rất lâu trước khi nó được nhìn thấy. Đó là lý do tại sao tôi đang cố gắng tìm một sự kiện phát sinh khi đoạn thứ hai hiển thị để hiển thị hộp thoại vào thời điểm thích hợp.

Điều này có thể giải quyết như thế nào?


18
"Tôi có 2 đoạn với ViewPager và FragmentPagerAd CHƯƠNG. Đoạn thứ hai chỉ có thể có sẵn cho người dùng được ủy quyền và tôi nên yêu cầu sử dụng để đăng nhập khi đoạn đó hiển thị (hộp thoại cảnh báo)." - IMHO, đó là UX khủng khiếp. Bật lên hộp thoại vì người dùng vuốt theo chiều ngang sẽ khiến tôi đưa ra xếp hạng một sao trên Cửa hàng Play.
CommonsWare

tốt hơn là chỉ hiển thị thông tin tại TextView bằng nút "Đăng nhập"? Giải pháp của bạn cho trường hợp đó là gì?
giờ chiều

5
"Không cần tải dữ liệu nếu nó sẽ không được hiển thị." - sau đó bạn không nên đặt nó trong a ViewPager. Trong một máy nhắn tin hai trang, cả hai trang sẽ được tải ngay lập tức, cho dù bạn có thích hay không. Trải nghiệm người dùng ViewPagerđược cho là nội dung có ngay lập tức khi vuốt, không lâu sau đó. Đó là lý do tại sao ViewPagerkhởi tạo một trang trước những gì có thể nhìn thấy, để giúp đảm bảo trải nghiệm người dùng đó.
CommonsWare

2
Có vẻ như ViewPager không đủ linh hoạt và nó không cho phép tắt bộ đệm ẩn vì bộ tối thiểu OfferscreenPageLimit là 1: stackoverflow.com/questions/10073214/ . Không thấy bất kỳ lý do nào cho việc này và hành vi dự kiến ​​(trong trường hợp bộ nhớ đệm bắt buộc) là để tạo ra mảnh vỡ NHƯNG mảnh vỡ lửa onResume () khi mảnh vỡ hiển thị.
4ntoine

1
Hơi muộn một chút, nhưng với bất kỳ ai gặp phải vấn đề tương tự, bạn có thể dùng thử thư viện FragmentViewPager (tôi là tác giả), giải quyết vấn đề này và cung cấp thêm một vài tính năng. Đối với một mẫu, kiểm tra trang GitHub của dự án hoặc câu trả lời stackoverflow này .
S. Brukhanda

Câu trả lời:


582

Cách xác định khi Fragment hiển thị trong ViewPager

Bạn có thể làm như sau bằng cách ghi đè setUserVisibleHinttrong Fragment:

public class MyFragment extends Fragment {
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
        }
        else {
        }
    }
}

7
Nhờ cập nhật Thư viện hỗ trợ Android ngày hôm nay (phiên bản 11), vấn đề gợi ý hiển thị của người dùng cuối cùng đã được khắc phục. Bây giờ an toàn để sử dụng gợi ý hiển thị của người dùng cho ViewPager.
Oasis Feng

58
Tôi đã thấy rằng phương thức setUserVisibleHint được gọi là TRƯỚC KHI onCreateView được gọi và điều này gây khó khăn cho việc theo dõi bất kỳ khởi tạo nào.
AndroidDev

11
@AndroidDev Nếu bạn muốn chạy một số mã khi gợi ý đến là đúng, nhưng cần cây xem đã được khởi tạo, chỉ cần bọc khối mã đó isResumed()để tránh NPE. Đã làm việc tốt cho tôi.
Ravi Thapliyal

13
Thật nực cười khi có rất nhiều hack khác nhau cho một cái gì đó mà SDK nên cung cấp theo mặc định.
Mike6679

15
setUserVisibleHinthiện không được chấp nhận
SR

525

CẬP NHẬT : Thư viện hỗ trợ Android (phiên bản 11) cuối cùng đã khắc phục sự cố gợi ý hiển thị của người dùng , bây giờ nếu bạn sử dụng thư viện hỗ trợ cho các đoạn, thì bạn có thể sử dụng getUserVisibleHint()hoặc ghi đè một cách an toàn setUserVisibleHint()để nắm bắt các thay đổi như được mô tả bởi câu trả lời của gorn.

CẬP NHẬT 1 Đây là một vấn đề nhỏ với getUserVisibleHint(). Giá trị này là mặc định true.

// Hint provided by the app that this fragment is currently visible to the user.
boolean mUserVisibleHint = true;

Vì vậy, có thể có một vấn đề khi bạn cố gắng sử dụng nó trước khi setUserVisibleHint()được gọi. Như một cách giải quyết, bạn có thể đặt giá trị trong onCreatephương thức như thế này.

public void onCreate(@Nullable Bundle savedInstanceState) {
    setUserVisibleHint(false);

Câu trả lời lỗi thời:

Trong hầu hết các trường hợp sử dụng, ViewPagerchỉ hiển thị một trang tại một thời gian, nhưng các mảnh vỡ trước cache cũng được đưa vào "nhìn thấy" nhà nước (thực sự vô hình) nếu bạn đang sử dụng FragmentStatePagerAdaptertrong Android Support Library pre-r11.

Tôi ghi đè:

public class MyFragment extends Fragment {
    @Override
    public void setMenuVisibility(final boolean visible) {
        super.setMenuVisibility(visible);
        if (visible) {
            // ...
        }
    }
   // ...
}

Để nắm bắt trạng thái tiêu điểm của đoạn, mà tôi nghĩ là trạng thái phù hợp nhất với "mức độ hiển thị" mà bạn muốn nói, vì chỉ một đoạn trong ViewPager thực sự có thể đặt các mục menu của nó cùng với các mục của hoạt động cha mẹ.


73
Hãy cẩn thận khi sử dụng getActivity (), khi bắt đầu lần đầu tiên, nó sẽ là null.
vovkab

10
Lưu ý rằng setUserVisibleHint () luôn hoạt động chính xác trong FragmentPagerAd CHƯƠNG.
louielouie

8
Giải pháp này (và cũng là của Gorn) đang bị chậm trễ một chút. Trong trường hợp trình xem được quét nhanh và nhiều lần, phương thức này sẽ được gọi với độ trễ (khi đoạn không còn hiển thị / vô hình).
AsafK

3
Tôi đang nhận được truegetUserVisibleHint () khi onCreateOptionsMenuđược gọi và khi setUserVisibleHintđược gọi, menu chưa được tạo. Cuối cùng, tôi nhận được hai tùy chọn Menu được thêm vào khi tôi chỉ muốn menu từ đoạn có thể nhìn thấy. Bất kỳ đề nghị về vấn đề này?
cYrixmorten

13
setUserVisibleHinthiện không được chấp nhận
SR

143

Điều này dường như để khôi phục onResume()hành vi bình thường mà bạn mong đợi. Nó chơi tốt với việc bấm phím home để rời khỏi ứng dụng và sau đó nhập lại ứng dụng. onResume()không được gọi hai lần liên tiếp.

@Override
public void setUserVisibleHint(boolean visible)
{
    super.setUserVisibleHint(visible);
    if (visible && isResumed())
    {
        //Only manually call onResume if fragment is already visible
        //Otherwise allow natural fragment lifecycle to call onResume
        onResume();
    }
}

@Override
public void onResume()
{
    super.onResume();
    if (!getUserVisibleHint())
    {
        return;
    }

    //INSERT CUSTOM CODE HERE
}

4
Chính xác những gì tôi đang tìm kiếm. Giải quyết vấn đề với setUserVisibleHintviệc được gọi trước đó onCreateViewvà điều setUserVisibleHintđó không được gọi nếu ứng dụng chạy nền và sau đó là tiền cảnh. Tuyệt vời! Cảm ơn bạn!
Alex

11
Tôi nghĩ rằng tự gọi onResume là một ý tưởng khá tệ, nhưng nếu không, mọi thứ bạn cần để trả lời câu hỏi của bài đăng này đều nằm trong hàm setUserVisibleHint!
Quentin G.

4
Tôi thà thực hiện một onVisibleToUser()phương thức đơn giản và có từ đó gọi từ onResume()setUserVisibleHint(boolean)thay vì onResume()tự gọi mình và can thiệp vào các cuộc gọi lại vòng đời. Nếu không, tôi nghĩ rằng phương pháp này hoạt động độc đáo, cảm ơn!
Stephan Henningsen

1
Có tôi đồng ý với ý kiến ​​trên. Nói chung, cố gắng tránh gọi các phương thức công khai từ các phương thức công khai trong lớp của bạn. Tạo một phương thức riêng và gọi nó từ cả hai phương thức công khai.
Johan Franzén

4
setUserVisibleHint không dùng nữa!
Hamid Reza

70

Đây là một cách khác để sử dụng onPageChangeListener:

  ViewPager pager = (ViewPager) findByViewId(R.id.viewpager);
  FragmentPagerAdapter adapter = new FragmentPageAdapter(getFragmentManager);
  pager.setAdapter(adapter);
  pager.setOnPageChangeListener(new OnPageChangeListener() {

  public void onPageSelected(int pageNumber) {
    // Just define a callback method in your fragment and call it like this! 
    adapter.getItem(pageNumber).imVisible();

  }

  public void onPageScrolled(int arg0, float arg1, int arg2) {
    // TODO Auto-generated method stub

  }

  public void onPageScrollStateChanged(int arg0) {
    // TODO Auto-generated method stub

  }
});

1
Giải pháp này hoạt động tốt, cảm ơn. Đây phải là giải pháp được đề xuất cho những người xây dựng nền tảng cũ hơn bằng cách sử dụng gói hỗ trợ phân đoạn (v4)
Frank Yin

7
Điều đáng chú ý là điều này không thể mang lại cho bạn kết quả như mong đợi nếu bạn đang sử dụng FragmentStatePagerAd CHƯƠNG và khởi tạo một thể hiện Fragment mới trên getItem (), bởi vì bạn sẽ chỉ đặt trạng thái trên đoạn mới chứ không phải là trạng thái mà người xem có được
Ben Pearson

1
Giải pháp này là tốt nếu bạn muốn thực hiện một cái gì đó khi mảnh vỡ trở nên nhìn thấy. Nó không giữ trạng thái hữu hình của một mảnh (nghĩa là nó không biết khi nào mảnh đó là vô hình).
AsafK

Tôi cùng loại vấn đề. hãy giúp tôi giải quyết vấn đề Bài đăng của tôi: stackoverflow.com/questions/23115283/ Lời
Jeeten Parmar

điều này đưa ra ngoại lệ con trỏ null cho bộ điều hợp mà tôi đang sử dụng trong phương thức iamVisible của đoạn. Tôi đang cố gắng thiết lập dữ liệu nhận được từ mạng chỉ khi đoạn này hiển thị.
zaphod100.10

58

setUserVisibleHint()đôi khi được gọi onCreateView() và đôi khi sau đó gây rắc rối.

Để khắc phục điều này bạn cần kiểm tra isResumed()bên trong setUserVisibleHint()phương pháp. Nhưng trong trường hợp này tôi nhận ra setUserVisibleHint()được gọi là chỉ nếu Fragment thi đấu trở lại và có thể nhìn thấy, không phải khi tạo.

Vì vậy, nếu bạn muốn cập nhật một cái gì đó khi Fragment visible, hãy đặt chức năng cập nhật của bạn cả trong onCreate()setUserVisibleHint():

@Override
public View onCreateView(...){
    ...
    myUIUpdate();
    ...        
}
  ....
@Override
public void setUserVisibleHint(boolean visible){
    super.setUserVisibleHint(visible);
    if (visible && isResumed()){
        myUIUpdate();
    }
}

CẬP NHẬT:myUIUpdate() Đôi khi tôi nhận ra được gọi hai lần, lý do là, nếu bạn có 3 tab và mã này nằm trên tab thứ 2, khi bạn mở tab thứ nhất, tab thứ 2 cũng được tạo ngay cả khi nó không hiển thị và myUIUpdate()được gọi. Sau đó, khi bạn vuốt sang tab thứ 2, myUIUpdate()từ đó if (visible && isResumed())được gọi và kết quả là,myUIUpdate() có thể được gọi hai lần trong một giây.

Người kia vấn đề!visiblesetUserVisibleHint cả hai được gọi là 1) khi bạn rời khỏi màn hình phân đoạn và 2) trước khi nó được tạo, khi bạn chuyển sang màn hình phân đoạn lần đầu tiên.

Giải pháp:

private boolean fragmentResume=false;
private boolean fragmentVisible=false;
private boolean fragmentOnCreated=false;
...

@Override
public View onCreateView(...){
    ...
    //Initialize variables
    if (!fragmentResume && fragmentVisible){   //only when first time fragment is created
        myUIUpdate();
    }
    ...        
}

@Override
public void setUserVisibleHint(boolean visible){
    super.setUserVisibleHint(visible);
    if (visible && isResumed()){   // only at fragment screen is resumed
        fragmentResume=true;
        fragmentVisible=false;
        fragmentOnCreated=true;
        myUIUpdate();
    }else  if (visible){        // only at fragment onCreated
        fragmentResume=false;
        fragmentVisible=true;
        fragmentOnCreated=true;
    }
    else if(!visible && fragmentOnCreated){// only when you go out of fragment screen
        fragmentVisible=false;
        fragmentResume=false;
    }
}

Giải trình:

fragmentResume, fragmentVisible: Tạo sự chắc chắn myUIUpdate()trong onCreateView()được gọi là chỉ khi đoạn được tạo ra và có thể nhìn thấy, không phải trên sơ yếu lý lịch. Nó cũng giải quyết vấn đề khi bạn ở tab 1, tab thứ 2 được tạo ngay cả khi nó không hiển thị. Điều này giải quyết điều đó và kiểm tra xem màn hình mảnh có thể nhìn thấy khi nào onCreate.

fragmentOnCreated: Đảm bảo đoạn không hiển thị và không được gọi khi bạn tạo đoạn đầu tiên. Vì vậy, bây giờ mệnh đề if này chỉ được gọi khi bạn vuốt ra khỏi đoạn.

Cập nhật Bạn có thể đặt tất cả mã này vào BaseFragmentnhư thế này và phương thức ghi đè.


1
Giải pháp của bạn hoạt động hoàn hảo ngoại trừ một kịch bản, khi đoạn được mở bình thường và sau đó bạn nhấp vào nút nào đó để thay thế bằng đoạn khác và sau đó nhấp lại để bật đoạn mới từ backstack, trường hợp setUserVisibleHintđó không nhận được cuộc gọi .. ! và bên trong onCreateViewphương pháp fragmentVisiblefalsegì!? vậy là mảnh vỡ hiện lên trống rỗng ..! bất kỳ suy nghĩ.?
Alaa AbuZarifa

28

Để phát hiện Fragmenttrong tầm ViewPagernhìn, tôi khá chắc chắn rằng chỉ sử dụng setUserVisibleHint là không đủ.
Đây là giải pháp của tôi để kiểm tra xem một mảnh có thể nhìn thấy hoặc vô hình. Đầu tiên khi khởi chạy viewpager, chuyển đổi giữa trang, đi đến một hoạt động / đoạn / nền / foreground` khác

public class BaseFragmentHelpLoadDataWhenVisible extends Fragment {
    protected boolean mIsVisibleToUser; // you can see this variable may absolutely <=> getUserVisibleHint() but it not. Currently, after many test I find that

    /**
     * This method will be called when viewpager creates fragment and when we go to this fragment background or another activity or fragment
     * NOT called when we switch between each page in ViewPager
     */
    @Override
    public void onStart() {
        super.onStart();
        if (mIsVisibleToUser) {
            onVisible();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mIsVisibleToUser) {
            onInVisible();
        }
    }

    /**
     * This method will called at first time viewpager created and when we switch between each page
     * NOT called when we go to background or another activity (fragment) when we go back
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        mIsVisibleToUser = isVisibleToUser;
        if (isResumed()) { // fragment have created
            if (mIsVisibleToUser) {
                onVisible();
            } else {
                onInVisible();
            }
        }
    }

    public void onVisible() {
        Toast.makeText(getActivity(), TAG + "visible", Toast.LENGTH_SHORT).show();
    }

    public void onInVisible() {
        Toast.makeText(getActivity(), TAG + "invisible", Toast.LENGTH_SHORT).show();
    }
}

GIẢI THÍCH Bạn có thể kiểm tra logcat bên dưới một cách cẩn thận sau đó tôi nghĩ bạn có thể biết tại sao giải pháp này sẽ hoạt động

Ra mắt lần đầu

Fragment1: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment2: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment3: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment1: setUserVisibleHint: isVisibleToUser=true isResumed=false // AT THIS TIME isVisibleToUser=true but fragment still not created. If you do something with View here, you will receive exception
Fragment1: onCreateView
Fragment1: onStart mIsVisibleToUser=true
Fragment2: onCreateView
Fragment3: onCreateView
Fragment2: onStart mIsVisibleToUser=false
Fragment3: onStart mIsVisibleToUser=false

Chuyển đến trang2

Fragment1: setUserVisibleHint: isVisibleToUser=false isResumed=true
Fragment2: setUserVisibleHint: isVisibleToUser=true isResumed=true

Chuyển đến trang 3

Fragment2: setUserVisibleHint: isVisibleToUser=false isResumed=true
Fragment3: setUserVisibleHint: isVisibleToUser=true isResumed=true

Chuyển đến nền:

Fragment1: onStop mIsVisibleToUser=false
Fragment2: onStop mIsVisibleToUser=false
Fragment3: onStop mIsVisibleToUser=true

Đi tới tiền cảnh

Fragment1: onStart mIsVisibleToUser=false
Fragment2: onStart mIsVisibleToUser=false
Fragment3: onStart mIsVisibleToUser=true

Dự án DEMO tại đây

Hy vọng nó sẽ giúp


1
Nó không hoạt động khi một đoạn có một máy nhắn tin xem khác cũng bao gồm các đoạn.
Shihab Uddin

xin lỗi, tôi không thể đăng câu trả lời vào lúc này vì tôi không có đủ thời gian, nếu có thể xin vui lòng giới thiệu git của tôi về vấn đề của bạn ở đây github.com/PhanVanLinh/AndroidViewPagerSkeleton/tree/master . SubChildContainerFragmentđược sử dụng để phát hiện a fragment has another view pager which also consists fragment. Bạn có thể trộn SubChildContainerFragmentChildContainerFragmentđến 1 lớp. Hy vọng nó sẽ giúp. Tôi sẽ đăng câu trả lời đầy đủ sau
Phan Văn Linh

26
package com.example.com.ui.fragment;


import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.example.com.R;

public class SubscribeFragment extends Fragment {

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

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        if (isVisibleToUser) {
            // called here
        }
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }
}

2
Đây phải là câu trả lời được chấp nhận. Hoạt động với android.app.Fragment cũng là một phần thưởng rất lớn.
RajV

setUserVisibleHintKhông dùng nữa
ekashking

23

Trong ViewPager2ViewPagertừ phiên bản, androidx.fragment:fragment:1.1.0bạn chỉ có thể sử dụng onPauseonResumegọi lại để xác định đoạn nào hiện đang hiển thị cho người dùng. onResumegọi lại được gọi khi mảnh vỡ trở nên nhìn thấy vàonPause khi nó dừng lại để được nhìn thấy.

Trong trường hợp ViewPager2, đó là hành vi mặc định nhưng hành vi tương tự có thể được kích hoạt cho mục đích cũ ViewPager cách dễ dàng.

Để kích hoạt hành vi này trong ViewPager đầu tiên, bạn phải truyền FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENTtham số làm đối số thứ hai của hàm FragmentPagerAdaptertạo.

FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)

Lưu ý: setUserVisibleHint()phương thức và hàm FragmentPagerAdaptertạo với một tham số hiện không được chấp nhận trong phiên bản mới của Fragment từ android jetpack.


1
Cảm ơn giải pháp tốt đẹp. Tôi đã tìm kiếm điều này từ lâu. Công việc tuyệt vời
Anurag Srivastava

1
Đối với ViewPager2, tùy chọn BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT không phải là hành vi mặc định, bạn cần đặt thủ công. Khi thiết lập tùy chọn này, giải pháp của bạn đã làm việc cho tôi. Cảm ơn.
DriAn

1
Kể từ APR 2020, đây là giải pháp cập nhật và hoạt động như một cơ duyên.
Prakash

1
@ 4ntoine vui lòng xem xét chấp nhận câu trả lời này là câu trả lời đúng, vì hiện tại điều này là chính xác và hầu hết các câu trả lời đều sử dụng phương thức setUserVisibleHint không dùng nữa
Jorn Rigter

16

Ghi đè setPrimaryItem()trong FragmentPagerAdapterlớp con. Tôi sử dụng phương pháp này, và nó hoạt động tốt.

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    // This is what calls setMenuVisibility() on the fragments
    super.setPrimaryItem(container, position, object);

    if (object instanceof MyWhizBangFragment) {
        MyWhizBangFragment fragment = (MyWhizBangFragment) object;
        fragment.doTheThingYouNeedToDoOnBecomingVisible();
    }
}

1
Điều này gần như đã làm việc nhưng có vấn đề với NPE và được gọi nhiều lần. Đăng một thay thế dưới đây!
Gober


@Gober lưu đối tượng Object lần đầu tiên và kiểm tra và bỏ qua cuộc gọi tiếp theo với cùng một đối tượng Object giống như FragmentStateAd CHƯƠNG làm trong phương thức setPrimaryItem ()
wanglugao

12

Ghi đè Fragment.onHiddenChanged()cho điều đó.

public void onHiddenChanged(boolean hidden)

Được gọi khi trạng thái ẩn (như được trả về isHidden()) của đoạn đã thay đổi. Những mảnh vỡ bắt đầu không bị che giấu; cái này sẽ được gọi bất cứ khi nào đoạn thay đổi trạng thái từ đó.

Tham số
hidden- boolean: Đúng nếu đoạn này bị ẩn, sai nếu không hiển thị.


3
phương pháp này không còn được sử dụng nữa và không thể sử dụng được nữa
bluewhile

4
@bluewhile bạn thấy nó bị phản đối ở đâu? developer.android.com/reference/android/app/NH
Cel

1
Tôi vừa mới sử dụng thành công và tài liệu này dường như không cho thấy nó không dùng nữa.
Trevor

1
@bluewhile, trong 4.1 (api 16) nó vẫn chưa được phản đối.
dan

8
Tôi đang sử dụng API cấp 19 và có thể xác nhận rằng mặc dù điều này không bị phản đối, nhưng nó không hoạt động như quảng cáo. onHiddenChanged không được gọi khi đoạn bị ẩn bởi một đoạn khác, ví dụ.

4

Tôi đã tìm ra điều đó onCreateOptionsMenuonPrepareOptionsMenucác phương pháp chỉ được gọi trong trường hợp mảnh thực sự có thể nhìn thấy. Tôi không thể tìm thấy bất kỳ phương thức nào hoạt động như thế này, tôi cũng đã thử OnPageChangeListenernhưng nó không hoạt động cho các tình huống, ví dụ, tôi cần một biến được khởi tạo trong onCreatephương thức.

Vì vậy, hai phương pháp này có thể được sử dụng cho vấn đề này như một cách giải quyết, đặc biệt cho các công việc nhỏ và ngắn.

Tôi nghĩ rằng, đây là giải pháp tốt hơn nhưng không phải là tốt nhất. Tôi sẽ sử dụng điều này nhưng chờ giải pháp tốt hơn cùng một lúc.

Trân trọng.


2

Một giải pháp khác được đăng ở đây ghi đè setPrimaryItem trong pagerad CHƯƠNG bởi kris larson gần như làm việc cho tôi. Nhưng phương pháp này được gọi là nhiều lần cho mỗi thiết lập. Ngoài ra tôi đã nhận được NPE từ các lượt xem, v.v. trong đoạn này vì điều này chưa sẵn sàng trong vài lần đầu tiên phương thức này được gọi. Với những thay đổi sau đây, điều này có hiệu quả với tôi:

private int mCurrentPosition = -1;

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    super.setPrimaryItem(container, position, object);

    if (position == mCurrentPosition) {
        return;
    }

    if (object instanceof MyWhizBangFragment) {
        MyWhizBangFragment fragment = (MyWhizBangFragment) object;

        if (fragment.isResumed()) {
            mCurrentPosition = position;
            fragment.doTheThingYouNeedToDoOnBecomingVisible();
        }
    }
}

2

Thêm mã sau vào đoạn

@Override
public void setMenuVisibility(final boolean visible) 
 {
    super.setMenuVisibility(visible);
    if (visible && isResumed()) 
     {

     }
}

Điều này chỉ hoạt động cho ViewPagerFragments. Không cho các mảnh trong hoạt động bình thường.
AndroidGuy

2

Tôi gặp vấn đề tương tự trong khi làm việc với FragmentStatePagerAdaptersvà 3 tab. Tôi đã phải hiển thị Dilaog bất cứ khi nào tab thứ 1 được nhấp và ẩn nó khi nhấp vào các tab khác.

Ghi đè setUserVisibleHint()một mình không giúp tìm thấy mảnh vỡ hiện tại.

Khi nhấp từ tab thứ 3 -----> tab thứ 1. Nó kích hoạt hai lần cho đoạn thứ 2 và cho đoạn thứ nhất. Tôi đã kết hợp nó với phương thức isResumed ().

    @Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    isVisible = isVisibleToUser;

    // Make sure that fragment is currently visible
    if (!isVisible && isResumed()) {
        // Call code when Fragment not visible
    } else if (isVisible && isResumed()) {
       // Call code when Fragment becomes visible.
    }

}

2

Chúng tôi có một trường hợp đặc biệt với MVP trong đó đoạn cần thông báo cho người trình bày rằng chế độ xem đã hiển thị và người trình bày được Dagger đưa vào fragment.onAttach().

setUserVisibleHint()là không đủ, chúng tôi đã phát hiện 3 trường hợp khác nhau cần được giải quyết ( onAttach()được đề cập để bạn biết khi nào người trình bày có mặt):

  1. Mảnh vỡ vừa được tạo ra. Hệ thống thực hiện các cuộc gọi sau:

    setUserVisibleHint() // before fragment's lifecycle calls, so presenter is null
    onAttach()
    ...
    onResume()
  2. Đoạn đã được tạo và nút home được nhấn. Khi khôi phục ứng dụng thành tiền cảnh, điều này được gọi là:

    onResume()
  3. Thay đổi định hướng:

    onAttach() // presenter available
    onResume()
    setUserVisibleHint()

Chúng tôi chỉ muốn gợi ý về khả năng hiển thị để đến với người thuyết trình một lần, vì vậy đây là cách chúng tôi thực hiện:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment_list, container, false);
    setHasOptionsMenu(true);

    if (savedInstanceState != null) {
        lastOrientation = savedInstanceState.getInt(STATE_LAST_ORIENTATION,
              getResources().getConfiguration().orientation);
    } else {
        lastOrientation = getResources().getConfiguration().orientation;
    }

    return root;
}

@Override
public void onResume() {
    super.onResume();
    presenter.onResume();

    int orientation = getResources().getConfiguration().orientation;
    if (orientation == lastOrientation) {
        if (getUserVisibleHint()) {
            presenter.onViewBecomesVisible();
        }
    }
    lastOrientation = orientation;
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (presenter != null && isResumed() && isVisibleToUser) {
        presenter.onViewBecomesVisible();
    }
}

@Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(STATE_LAST_ORIENTATION, lastOrientation);
}

2

Phát hiện bởi focused view !

Điều này làm việc cho tôi

public static boolean isFragmentVisible(Fragment fragment) {
    Activity activity = fragment.getActivity();
    View focusedView = fragment.getView().findFocus();
    return activity != null
            && focusedView != null
            && focusedView == activity.getWindow().getDecorView().findFocus();
}

1

Tôi gặp phải vấn đề này khi tôi đang cố gắng để hẹn giờ kích hoạt khi đoạn trong trình xem được hiển thị trên màn hình để người dùng nhìn thấy.

Đồng hồ bấm giờ luôn bắt đầu ngay trước khi người dùng nhìn thấy mảnh vỡ. Điều này là do onResume()phương thức trong đoạn được gọi trước khi chúng ta có thể thấy đoạn đó.

Giải pháp của tôi là kiểm tra onResume()phương pháp. Tôi muốn gọi một phương thức nhất định là 'foo ()' khi đoạn 8 là chế độ xem phân trang hiện tại.

@Override
public void onResume() {
    super.onResume();
    if(viewPager.getCurrentItem() == 8){
        foo();
        //Your code here. Executed when fragment is seen by user.
    }
}

Hi vọng điêu nay co ich. Tôi đã thấy vấn đề này bật lên rất nhiều. Đây dường như là giải pháp đơn giản nhất tôi từng thấy. Nhiều người khác không tương thích với các API thấp hơn, v.v.


1

Tôi gặp vấn đề tương tự. ViewPagerthực hiện các sự kiện vòng đời mảnh vỡ khác và tôi không thể thay đổi hành vi đó. Tôi đã viết một máy nhắn tin đơn giản bằng cách sử dụng các đoạn và hình ảnh động có sẵn. Đơn giản


1

Tôi đã sử dụng nó và nó đã làm việc!

mContext.getWindow().getDecorView().isShown() //boolean

1

Tôi hỗ trợ SectionsPagerAd CHƯƠNG với các đoạn con, vì vậy sau nhiều lần đau đầu, cuối cùng tôi cũng có phiên bản làm việc dựa trên các giải pháp từ chủ đề này:

public abstract class BaseFragment extends Fragment {

    private boolean visible;
    private boolean visibilityHintChanged;

    /**
     * Called when the visibility of the fragment changed
     */
    protected void onVisibilityChanged(View view, boolean visible) {

    }

    private void triggerVisibilityChangedIfNeeded(boolean visible) {
        if (this.visible == visible || getActivity() == null || getView() == null) {
            return;
        }
        this.visible = visible;
        onVisibilityChanged(getView(), visible);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (!visibilityHintChanged) {
            setUserVisibleHint(false);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (getUserVisibleHint() && !isHidden()) {
            triggerVisibilityChangedIfNeeded(true);
        }
    }

    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        triggerVisibilityChangedIfNeeded(!hidden);
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        visibilityHintChanged = true;
        if (isVisibleToUser && isResumed() && !isHidden()) {
            triggerVisibilityChangedIfNeeded(true);
        } else if (!isVisibleToUser) {
            triggerVisibilityChangedIfNeeded(false);
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        triggerVisibilityChangedIfNeeded(false);
    }

    @Override
    public void onStop() {
        super.onStop();
        triggerVisibilityChangedIfNeeded(false);
    }

    protected boolean isReallyVisible() {
        return visible;
    }
}

Quên đề cập đến, một khi bạn thêm đoạn con vào đoạn cha mẹ thì đoạn đoạn.setUserVisibleHint (true);
Andoctorey

Và đừng quên sử dụng getChildFragmentManager () thay vì getFragmentManager () để thêm các đoạn con.
Andoctorey

0

Lưu ý rằng setUserVisibleHint(false)không được gọi là dừng hoạt động / phân đoạn. Bạn vẫn sẽ cần kiểm tra bắt đầu / dừng để đúng register/unregisterngười nghe / v.v.

Ngoài ra, bạn sẽ nhận được setUserVisibleHint(false)nếu đoạn của bạn bắt đầu ở trạng thái không nhìn thấy được; bạn không muốn đến unregisterđó vì bạn chưa bao giờ đăng ký trước đó trong trường hợp đó.

@Override
public void onStart() {
    super.onStart();

    if (getUserVisibleHint()) {
        // register
    }
}

@Override
public void onStop() {
    if (getUserVisibleHint()) {
        // unregister
    }

    super.onStop();
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    if (isVisibleToUser && isResumed()) {
        // register

        if (!mHasBeenVisible) {
            mHasBeenVisible = true;
        }
    } else if (mHasBeenVisible){
        // unregister
    }
}

0

Một cách đơn giản để thực hiện đó là kiểm tra xem người dùng có đăng nhập hay không trước khi đi đến đoạn.

Trong MainActivity của bạn, bạn có thể làm một cái gì đó như thế này bên trong phương thức onNavlationItemSelected .

 case R.id.nav_profile_side:


                if (User_is_logged_in) {

                    fragmentManager.beginTransaction()
                            .replace(R.id.content_frame
                                    , new FragmentProfile())
                            .commit();
                }else {

                    ShowLoginOrRegisterDialog(fragmentManager);

                }

                break;

Tuy nhiên, nếu bạn đang sử dụng ngăn kéo điều hướng, lựa chọn trong ngăn kéo sẽ thay đổi thành Hồ sơ mặc dù chúng tôi chưa đi đến ProfileFragment.

Để đặt lại lựa chọn về lựa chọn hiện tại, hãy chạy đoạn mã dưới đây

        navigationView.getMenu().getItem(0).setChecked(true);

-4

Tôi áp dụng phương thức Count của FragmentStatePagerAdOG được liên kết và nó sẽ trả về tổng số trừ đi số lượng trang cần ẩn:

 public class MyAdapter : Android.Support.V13.App.FragmentStatePagerAdapter
 {   
     private List<Fragment> _fragments;

     public int TrimmedPages { get; set; }

     public MyAdapter(Android.App.FragmentManager fm) : base(fm) { }

     public MyAdapter(Android.App.FragmentManager fm, List<Android.App.Fragment> fragments) : base(fm)
     {
         _fragments = fragments;

         TrimmedPages = 0;
     }

     public override int Count
     {
         //get { return _fragments.Count; }
         get { return _fragments.Count - TrimmedPages; }
     }
 }

Vì vậy, nếu có 3 đoạn ban đầu được thêm vào ViewPager và chỉ hiển thị 2 đoạn đầu tiên cho đến khi một số điều kiện được đáp ứng, hãy ghi đè số trang bằng cách đặt TrimmedPages thành 1 và nó chỉ hiển thị hai trang đầu tiên.

Điều này hoạt động tốt cho các trang ở cuối, nhưng sẽ không thực sự giúp ích cho những trang ở đầu hoặc giữa (mặc dù có rất nhiều cách để làm điều này).

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.