Skip to content

Instantly share code, notes, and snippets.

@douo
Created July 26, 2016 14:11
Show Gist options
  • Save douo/41ebe572860cfc27fd1375b0105f618a to your computer and use it in GitHub Desktop.
Save douo/41ebe572860cfc27fd1375b0105f618a to your computer and use it in GitHub Desktop.
可嵌套使用的 FragmentPagerAdapter 主要修复 FragmentPagerAdapter 处理 {@link Fragment#setMenuVisibility(boolean)} 的 bug
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dstech.huishou;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.PagerAdapter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
/**
*
* 可嵌套使用的 FragmentPagerAdapter
* 主要修复 FragmentPagerAdapter 处理 {@link Fragment#setMenuVisibility(boolean)} 的 bug
* Created by Tiou on 6/20/16.
*/
public abstract class HierarchyFragmentPagerAdapter extends PagerAdapter {
private static final String TAG = "HierarchyFragmentPager";
private static final boolean DEBUG = false;
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
private Fragment mCurrentPrimaryItem = null;
private boolean currentPrimary;
private AdapterHolder mHolder;
private HierarchyFragmentPagerAdapter mParent;
public HierarchyFragmentPagerAdapter(FragmentManager fm, @Nullable HierarchyFragmentPagerAdapter parent, @NonNull AdapterHolder holder) {
mFragmentManager = fm;
mParent = parent;
if (parent == null || parent.mCurrentPrimaryItem == holder) {
// 1. 根节点始终是主要的
// 2. Holder Fragment 构造 Adapter 时已经是 currentPrimaryItem
currentPrimary = true;
}
mHolder = holder;
}
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
//
// public void addChild(HierarchyFragmentPagerAdapter child) {
// mChildren.add(child);
// }
/**
* Return the Fragment associated with a specified position.
*/
public abstract Fragment getItem(int position);
@Override
public void startUpdate(ViewGroup container) {
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
+ " v=" + ((Fragment) object).getView());
mCurTransaction.detach((Fragment) object);
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false);
if (mCurrentPrimaryItem instanceof AdapterHolder) {
notifyHolderParentPrimaryChanged((AdapterHolder) mCurrentPrimaryItem, false);
}
}
if (fragment != null) {
if (mHolder != null && isHolderCurrentPrimary()) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
if (fragment instanceof AdapterHolder) {
Log.d(TAG, "Holder:" + mHolder.getClass());
Log.d(TAG, "Fragment:" + fragment.getClass());
notifyHolderParentPrimaryChanged((AdapterHolder) fragment, true);
}
}
mCurrentPrimaryItem = fragment;
}
}
private void notifyHolderParentPrimaryChanged(AdapterHolder fragment, boolean primary) {
if (fragment.getAdapter() != null) {
fragment.getAdapter().onParentPrimaryChanged(primary);
}
}
private boolean isHolderCurrentPrimary() {
if (mHolder.getAdapter() != null) {
return mHolder.getAdapter().isCurrentPrimary();
} else {
return false;
}
}
/**
* 表示当前 Adapter 是否是首要的,如果 adapter 标记首要,当其父 Adapter 不是首要,仍返回非首要
*
* @return
*/
public boolean isCurrentPrimary() {
return currentPrimary && (mParent == null || mParent.isCurrentPrimary());
}
/**
* 容器 Fragment 是否是首要页发生变化
*
* @param primary 父 fragment 是否是首要的
*/
protected void onParentPrimaryChanged(boolean primary) {
Log.d(TAG, "Holder:" + mHolder.getClass());
currentPrimary = primary;
if (mCurrentPrimaryItem != null) {
Log.d(TAG, "onParentPrimaryChanged:" + mCurrentPrimaryItem.getClass() + ":" + primary);
if (primary) {
mCurrentPrimaryItem.setMenuVisibility(true);
mCurrentPrimaryItem.setUserVisibleHint(true);
} else {
mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
// 递归传递消息
if (mCurrentPrimaryItem instanceof AdapterHolder) {
((AdapterHolder) mCurrentPrimaryItem).getAdapter().onParentPrimaryChanged(primary);
}
}
}
@Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitAllowingStateLoss();
mCurTransaction = null;
mFragmentManager.executePendingTransactions();
}
}
@Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment) object).getView() == view;
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
}
/**
* Return a unique identifier for the item at the given position.
* <p>
* <p>The default implementation returns the given position.
* Subclasses should override this method if the positions of items can change.</p>
*
* @param position Position within this adapter
* @return Unique identifier for the item at position
*/
public long getItemId(int position) {
return position;
}
public interface AdapterHolder {
HierarchyFragmentPagerAdapter getAdapter();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment