2017-07-08 44 views
0

我想使用ViewPager创建视频列表。我必须知道何时传呼机项目可见,并且不可见。 我使用VideoListAdapter为ViewPager扩展FragmentStatePagerAdapter。 我使用Fragment方法setUserVisibleHint触发视频开始或暂停。 但是有一个问题,ViewPager位置0处的Fragment抛出一个NullPointerException异常。然后,我打印记录相关碎片的方法。
日志I进入VideoListActivity: 07-08 17:06:50.264 E/lemon: startUpdate 07-08 17:06:50.264 E/lemon: instantiateItem 0 07-08 17:06:50.264 E/lemon: getItem 0 07-08 17:06:50.264 E/lemon: setUserVisibleHint 0 isVisibleToUser false 07-08 17:06:50.264 E/lemon: instantiateItem 1 07-08 17:06:50.264 E/lemon: getItem 1 07-08 17:06:50.264 E/lemon: setUserVisibleHint 0 isVisibleToUser false 07-08 17:06:50.264 E/lemon: setPrimaryItem 0 07-08 17:06:50.264 E/lemon: setUserVisibleHint 0 isVisibleToUser true 07-08 17:06:50.264 E/lemon: finishUpdate 07-08 17:06:50.265 E/lemon: onAttach 0 07-08 17:06:50.265 E/lemon: onAttach 1 07-08 17:06:50.265 E/lemon: onCreateView 0 07-08 17:06:50.267 E/lemon: onstart 0 07-08 17:06:50.267 E/lemon: onCreateView 1 07-08 17:06:50.269 E/lemon: onstart 1 07-08 17:06:50.270 E/lemon: startUpdate 07-08 17:06:50.270 E/lemon: setPrimaryItem 0 07-08 17:06:50.270 E/lemon: finishUpdate 07-08 17:06:50.297 E/lemon: startUpdate 07-08 17:06:50.297 E/lemon: setPrimaryItem 0 07-08 17:06:50.297 E/lemon: finishUpdate 07-08 17:06:50.297 E/lemon: startUpdate 07-08 17:06:50.297 E/lemon: setPrimaryItem 0 07-08 17:06:50.297 E/lemon: finishUpdate 07-08 17:06:50.703 E/lemon: startUpdate 07-08 17:06:50.703 E/lemon: setPrimaryItem 0 07-08 17:06:50.703 E/lemon: finishUpdate 07-08 17:06:50.704 E/lemon: startUpdate 07-08 17:06:50.704 E/lemon: setPrimaryItem 0 07-08 17:06:50.704 E/lemon: finishUpdate FragmentStatePagerAdapter中片段的生命周期是意外的

日志I滚动到位置1: 07-08 17:09:41.154 E/lemon: startUpdate 07-08 17:09:41.154 E/lemon: setPrimaryItem 0 07-08 17:09:41.154 E/lemon: finishUpdate 07-08 17:09:41.966 E/lemon: startUpdate 07-08 17:09:41.966 E/lemon: instantiateItem 2 07-08 17:09:41.967 E/lemon: getItem 2 07-08 17:09:41.967 E/lemon: setUserVisibleHint 0 isVisibleToUser false 07-08 17:09:41.967 E/lemon: setPrimaryItem 1 07-08 17:09:41.967 E/lemon: setUserVisibleHint 0 isVisibleToUser false 07-08 17:09:41.967 E/lemon: setUserVisibleHint 1 isVisibleToUser true 07-08 17:09:41.967 E/lemon: finishUpdate 07-08 17:09:41.968 E/lemon: onAttach 2 07-08 17:09:41.968 E/lemon: onCreateView 2 07-08 17:09:41.971 E/lemon: onstart 2 07-08 17:09:41.971 E/lemon: startUpdate 07-08 17:09:41.971 E/lemon: setPrimaryItem 1 07-08 17:09:41.971 E/lemon: finishUpdate 07-08 17:09:41.972 E/lemon: startUpdate 07-08 17:09:41.972 E/lemon: setPrimaryItem 1 07-08 17:09:41.972 E/lemon: finishUpdate
我分析这些日志,我发现,在positino 0片段调用setUserVisibleHint(真),然后再调用onAttach( ),但位置1中的片段在之后调用onAttach()first和setUserVisibleHint(true)。

因此,我在onAttach()和setUserVisibleHint(true)中调用onFragment中的onTrigger()方法,但失败。然后我调试我的代码,它显示onAttach()中调用onTrigger时,onTrigger()中的isAdded()返回false。

因此,任何建议,让我知道什么时候触发我的视频开始。非常感谢。

