Android踩坑日记

0、介绍

本篇整理自己在开发过程中遇到的各种坑,以便以后参考。

1、自定义Dialog样式时发现顶部出现一条蓝色横线

//部分手机dialog会出现蓝色横线,因此将它隐藏掉
        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

崩溃原因:测试机版本>=M,代码中通过ACTION调取摄像机,如果Manifest文件中声明了android.permission.CAMERA权限则会崩溃。


image.png

解决方案:
1、如果项目中没有使用到相机权限则把CAMERA权限删掉。
2、启动该ACTION时先询问相机权限是否已动态声明。

3、getExternalFilesDir(null)可能返回空

getExternalFilesDir(null).getPath() 由于可能返回空报NullPointerException,因此使用时需要加一层trycatch保护

4、升级AS的ndk时出现"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
在这里下载稳定的ndk库,下载完之后跟Android/sdk/ndk-bundle/toolchains/下的文件比对,把少了的补充进去。

5、gradle dependency中exclude用法

一般在dependencies下的依赖格式:
groupName:moduleName:版本号
例如:
com.android.support:appcompat-v7:$SUPPORT_LIBRARY_VERSION
查看某个依赖它下面的依赖树,一般类似于:

+--- 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'
    }
规则很简单,移除该依赖下的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 (*)

同时还支持只指定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库这些)的依赖下不奏效,这个不知道什么原因。

6、DialogFragment给Dialog配置onDismissListener()不起作用。

案例

      dialogFragment.getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() {
        @Override
        public void onDismiss(DialogInterface dialogInterface) {
          //TODO
        }
      });

      dialogFragment.show(getFragmentManager(),"");

这种情况下dialog关闭时onDismiss不会调用。
原因
查看DialogFragment的源码发现,DialogFragment里面也有给dialog设置监听器。

   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);
                }
            }

        }
    }

因此如果我们按案例中的方案设置的监听器会被DialogFragment本身覆盖,自然调用不到。

解决方案
自定义DialogFragment,覆盖DialogFragment的onDismiss()方法,添加上自己的逻辑既可。

  @Override
  public void onDismiss(DialogInterface dialogInterface) {
    super.onDismiss(dialogInterface);
    if (onDissmissListener != null) onDissmissListener.onDismiss(dialogInterface);
  }

7、GridView的item有默认点击背景

解决方案:

gridView.setSelector(new ColorDrawable(Color.TRANSPARENT));// 去掉默认点击背景

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当使用Spannable时不显示省略号的问题

重写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不是静态常量

原因:
(1)该module编译后的代码中该资源被替换成值
(2)主工程下有同名资源id的话会被替换掉
(3)主工程会重新针对该资源生成一个id
(4)之后就会出现子module中该资源找不到的情况

子module资源id非静态常量可能出现的情况
(1)主工程有相同名称资源时会使用主工程的
(2)子module下由于资源id非静态常量,没办法使用switch-case
(3)子module下没法直接使用ButterKnife

12、当同个布局下出现两个同名id控件的情况

出现情况:Fragment包含ViewPager,ViewPager又包含Fragment,此时父Fragment和子Fragment拥有同id控件,父Fragment通过findViewById()找到的不一定是父Fragment下的控件,因为它会返回第一个找到的控件

13、Fragment中调用startActivityForResult

(1)getActivity().startActivityForResult(),只有父Aty的onActivityResult被调用,Fragment的不被调用
(2)startActivityForResult(),当前Fragment和父Activity的onActivityResult被调用
()getParentFragment().startAcitivtyForResult(),父Fragment和父Activity的onActivityResult被调用

14、latest.release指向非最新版本号

与第三方联调时候会发现当第三方第一时间更新了maven依赖时通过latest.release没有把最新版本的依赖拉下来,拉的是之前的版本。这个时候手动改版本号就可解决。
如果找得到原因的话,这里补充

15、Dialog初始化Context误区

因为最近有个需求是需要计时后弹出Dialog,此时如果用户的app在后台且计时到了需要弹窗,如果Activity被回收了,出现了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()时候需要判断Activity是否存活
解决方案:
初始化时Context尽量传入为Activity

if(!activity.isFinishing()) dialog.show()

16、ViewPager+Fragment重建时泄漏问题

场景
当首页使用ViewPager+Fragment,之后进入其他Activity,在某一个Activity中崩溃时,app会尝试恢复栈顶崩溃前的Aty。这种崩溃情况下会导致Fragment重复创建内存泄漏。
此时Aty生命周期:onSaveInstanceState---> onRestoreINstanceState。并重新走onCreate()创建流程。
如果你与我使用的是该场景:

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);

