Created
July 26, 2016 14:11
-
-
Save douo/41ebe572860cfc27fd1375b0105f618a to your computer and use it in GitHub Desktop.
可嵌套使用的 FragmentPagerAdapter 主要修复 FragmentPagerAdapter 处理 {@link Fragment#setMenuVisibility(boolean)} 的 bug
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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