public class FullScreenVideoFragment extends Fragment { 

    private FragmentFullScreenVideoBinding binding; 
    int colorRes; 
    int position; 

    @Nullable 
    @Override 
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 
     Log.e("lemon", "onCreateView " + position); 
     binding = DataBindingUtil.inflate(inflater, R.layout.fragment_full_screen_video, container, false); 
     setView(); 
     return binding.getRoot(); 
    } 

    @Override 
    public void onCreate(@Nullable Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
    } 

    public void setBgAndPosition(int position, int colorRes) { 
     this.position = position; 
     this.colorRes = colorRes; 
    } 

    @Override 
    public void onAttach(Context context) { 
     Log.e("lemon", "onAttach " + position); 
     super.onAttach(context); 
     onTriger(); 
    } 

    @Override 
    public void onDetach() { 
     Log.e("lemon", "onDetach " + position); 
     super.onDetach(); 
    } 

    @Override 
    public void onAttachFragment(Fragment childFragment) { 
     Log.e("lemon", "onAttachFragment " + position); 
     super.onAttachFragment(childFragment); 
    } 

    @Override 
    public void setUserVisibleHint(boolean isVisibleToUser) { 
     Log.e("lemon", "setUserVisibleHint " + position + " isVisibleToUser " + isVisibleToUser); 
     super.setUserVisibleHint(isVisibleToUser); 
     onTriger(); 
    } 

    private void setView() { 
     binding.getRoot().setBackgroundResource(colorRes); 
     binding.position.setText(String.valueOf(position)); 
    } 

    private void onTriger() { 
     if (!isVisible()) return; 
     binding.position.setText(position + " start"); 
    } 
} 

public class VideoListAdapter extends FragmentStatePagerAdapter { 
    private LinkedList<FullScreenVideoFragment> fragmentCaches; 
    private int[] colors = new int[]{android.graphics.Color.RED, android.graphics.Color.BLUE, android.graphics.Color.GREEN}; 

    public VideoListAdapter(FragmentManager fm) { 
     super(fm); 
     fragmentCaches = new LinkedList<>(); 
    } 

    @Override 
    public Fragment getItem(int position) { 
     Log.e("lemon", "getItem " + position); 
     FullScreenVideoFragment fragment = generateItem(); 
     return fragment; 
    } 

    @Override 
    public int getCount() { 
     return 10; 
    } 

    @Override 
    public void destroyItem(ViewGroup container, int position, Object object) { 
     Log.e("lemon", "destroyItem " + position); 
     super.destroyItem(container, position, object); 
    } 

    @Override 
    public void setPrimaryItem(ViewGroup container, int position, Object object) { 
     Log.e("lemon", "setPrimaryItem " + position); 
     super.setPrimaryItem(container, position, object); 
    } 

    @Override 
    public Object instantiateItem(ViewGroup container, int position) { 
     Log.e("lemon", "instantiateItem " + position); 
     FullScreenVideoFragment fragment = (FullScreenVideoFragment) super.instantiateItem(container, position); 
     fragment.setBgAndPosition(position, colors[position % 3]); 
     return fragment; 
    } 

    @Override 
    public void startUpdate(ViewGroup container) { 
     Log.e("lemon", "startUpdate"); 
     super.startUpdate(container); 
    } 

    @Override 
    public void finishUpdate(ViewGroup container) { 
     Log.e("lemon", "finishUpdate"); 
     super.finishUpdate(container); 
    } 

    @Override 
    public void restoreState(Parcelable state, ClassLoader loader) { 
     Log.e("lemon", "restoreState"); 
     super.restoreState(state, loader); 
    } 

    @Override 
    public Parcelable saveState() { 
     Log.e("lemon", "saveState"); 
     return super.saveState(); 
    } 

    private FullScreenVideoFragment generateItem() { 
     FullScreenVideoFragment neededFragment = null; 
     if (!fragmentCaches.isEmpty()) { 
      neededFragment = fragmentCaches.get(0); 
      fragmentCaches.remove(0); 
      return neededFragment; 
     } 
     neededFragment = new FullScreenVideoFragment(); 
     return neededFragment; 
    } 
} 

片段的xml:

<?xml version="1.0" encoding="utf-8"?> 
<layout xmlns:android="http://schemas.android.com/apk/res/android"> 

    <data></data> 

    <RelativeLayout 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"> 
     <TextView 
      android:id="@+id/position" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:layout_centerInParent="true" 
      android:textColor="@color/account_name_color"/> 
    </RelativeLayout> 