你会发现通过adapter.getItem()拿到的Fragment其实与viewPager上的不是同一个实例。

解决方案

class MainActivity extends Activity {
      @Override
    protected void onCreate(Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            savedInstanceState.remove("android:support:fragments"); //不保存fragments,当Activity被销毁时保证每次Fragment都是重新创建
        }
  }
}

补充另一个更加优化的处理方案:https://www.jianshu.com/p/e35089896ed4

17、PopupWindow注意事项

1、调用show()时,需要注意不能在onCreate阶段调用,否则报错

Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?

正确做法:post延时处理

        findViewById(R.id.root).post(new Runnable() {
            @Override
            public void run() {
                popupWindow.showAsDropDown(findViewById(R.id.btn1));
            }
        });

2、调用dismiss()时,需要判断当前Actiivty是否finish(),否则crash(目前该crash只在5.X手机上发现)

java.lang.IllegalArgumentException: View=com.iqiyi.ishow.attention.view.nul{3c6fd43e V.E..... .......D 0,0-720,120} not attached to window manager

正确做法:判断Activity状态

    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查看日志技巧

官网链接:https://developer.android.com/studio/debug/am-logcat#memory-logs
当引用发生GC时,响应的消息会输出到logcat中,可以根据Logcat定位gc相关信息。

19、单个页面多个ViewPager使用同一个id出现的情况

描述场景:当一个Activity中包含了多个ViewPager,且这几个ViewPager的id是一样的时候,如果ViewPager中包含的都是Fragment,那只有第一个ViewPager会显示Fragment,其余的ViewPager会显示没有子View。

描述结果:由于ViewPager具有相同的id,所有的Fragment都会被添加到Activity的contentView找到的第一个ViewPager中,查看布局元素就会发现,所有的Fragment生命周期正常,且Fragment.getView().getParent()都指向第一个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 这句是关键
            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步骤,代码会根据生成的name从FragmentManager中找对应的Fragment,如果找到,则不走getItem()获取Fragment。也就是说如果你的页面中存在2个同名的ViewPager,那么第二个ViewPager可能会因为生成的name相同而没法将Fragment添加到FragmentManager中。而会出现什么样的后果还没尝试过
3、在#2步骤,代码会把得到的Fragment添加到FragmentTransaction事务中

    @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));
    }

这里最关键的一个步骤是fragment.mContainerId = fragment.mFragmentId = containerViewId; 将Fragment的mContainerId置为了ViewPager的id。接下来看Fragment.mContainerId在哪里使用,就会发现神奇的事情。

        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方法中,当newState为CREATED的时候,演示了Fragment是如何添加到布局中的:
步骤1、通过mContainer.onFindViewById方法,根据Fragment.mContainerId从页面中找到Fragment的父布局。mContainer是FragmentManagerImpl的局部变量,它的赋值对象一般情况下是FragmentHostCallbacks,而调用onFindViewById方法最终会调用FragmentActivity.findViewById()方法,也就是从根布局中找Fragment的父布局。在该场景下,由于单个页面中存在多个相同id的ViewPager,所以FragmentManagerImpl根据Fragment.mContainerId找到的一直都是第一个ViewPager。

步骤2、调用Fragment.performCreateView(),在里面又会调用我们熟悉的onCreateView()方法创建根布局。

步骤3、container.addView()这里会把Fragment.mView添加到container里,这里也就证实了为什么后面ViewPager的Fragment会被添加进第一个ViewPager里面了。

结论:
当同一个页面下如果存在多个id相同的ViewPager时,如果每一页都是Fragment的情况下会出现添加异常情况,后面几个ViewPager期望添加的Fragment会因为FragmentManagerImpl中找Fragment的父布局时,由于根据id都找到了第一个ViewPager,而将所有的Fragment都添加到了第一个ViewPager中去。此时就出现了后面几个ViewPager没有内容的情况。

还有种情况是可能让同一个页面中允许存在2个相同id的ViewPager且显示正常的。当且仅当两个ViewPager传入的FragmentManger不是同一个的情况(例如一个传入的是Activity的getSupportFragmentManager,另一个传入的是Fragment的getChildFragmentManager),当FragmentManger不为同一个的情况时,在步骤1中就会因为从不同的根布局中寻找各自的子View,这个时候找到的ViewPager就不会是同一个,因而添加Fragment的显示逻辑就会正常。

20、ImageView设置ImageResource的时候是否引起重绘

需要根据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();
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容