0包雀、介紹
本篇整理自己在開發(fā)過程中遇到的各種坑,以便以后參考适室。
1嫡意、自定義Dialog樣式時(shí)發(fā)現(xiàn)頂部出現(xiàn)一條藍(lán)色橫線
//部分手機(jī)dialog會出現(xiàn)藍(lán)色橫線,因此將它隱藏掉
Context context = getDialog().getContext();
int divierId = context.getResources().getIdentifier("android:id/titleDivider", null, null);
View divider = getDialog().findViewById(divierId);
if (divider != null) divider.setBackgroundColor(Color.TRANSPARENT);
在show之前將橫線隱藏掉
2捣辆、 Permission Denial: starting Intent with revoked permission android.permission.CAMERA
崩潰原因:測試機(jī)版本>=M蔬螟,代碼中通過ACTION調(diào)取攝像機(jī),如果Manifest文件中聲明了android.permission.CAMERA權(quán)限則會崩潰汽畴。
解決方案:
1旧巾、如果項(xiàng)目中沒有使用到相機(jī)權(quán)限則把CAMERA權(quán)限刪掉。
2忍些、啟動該ACTION時(shí)先詢問相機(jī)權(quán)限是否已動態(tài)聲明鲁猩。
3、getExternalFilesDir(null)可能返回空
getExternalFilesDir(null).getPath() 由于可能返回空報(bào)NullPointerException罢坝,因此使用時(shí)需要加一層trycatch保護(hù)
4廓握、升級AS的ndk時(shí)出現(xiàn)"No toolchains found in the NDK toolchains folder for ABI with prefix: mips64el-linux-android"
崩潰原因:更新之后少了ndk-bundle下的toolchains少了文件
解決方案:
https://developer.android.com/ndk/downloads/?hl=zh-en
在這里下載穩(wěn)定的ndk庫,下載完之后跟Android/sdk/ndk-bundle/toolchains/下的文件比對嘁酿,把少了的補(bǔ)充進(jìn)去隙券。
5、gradle dependency中exclude用法
一般在dependencies下的依賴格式:
groupName:moduleName:版本號
例如:
com.android.support:appcompat-v7:$SUPPORT_LIBRARY_VERSION
查看某個(gè)依賴它下面的依賴樹闹司,一般類似于:
+--- com.android.support:appcompat-v7:27.1.1
| +--- com.android.support:support-annotations:27.1.1
| +--- com.android.support:support-core-utils:27.1.1
| | +--- com.android.support:support-annotations:27.1.1
| | \--- com.android.support:support-compat:27.1.1
| | +--- com.android.support:support-annotations:27.1.1
| | \--- android.arch.lifecycle:runtime:1.1.0
| | +--- android.arch.lifecycle:common:1.1.0
| | \--- android.arch.core:common:1.1.0
| +--- com.android.support:support-fragment:27.1.1
| | +--- com.android.support:support-compat:27.1.1 (*)
| | +--- com.android.support:support-core-ui:27.1.1
| | | +--- com.android.support:support-annotations:27.1.1
| | | +--- com.android.support:support-compat:27.1.1 (*)
| | | \--- com.android.support:support-core-utils:27.1.1 (*)
| | +--- com.android.support:support-core-utils:27.1.1 (*)
| | +--- com.android.support:support-annotations:27.1.1
| | +--- android.arch.lifecycle:livedata-core:1.1.0
| | | +--- android.arch.lifecycle:common:1.1.0
| | | +--- android.arch.core:common:1.1.0
| | | \--- android.arch.core:runtime:1.1.0
| | | \--- android.arch.core:common:1.1.0
| | \--- android.arch.lifecycle:viewmodel:1.1.0
| +--- com.android.support:support-vector-drawable:27.1.1
| | +--- com.android.support:support-annotations:27.1.1
| | \--- com.android.support:support-compat:27.1.1 (*)
| \--- com.android.support:animated-vector-drawable:27.1.1
| +--- com.android.support:support-vector-drawable:27.1.1 (*)
| \--- com.android.support:support-core-ui:27.1.1 (*)
+--- com.jakewharton:butterknife:8.8.1
| +--- com.jakewharton:butterknife-annotations:8.8.1
| | \--- com.android.support:support-annotations:25.3.0 -> 27.1.1
| +--- com.android.support:support-annotations:25.3.0 -> 27.1.1
| \--- com.android.support:support-compat:25.3.0 -> 27.1.1 (*)
exclude的格式為:
implementation ('com.jakewharton:butterknife:8.8.1') {
exclude group: 'com.android.support',module:'support-annotations'
}
規(guī)則很簡單娱仔,移除該依賴下的groupName為"com.android.support",且moduleName為"support-annotations"的依賴
改完之后的依賴樹
+--- com.android.support:appcompat-v7:27.1.1
| +--- com.android.support:support-annotations:27.1.1
| +--- com.android.support:support-core-utils:27.1.1
| | +--- com.android.support:support-annotations:27.1.1
| | \--- com.android.support:support-compat:27.1.1
| | +--- com.android.support:support-annotations:27.1.1
| | \--- android.arch.lifecycle:runtime:1.1.0
| | +--- android.arch.lifecycle:common:1.1.0
| | \--- android.arch.core:common:1.1.0
| +--- com.android.support:support-fragment:27.1.1
| | +--- com.android.support:support-compat:27.1.1 (*)
| | +--- com.android.support:support-core-ui:27.1.1
| | | +--- com.android.support:support-annotations:27.1.1
| | | +--- com.android.support:support-compat:27.1.1 (*)
| | | \--- com.android.support:support-core-utils:27.1.1 (*)
| | +--- com.android.support:support-core-utils:27.1.1 (*)
| | +--- com.android.support:support-annotations:27.1.1
| | +--- android.arch.lifecycle:livedata-core:1.1.0
| | | +--- android.arch.lifecycle:common:1.1.0
| | | +--- android.arch.core:common:1.1.0
| | | \--- android.arch.core:runtime:1.1.0
| | | \--- android.arch.core:common:1.1.0
| | \--- android.arch.lifecycle:viewmodel:1.1.0
| +--- com.android.support:support-vector-drawable:27.1.1
| | +--- com.android.support:support-annotations:27.1.1
| | \--- com.android.support:support-compat:27.1.1 (*)
| \--- com.android.support:animated-vector-drawable:27.1.1
| +--- com.android.support:support-vector-drawable:27.1.1 (*)
| \--- com.android.support:support-core-ui:27.1.1 (*)
+--- com.jakewharton:butterknife:8.8.1
| +--- com.jakewharton:butterknife-annotations:8.8.1
| \--- com.android.support:support-compat:25.3.0 -> 27.1.1 (*)
同時(shí)還支持只指定moduleName或者只指定groupName的情況游桩。
只指定moduleName:
implementation ('com.jakewharton:butterknife:8.8.1') {
exclude module:'support-annotations'
}
這之后的依賴樹為:
+--- com.android.support:appcompat-v7:27.1.1
| +--- com.android.support:support-annotations:27.1.1
| +--- com.android.support:support-core-utils:27.1.1
| | +--- com.android.support:support-annotations:27.1.1
| | \--- com.android.support:support-compat:27.1.1
| | +--- com.android.support:support-annotations:27.1.1
| | \--- android.arch.lifecycle:runtime:1.1.0
| | +--- android.arch.lifecycle:common:1.1.0
| | \--- android.arch.core:common:1.1.0
| +--- com.android.support:support-fragment:27.1.1
| | +--- com.android.support:support-compat:27.1.1 (*)
| | +--- com.android.support:support-core-ui:27.1.1
| | | +--- com.android.support:support-annotations:27.1.1
| | | +--- com.android.support:support-compat:27.1.1 (*)
| | | \--- com.android.support:support-core-utils:27.1.1 (*)
| | +--- com.android.support:support-core-utils:27.1.1 (*)
| | +--- com.android.support:support-annotations:27.1.1
| | +--- android.arch.lifecycle:livedata-core:1.1.0
| | | +--- android.arch.lifecycle:common:1.1.0
| | | +--- android.arch.core:common:1.1.0
| | | \--- android.arch.core:runtime:1.1.0
| | | \--- android.arch.core:common:1.1.0
| | \--- android.arch.lifecycle:viewmodel:1.1.0
| +--- com.android.support:support-vector-drawable:27.1.1
| | +--- com.android.support:support-annotations:27.1.1
| | \--- com.android.support:support-compat:27.1.1 (*)
| \--- com.android.support:animated-vector-drawable:27.1.1
| +--- com.android.support:support-vector-drawable:27.1.1 (*)
| \--- com.android.support:support-core-ui:27.1.1 (*)
+--- com.jakewharton:butterknife:8.8.1
| +--- com.jakewharton:butterknife-annotations:8.8.1
| \--- com.android.support:support-compat:25.3.0 -> 27.1.1 (*)
剩余的一種情況就不演示了牲迫。
注意:
1、exclude語法在官方包(就是support庫這些)的依賴下不奏效借卧,這個(gè)不知道什么原因盹憎。
6、DialogFragment給Dialog配置onDismissListener()不起作用铐刘。
案例:
dialogFragment.getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialogInterface) {
//TODO
}
});
dialogFragment.show(getFragmentManager(),"");
這種情況下dialog關(guān)閉時(shí)onDismiss不會調(diào)用陪每。
原因:
查看DialogFragment的源碼發(fā)現(xiàn),DialogFragment里面也有給dialog設(shè)置監(jiān)聽器滨达。
public void onActivityCreated(Bundle var1) {
super.onActivityCreated(var1);
......
this.mDialog.setCancelable(this.mCancelable);
this.mDialog.setOnCancelListener(this);
this.mDialog.setOnDismissListener(this);
if (var1 != null) {
Bundle var4 = var1.getBundle("android:savedDialogState");
if (var4 != null) {
this.mDialog.onRestoreInstanceState(var4);
}
}
}
}
因此如果我們按案例中的方案設(shè)置的監(jiān)聽器會被DialogFragment本身覆蓋奶稠,自然調(diào)用不到。
解決方案
自定義DialogFragment捡遍,覆蓋DialogFragment的onDismiss()方法锌订,添加上自己的邏輯既可。
@Override
public void onDismiss(DialogInterface dialogInterface) {
super.onDismiss(dialogInterface);
if (onDissmissListener != null) onDissmissListener.onDismiss(dialogInterface);
}
7画株、GridView的item有默認(rèn)點(diǎn)擊背景
解決方案:
gridView.setSelector(new ColorDrawable(Color.TRANSPARENT));// 去掉默認(rèn)點(diǎn)擊背景
8辆飘、gradle dependecy一直依賴最新版本的依賴
configurations.all {
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}
9啦辐、Android stroke邊線框只畫某一邊
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:left="-2dp"
android:right="-2dp"
android:top="-2dp">
<shape>
<solid android:color="#ffffff"/>
<stroke
android:width="1dp"
android:color="#ff0000"/>
</shape>
</item>
</layer-list>
10、TextView當(dāng)使用Spannable時(shí)不顯示省略號的問題
重寫TextView蜈项,反射修改其中的屬性
public class FoldTextView extends AppCompatTextView {
public FoldTextView (Context context) {
super(context);
}
public FoldTextView (Context context, AttributeSet attrs) {
super(context, attrs);
}
public FoldTextView (Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
StaticLayout layout = null;
Field field = null;
try {
Field staticField = DynamicLayout.class.getDeclaredField("sStaticLayout");
staticField.setAccessible(true);
layout = (StaticLayout) staticField.get(DynamicLayout.class);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (layout != null) {
try {
field = StaticLayout.class.getDeclaredField("mMaximumVisibleLineCount");
field.setAccessible(true);
field.setInt(layout,2);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
if (layout != null && field != null) {
try {
field.setInt(layout,Integer.MAX_VALUE);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
11芹关、子module下的資源文件id不是靜態(tài)常量
原因:
(1)該module編譯后的代碼中該資源被替換成值
(2)主工程下有同名資源id的話會被替換掉
(3)主工程會重新針對該資源生成一個(gè)id
(4)之后就會出現(xiàn)子module中該資源找不到的情況
子module資源id非靜態(tài)常量可能出現(xiàn)的情況
(1)主工程有相同名稱資源時(shí)會使用主工程的
(2)子module下由于資源id非靜態(tài)常量,沒辦法使用switch-case
(3)子module下沒法直接使用ButterKnife
12紧卒、當(dāng)同個(gè)布局下出現(xiàn)兩個(gè)同名id控件的情況
出現(xiàn)情況:Fragment包含ViewPager侥衬,ViewPager又包含F(xiàn)ragment,此時(shí)父Fragment和子Fragment擁有同id控件跑芳,父Fragment通過findViewById()找到的不一定是父Fragment下的控件轴总,因?yàn)樗鼤祷氐谝粋€(gè)找到的控件
13、Fragment中調(diào)用startActivityForResult
(1)getActivity().startActivityForResult()博个,只有父Aty的onActivityResult被調(diào)用怀樟,F(xiàn)ragment的不被調(diào)用
(2)startActivityForResult(),當(dāng)前Fragment和父Activity的onActivityResult被調(diào)用
()getParentFragment().startAcitivtyForResult()盆佣,父Fragment和父Activity的onActivityResult被調(diào)用
14往堡、latest.release指向非最新版本號
與第三方聯(lián)調(diào)時(shí)候會發(fā)現(xiàn)當(dāng)?shù)谌降谝粫r(shí)間更新了maven依賴時(shí)通過latest.release沒有把最新版本的依賴?yán)聛恚氖侵暗陌姹竟菜!_@個(gè)時(shí)候手動改版本號就可解決虑灰。
如果找得到原因的話,這里補(bǔ)充
15征堪、Dialog初始化Context誤區(qū)
因?yàn)樽罱袀€(gè)需求是需要計(jì)時(shí)后彈出Dialog瘩缆,此時(shí)如果用戶的app在后臺且計(jì)時(shí)到了需要彈窗关拒,如果Activity被回收了佃蚜,出現(xiàn)了crash
android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@ce0817e is not valid; is your activity running?
原因:
Dialog可以傳入Context初始化,但Dialog的展示需要依附Activity着绊,換言之就是show()時(shí)候需要判斷Activity是否存活
解決方案:
初始化時(shí)Context盡量傳入為Activity
if(!activity.isFinishing()) dialog.show()
16谐算、ViewPager+Fragment重建時(shí)泄漏問題
場景:
當(dāng)首頁使用ViewPager+Fragment,之后進(jìn)入其他Activity归露,在某一個(gè)Activity中崩潰時(shí)洲脂,app會嘗試恢復(fù)棧頂崩潰前的Aty。這種崩潰情況下會導(dǎo)致Fragment重復(fù)創(chuàng)建內(nèi)存泄漏剧包。
此時(shí)Aty生命周期:onSaveInstanceState---> onRestoreINstanceState恐锦。并重新走onCreate()創(chuàng)建流程。
如果你與我使用的是該場景:
FragmentManager fragmentManager = getSupportFragmentManager();
List<Fragment> fragmentList = new ArrayList<>();
List<String> titleList = new ArrayList<>();
fragmentList.add(new HomeFragment());
titleList.add("HomeFragment");
fragmentList.add(new TaskFragment().addStatusBarPadding(true));
titleList.add("TaskFragment");
fragmentList.add(new UserCenterFragment().addStatusBarPadding(true));
titleList.add("UserCenterFragment");
mPageAdapter = new PagerFragment(fragmentManager, fragmentList, titleList);
viewPager.setAdapter(mPageAdapter);
你會發(fā)現(xiàn)通過adapter.getItem()拿到的Fragment其實(shí)與viewPager上的不是同一個(gè)實(shí)例疆液。
解決方案:
class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
savedInstanceState.remove("android:support:fragments"); //不保存fragments一铅,當(dāng)Activity被銷毀時(shí)保證每次Fragment都是重新創(chuàng)建
}
}
}
補(bǔ)充另一個(gè)更加優(yōu)化的處理方案:http://www.reibang.com/p/e35089896ed4
17、PopupWindow注意事項(xiàng)
1堕油、調(diào)用show()時(shí)潘飘,需要注意不能在onCreate階段調(diào)用肮之,否則報(bào)錯(cuò)
Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
正確做法:post延時(shí)處理
findViewById(R.id.root).post(new Runnable() {
@Override
public void run() {
popupWindow.showAsDropDown(findViewById(R.id.btn1));
}
});
2、調(diào)用dismiss()時(shí)卜录,需要判斷當(dāng)前Actiivty是否finish()戈擒,否則crash(目前該crash只在5.X手機(jī)上發(fā)現(xiàn))
java.lang.IllegalArgumentException: View=com.iqiyi.ishow.attention.view.nul{3c6fd43e V.E..... .......D 0,0-720,120} not attached to window manager
正確做法:判斷Activity狀態(tài)
private static void dismissWithCheck() {
if (popupWindow != null && popupWindow.isShowing()) {
Context context = ((ContextWrapper)popupWindow.getContentView().getContext()).getBaseContext();
try {
if (context instanceof Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
if (!((Activity) context).isFinishing() && !((Activity) context).isDestroyed()) {
popupWindow.dismiss();
}
} else {
// Api < 17. Unfortunately cannot check for isDestroyed()
if (!((Activity) context).isFinishing()) {
popupWindow.dismiss();
}
}
}
} catch (IllegalArgumentException e) {
Log.append("IllegalArgumentException:").append(e.getMessage()).append("\n");
} catch (Exception e) {
Log.append("Exception:").append(e.getMessage()).append("\n");
}
}
}
18、Logcat查看日志技巧
官網(wǎng)鏈接:https://developer.android.com/studio/debug/am-logcat#memory-logs
當(dāng)引用發(fā)生GC時(shí)艰毒,響應(yīng)的消息會輸出到logcat中筐高,可以根據(jù)Logcat定位gc相關(guān)信息。
19丑瞧、單個(gè)頁面多個(gè)ViewPager使用同一個(gè)id出現(xiàn)的情況
描述場景:當(dāng)一個(gè)Activity中包含了多個(gè)ViewPager凯傲,且這幾個(gè)ViewPager的id是一樣的時(shí)候,如果ViewPager中包含的都是Fragment嗦篱,那只有第一個(gè)ViewPager會顯示Fragment冰单,其余的ViewPager會顯示沒有子View。
描述結(jié)果:由于ViewPager具有相同的id灸促,所有的Fragment都會被添加到Activity的contentView找到的第一個(gè)ViewPager中诫欠,查看布局元素就會發(fā)現(xiàn),所有的Fragment生命周期正常浴栽,且Fragment.getView().getParent()都指向第一個(gè)ViewPager荒叼。
原因分析:
首先看FragmentPagerAdapter中如何生成Fragment
@NonNull
@Override
public Object instantiateItem(@NonNull 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);
#1
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);
#2 這句是關(guān)鍵
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
} else {
fragment.setUserVisibleHint(false);
}
}
return fragment;
}
public long getItemId(int position) {
return position;
}
1、這里的container指的是ViewPager
2典鸡、在#1步驟被廓,代碼會根據(jù)生成的name從FragmentManager中找對應(yīng)的Fragment,如果找到萝玷,則不走getItem()獲取Fragment嫁乘。也就是說如果你的頁面中存在2個(gè)同名的ViewPager,那么第二個(gè)ViewPager可能會因?yàn)樯傻膎ame相同而沒法將Fragment添加到FragmentManager中球碉。而會出現(xiàn)什么樣的后果還沒嘗試過
3蜓斧、在#2步驟,代碼會把得到的Fragment添加到FragmentTransaction事務(wù)中
@NonNull
public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment,
@Nullable String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
}
void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
final Class<?> fragmentClass = fragment.getClass();
final int modifiers = fragmentClass.getModifiers();
...
if (containerViewId != 0) {
if (containerViewId == View.NO_ID) {
throw new IllegalArgumentException("Can't add fragment "
+ fragment + " with tag " + tag + " to container view with no id");
}
if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
throw new IllegalStateException("Can't change container ID of fragment "
+ fragment + ": was " + fragment.mFragmentId
+ " now " + containerViewId);
}
fragment.mContainerId = fragment.mFragmentId = containerViewId;
}
addOp(new Op(opcmd, fragment));
}
這里最關(guān)鍵的一個(gè)步驟是fragment.mContainerId = fragment.mFragmentId = containerViewId; 將Fragment的mContainerId置為了ViewPager的id睁冬。接下來看Fragment.mContainerId在哪里使用挎春,就會發(fā)現(xiàn)神奇的事情。
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
...
case Fragment.CREATED:
...
if (newState > Fragment.CREATED) {
if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
if (!f.mFromLayout) {
ViewGroup container = null;
if (f.mContainerId != 0) {
if (f.mContainerId == View.NO_ID) {
throwException(new IllegalArgumentException(
"Cannot create fragment "
+ f
+ " for a container view with no id"));
}
#步驟1
container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
if (container == null && !f.mRestored) {
String resName;
try {
resName = f.getResources().getResourceName(f.mContainerId);
} catch (Resources.NotFoundException e) {
resName = "unknown";
}
throwException(new IllegalArgumentException(
"No view found for id 0x"
+ Integer.toHexString(f.mContainerId) + " ("
+ resName
+ ") for fragment " + f));
}
}
#步驟2
f.mContainer = container;
f.performCreateView(f.performGetLayoutInflater(
f.mSavedFragmentState), container, f.mSavedFragmentState);
if (f.mView != null) {
f.mInnerView = f.mView;
f.mView.setSaveFromParentEnabled(false);
if (container != null) {
#步驟3
container.addView(f.mView);
}
if (f.mHidden) {
f.mView.setVisibility(View.GONE);
}
f.onViewCreated(f.mView, f.mSavedFragmentState);
dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
false);
// Only animate the view if it is visible. This is done after
// dispatchOnFragmentViewCreated in case visibility is changed
f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
&& f.mContainer != null;
} else {
f.mInnerView = null;
}
}
f.performActivityCreated(f.mSavedFragmentState);
dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
if (f.mView != null) {
f.restoreViewState(f.mSavedFragmentState);
}
f.mSavedFragmentState = null;
}
...
}
在FragmentManagerImpl.moveToState方法中豆拨,當(dāng)newState為CREATED的時(shí)候直奋,演示了Fragment是如何添加到布局中的:
步驟1、通過mContainer.onFindViewById方法施禾,根據(jù)Fragment.mContainerId從頁面中找到Fragment的父布局脚线。mContainer是FragmentManagerImpl的局部變量,它的賦值對象一般情況下是FragmentHostCallbacks拾积,而調(diào)用onFindViewById方法最終會調(diào)用FragmentActivity.findViewById()方法殉挽,也就是從根布局中找Fragment的父布局丰涉。在該場景下,由于單個(gè)頁面中存在多個(gè)相同id的ViewPager斯碌,所以FragmentManagerImpl根據(jù)Fragment.mContainerId找到的一直都是第一個(gè)ViewPager一死。
步驟2、調(diào)用Fragment.performCreateView()傻唾,在里面又會調(diào)用我們熟悉的onCreateView()方法創(chuàng)建根布局投慈。
步驟3、container.addView()這里會把Fragment.mView添加到container里冠骄,這里也就證實(shí)了為什么后面ViewPager的Fragment會被添加進(jìn)第一個(gè)ViewPager里面了伪煤。
結(jié)論:
當(dāng)同一個(gè)頁面下如果存在多個(gè)id相同的ViewPager時(shí),如果每一頁都是Fragment的情況下會出現(xiàn)添加異常情況凛辣,后面幾個(gè)ViewPager期望添加的Fragment會因?yàn)镕ragmentManagerImpl中找Fragment的父布局時(shí)抱既,由于根據(jù)id都找到了第一個(gè)ViewPager,而將所有的Fragment都添加到了第一個(gè)ViewPager中去扁誓。此時(shí)就出現(xiàn)了后面幾個(gè)ViewPager沒有內(nèi)容的情況防泵。
還有種情況是可能讓同一個(gè)頁面中允許存在2個(gè)相同id的ViewPager且顯示正常的。當(dāng)且僅當(dāng)兩個(gè)ViewPager傳入的FragmentManger不是同一個(gè)的情況(例如一個(gè)傳入的是Activity的getSupportFragmentManager蝗敢,另一個(gè)傳入的是Fragment的getChildFragmentManager)捷泞,當(dāng)FragmentManger不為同一個(gè)的情況時(shí),在步驟1中就會因?yàn)閺牟煌母季种袑ふ腋髯缘淖覸iew寿谴,這個(gè)時(shí)候找到的ViewPager就不會是同一個(gè)锁右,因而添加Fragment的顯示邏輯就會正常。
20讶泰、ImageView設(shè)置ImageResource的時(shí)候是否引起重繪
需要根據(jù)drawable的尺寸來分析咏瑟。如果跟之前的drawable一致那么就不會requestLayout(),如果不一致的話則會requestLayout()
public void setImageDrawable(@Nullable Drawable drawable) {
if (mDrawable != drawable) {
mResource = 0;
mUri = null;
final int oldWidth = mDrawableWidth;
final int oldHeight = mDrawableHeight;
updateDrawable(drawable);
if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
requestLayout();
}
invalidate();
}
}