</layout> 

回答

0

我发现碎片经常使用这三种方式进行切换:

  • 显示/隐藏

  • 连接/断开(更换)

  • ViewPager

这三种方式导致分片可见状态存在一定的差异,所以我定义了以下类这三种方式来区分。如果一组碎片使用相同的方式切换,我认为它应该是可靠的,如果以不同的方式切换,那么我不知道,没有仔细的测试。

import android.support.annotation.IntDef; 
import android.support.v4.app.Fragment; 

import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 

/** 
* Created by Kilnn on 2017/7/12. 
* A smart fragment know itself's visible state. 
*/ 
public abstract class SmartFragment extends Fragment { 

    private boolean isFragmentVisible; 

    @Override 
    public void onResume() { 
     super.onResume(); 
     int switchType = getSwitchType(); 
     if (switchType == ATTACH_DETACH) { 
      notifyOnFragmentVisible(); 
     } else if (switchType == SHOW_HIDE) { 
      if (!isHidden()) { 
       notifyOnFragmentVisible(); 
      } 
     } else if (switchType == VIEW_PAGER) { 
      //If the parent fragment exist and hidden when activity destroy, 
      //when the activity restore, The parent Fragment will be restore to hidden state. 
      //And the sub Fragment which in ViewPager is also be restored, and the onResumed() method will callback. 
      //And The sub Fragment's getUserVisibleHint() method will return true if it is in active position. 
      //So we need to judge the parent Fragment visible state. 
      if (getUserVisibleHint() && isParentFragmentVisible()) { 
       notifyOnFragmentVisible(); 
      } 
     } 
    } 

    @Override 
    public void setUserVisibleHint(boolean isVisibleToUser) { 
     super.setUserVisibleHint(isVisibleToUser); 
     int switchType = getSwitchType(); 
     if (switchType == VIEW_PAGER) { 
      if (isVisibleToUser) { 
       notifyOnFragmentVisible(); 
      } else { 
       notifyOnFragmentInvisible(); 
      } 
     } 
    } 

    @Override 
    public void onHiddenChanged(boolean hidden) { 
     super.onHiddenChanged(hidden); 
     int switchType = getSwitchType(); 
     if (switchType == SHOW_HIDE) { 
      if (hidden) { 
       notifyOnFragmentInvisible(); 
      } else { 
       notifyOnFragmentVisible(); 
      } 
     } 
    } 

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

    private boolean isParentFragmentVisible() { 
     Fragment parent = getParentFragment(); 
     if (parent == null) return true; 
     if (parent instanceof SmartFragment) { 
      return ((SmartFragment) parent).isFragmentVisible(); 
     } else { 
      //TODO May be can't get the correct visible state if parent Fragment is not SmartFragment 
      return parent.isVisible(); 
     } 
    } 

    public boolean isFragmentVisible() { 
     // Don't judge the state of the parent fragment, 
     // because if the parent fragment visible state changes, 
     // you must take the initiative to change the state of the sub fragment 
//  return isFragmentVisible && isParentFragmentVisible(); 
     return isFragmentVisible; 
    } 

    public void notifyOnFragmentVisible() { 
     if (!isFragmentVisible) { 
      onFragmentVisible(); 
      isFragmentVisible = true; 
     } 
    } 

    public void notifyOnFragmentInvisible() { 
     if (isFragmentVisible) { 
      onFragmentInvisible(); 
      isFragmentVisible = false; 
     } 
    } 

    /** 
    * If this method callback, the Fragment must be resumed. 
    */ 
    public void onFragmentVisible() { 

    } 

    /** 
    * If this method callback, the Fragment maybe is resumed or in onPause(). 
    */ 
    public void onFragmentInvisible() { 

    } 

    /** 
    * Fragments switch with attach/detach(replace) 
    */ 
    public static final int ATTACH_DETACH = 0; 

    /** 
    * Fragments switch with show/hide 
    */ 
    public static final int SHOW_HIDE = 1; 

    /** 
    * Fragments manage by view pager 
    */ 
    public static final int VIEW_PAGER = 2; 

    @Retention(RetentionPolicy.SOURCE) 
    @IntDef({ATTACH_DETACH, SHOW_HIDE, VIEW_PAGER}) 
    @interface SwitchType { 
    } 

    @SwitchType 
    public abstract int getSwitchType(); 

} 

所以你可以在onFragmentVisible()中做onTrigger()。

+0

最新的答案在这里:https://stackoverflow.com/questions/43207043/check-fragment-is-currently-visible-or-not-in-android/45059794#45059794 – Kilnn