Android踩坑日記

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)限則會崩潰汽畴。


image.png

解決方案:
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();
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末峻厚,一起剝皮案震驚了整個(gè)濱河市响蕴,隨后出現(xiàn)的幾起案子谆焊,更是在濱河造成了極大的恐慌惠桃,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辖试,死亡現(xiàn)場離奇詭異辜王,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)罐孝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門呐馆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人莲兢,你說我怎么就攤上這事汹来⌒牛” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵收班,是天一觀的道長坟岔。 經(jīng)常有香客問我,道長摔桦,這世上最難降的妖魔是什么社付? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮邻耕,結(jié)果婚禮上鸥咖,老公的妹妹穿的比我還像新娘。我一直安慰自己兄世,他們只是感情好啼辣,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著御滩,像睡著了一般熙兔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上艾恼,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天住涉,我揣著相機(jī)與錄音,去河邊找鬼钠绍。 笑死舆声,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的柳爽。 我是一名探鬼主播媳握,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼磷脯!你這毒婦竟也來了蛾找?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤赵誓,失蹤者是張志新(化名)和其女友劉穎打毛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體俩功,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幻枉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了诡蜓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片熬甫。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蔓罚,靈堂內(nèi)的尸體忽然破棺而出椿肩,到底是詐尸還是另有隱情瞻颂,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布郑象,位于F島的核電站蘸朋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏扣唱。R本人自食惡果不足惜藕坯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望噪沙。 院中可真熱鬧炼彪,春花似錦、人聲如沸正歼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽局义。三九已至喜爷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間萄唇,已是汗流浹背檩帐。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留另萤,地道東北人湃密。 一個(gè)月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像四敞,于是被迫代替她去往敵國和親泛源。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容