Toolbar 中 style 的自定义及加载过程
304501989
8年前
<p>不久前,公司里设计师大大踌躇满志的说我们的app要改版。首当其冲的就是主题色的改变,由红色改为白色。并且界面改为扁平风格。那么意味着需要对所有界面里的ActionBar或者ToolBar都要进行主题样式与elevation的修改。最后的结果如下图:</p> <p><img src="https://simg.open-open.com/show/eeaa14ce00d9a6a00ed6b1725f0eadc3.png"></p> <p>最简便的方法自然是定义style来更换,如果都在Java代码里去改的话工作量变多不说且都是重复的劳动。也不利于代码的维护。</p> <h3>一.定义Style</h3> <p>1.ActionBar的Style定义</p> <p>4.4及以下版本style.xml:</p> <pre> <code class="language-java"><style name="AppTheme.ActionBar" parent="AppTheme"> <!--ActionBar是否悬浮覆盖在你的布局上--> <item name="windowActionBarOverlay">false</item> <!--定义ActionBar窗体的背景--> <item name="android:windowContentOverlay">@null</item> <!--定义ActionBar的样式--> <item name="actionBarStyle">@style/AppTheme.ActionBarStyle</item> <!--定义Actionbar上Menu的字体样式--> <item name="actionMenuTextAppearance">@style/AppTheme.MyActionBarMenuTextStyle</item> <!--定义Actionbar上Menu的字体颜色--> <item name="actionMenuTextColor">@color/wechat_color</item> <!--定义Actionbar上回退按钮的图片--> <item name="homeAsUpIndicator">@drawable/icon_back_black</item> </style> <style name="AppTheme.ActionBarStyle" parent="Widget.AppCompat.Light.ActionBar"> <!--定义Actionbar上Title的样式--> <item name="titleTextStyle">@style/AppTheme.ActionBarTitleTextStyle</item> <!--定义Actionbar的背景,R.drawable.bg_frame是最底部为横线,其余位置是透明的.9图片 --> <item name="background">@drawable/bg_frame</item> </style></code></pre> <p>5.0及以上版本的style.xml:</p> <pre> <code class="language-java"><style name="AppTheme.ActionBar" parent="AppTheme"> <!--ActionBar是否悬浮覆盖在你的布局上--> <item name="windowActionBarOverlay">false</item> <!--定义ActionBar的样式--> <item name="actionBarStyle">@style/AppTheme.ActionBarStyle</item> <!--定义Actionbar上Menu的字体样式--> <item name="actionMenuTextAppearance">@style/AppTheme.ActionBarMenuTextStyle</item> <!--定义Actionbar上Menu的字体颜色--> <item name="actionMenuTextColor">@color/wechat_color</item> <!--定义Actionbar上回退按钮的图片--> <item name="homeAsUpIndicator">@drawable/icon_back_black</item> </style> <style name="AppTheme.ActionBarStyle" parent="@style/Widget.AppCompat.Light.ActionBar"> <item name="titleTextStyle">@style/AppTheme.ActionBarTitleTextStyle</item> <item name="background">@color/colorPrimary</item> <item name="elevation">1dp</item> </style></code></pre> <p>主题色:</p> <pre> <code class="language-java"><style name="AppTheme" parent="Theme.AppCompat.Light"> <!--主题色--> <item name="colorPrimary">@color/colorPrimary</item> <!--强调色--> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <!--点缀色--> <item name="colorAccent">@color/colorAccent</item> </style></code></pre> <p>文字样式的定义:</p> <pre> <code class="language-java"><style name="AppTheme.ActionBarTitleTextStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Title"> <item name="android:textSize">16sp</item> <item name="android:textColor">@color/非死book_color</item> </style> <style name="AppTheme.ActionBarMenuTextStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Menu"> <item name="android:textSize">12sp</item> </style></code></pre> <p>在Activity中引用style:</p> <pre> <code class="language-java"><activity android:name=".ActionBarActivity" android:theme="@style/AppTheme.ActionBar" /></code></pre> <p>注意:Android4.4版本及以下ActionBar取消elevation必须设置windowContentOverlay属性为null,这样背景就是默认的灰色了,所以还需要自己定义ActionBar的背景,如果在ActionBar底部需要分割线还要做一个.9的图片设置为ActionBar的背景。</p> <p>2.ToolBar的Style定义</p> <p>在style.xml中定义ToolBar样式:</p> <pre> <code class="language-java"><style name="AppTheme.ToolBar" parent="Theme.AppCompat.Light.NoActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <!--定义ToolBar上Menu的字体样式--> <item name="actionMenuTextAppearance">@style/AppTheme.MyActionBarMenuTextStyle</item> <!--定义ToolBar上Menu的字体颜色--> <item name="actionMenuTextColor">@color/wechat_color</item> <!--定义ToolBar上回退按钮的图片--> <item name="homeAsUpIndicator">@drawable/icon_back_black</item> </style> <!--定义ToolBar上Title的文字样式--> <style name="AppTheme.ToolbarTitleTextStyle" parent="TextAppearance.Widget.AppCompat.Toolbar.Title"> <item name="android:textSize">16sp</item> <item name="android:textColor">@color/非死book_color</item> </style></code></pre> <p>在layout文件中设置ToolBar的文字样式:</p> <pre> <code class="language-java"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white" android:orientation="vertical"> <android.support.design.widget.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:stateListAnimator="@animator/appbar_elevation"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="?attr/actionBarSize" app:titleTextAppearance="@style/AppTheme.ToolbarTitleTextStyle" /> </android.support.design.widget.AppBarLayout> </LinearLayout></code></pre> <p>在Activity中引用style,并且Activity继承supportV7包中的 <strong>AppCompatActivity</strong> :</p> <pre> <code class="language-java"><activity android:name=".ToolBarActivity" android:theme="@style/AppTheme.ToolBar" /></code></pre> <p>注意:Android4.4版本及以下因为不支持elevation属性,所以需要添加下面的代码才能显示ToolBar底部的横线。R.drawable.bg_frame是最底部为横线,背景透明的.9图片:</p> <pre> <code class="language-java">if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { ViewCompat.setBackground(findViewById(R.id.appbar), ContextCompat.getDrawable(this, R.drawable.bg_frame)); }</code></pre> <p>在Demo中最后的效果如下图,可以看到返回按钮、Title、Menu上的文字大小和颜色都是style定义后的。</p> <p><img src="https://simg.open-open.com/show/6b08f63095b980019a275841ec8e62d5.png"></p> <h3>二.遇到的坑</h3> <ol> <li>定义ActionBar样式时,4.4及以下版本手机设置了<item name="android:windowContentOverlay">@null</item>后,必须为ActionBar设置背景,否则ActionBar是默认的颜色</li> <li>4.4及以下版本手机不支持elevation属性,所以在ActionBar底部的分割线或阴影效果需要自己做图片。</li> <li>Menu菜单的文字颜色必须通过actionMenuTextColor属性设置,在actionMenuTextAppearance属性中设置textColor无效。</li> <li>ToolBar的文字样式titleTextAppearance必须在layout布局文件中引用,如果像Actionbar一样只在style.xml中定义是无效的。</li> <li>AppBarLayout的背景和elevation是由stateListAnimator控制的,如果需要改变elevation高度必须自定义stateListAnimator。因为stateListAnimator是5.0版本后的属性,4.4及以下版本手机必须重新设置AppBarLayout的background属性。</li> </ol> <h3>三.源码之下,了无秘密</h3> <p>修改Actionbar的样式固然很快,但是为了知道为什么会有上面写到的在定义style属性时遇到的坑,所以带着问题看看源码,在源码之下我们可以了解到ActionBar或ToolBar中的Style在AppCompatActivity中是如何被加载的。</p> <p>注意:下面贴出的源码不是完整的google官方代码,只截取了关键部分。</p> <p>首先AppCompatActivity在执行onCreate方法时创建了 <strong>AppCompatDelegate</strong> 对象,并进行了AppCompatDelegate的初始化。</p> <p>AppCompatDelegate相当于一个委托,appcompat适配包中一些方法委托AppCompatDelegate来调用。</p> <pre> <code class="language-java">protected void onCreate(@Nullable Bundle savedInstanceState) { final AppCompatDelegate delegate = getDelegate();//执行AppCompatDelegate.create(); //初始化工作 delegate.installViewFactory(); delegate.onCreate(savedInstanceState); super.onCreate(savedInstanceState); } //根据Android系统版本创建AppCompatDelegate对象 private static AppCompatDelegate create(Context context, Window window, AppCompatCallback callback) { final int sdk = Build.VERSION.SDK_INT; if (BuildCompat.isAtLeastN()) { return new AppCompatDelegateImplN(context, window, callback); } else if (sdk >= 23) { return new AppCompatDelegateImplV23(context, window, callback); } else if (sdk >= 14) { return new AppCompatDelegateImplV14(context, window, callback); } else if (sdk >= 11) { return new AppCompatDelegateImplV11(context, window, callback); } else { return new AppCompatDelegateImplV9(context, window, callback); } }</code></pre> <p>onCreate结束之后,在Activity中调用setContentView()方法后,AppCompatActivity中会执行ensureSubDecor()方法,这个方法具体做了ActionBar窗体的创建,并使ActionBar依附到屏幕的Window中去。</p> <pre> <code class="language-java">public void setContentView(int resId) { ensureSubDecor(); ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content); contentParent.removeAllViews(); LayoutInflater.from(mContext).inflate(resId, contentParent); mOriginalWindowCallback.onContentChanged(); } private void ensureSubDecor() { if (!mSubDecorInstalled) { mSubDecor = createSubDecor(); onSubDecorInstalled(mSubDecor); mSubDecorInstalled = true; } } private ViewGroup createSubDecor() { //加载Activity的主题 TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme); if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) { a.recycle(); throw new IllegalStateException( "You need to use a Theme.AppCompat theme (or descendant) with this activity."); } //读取windowNoTitle属性,判断是否需要ActionBar if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) { requestWindowFeature(Window.FEATURE_NO_TITLE); } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) { // Don't allow an action bar if there is no title. requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR); } //读取windowActionBarOverlay属性 if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) { requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY); } //读取windowActionModeOverlay属性 if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) { requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY); } //读取android:windowIsFloating属性 mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false); a.recycle(); // Now let's make sure that the Window has installed its decor by retrieving it mWindow.getDecorView(); final LayoutInflater inflater = LayoutInflater.from(mContext); ViewGroup subDecor = null; if (!mWindowNoTitle) { //当需要ActionBar时执行下面的逻辑 if (mIsFloating) { //省略 } else if (mHasActionBar) { //读取ActionBar主题 TypedValue outValue = new TypedValue(); mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true); //加载ActionBar窗体的布局文件R.layout.abc_screen_toolbar subDecor = (ViewGroup) LayoutInflater.from(themedContext).inflate(R.layout.abc_screen_toolbar, null); mDecorContentParent = (DecorContentParent) subDecor.findViewById(R.id.decor_content_parent); mDecorContentParent.setWindowCallback(getWindowCallback()); } } else { //省略 } //在该方法的最后会把ActionBar的窗体加载到屏幕的整个Window中去 mWindow.setContentView(subDecor); return subDecor; }</code></pre> <p>上面的代码我们看到映射了一个名为abc_screen_toolbar.xml的布局文件,在这个xml布局文件中引用了Toolbar,并且可以看到声明了属性style="?attr/toolbarStyle",那么是不是这个toolBarStyle就决定了ToolBar的样式呢?我们继续往下看。</p> <pre> <code class="language-java"><android.support.v7.widget.ActionBarOverlayLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/decor_content_parent" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <include layout="@layout/abc_screen_content_include"/> <android.support.v7.widget.ActionBarContainer android:id="@+id/action_bar_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" style="?attr/actionBarStyle" android:touchscreenBlocksFocus="true" android:gravity="top"> <android.support.v7.widget.Toolbar android:id="@+id/action_bar" android:layout_width="match_parent" android:layout_height="wrap_content" app:navigationContentDescription="@string/abc_action_bar_up_description" style="?attr/toolbarStyle"/> <android.support.v7.widget.ActionBarContextView android:id="@+id/action_context_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="gone" android:theme="?attr/actionBarTheme" style="?attr/actionModeStyle"/> </android.support.v7.widget.ActionBarContainer> </android.support.v7.widget.ActionBarOverlayLayout></code></pre> <p>接下来会执行Toolbar的构造函数,其中引用的style也是在xml布局文件里所声明的。</p> <pre> <code class="language-java">public Toolbar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); final TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,R.styleable.Toolbar, defStyleAttr, 0); //标题文字样式 mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0); mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0); //标题 final CharSequence title = a.getText(R.styleable.Toolbar_title); if (!TextUtils.isEmpty(title)) { setTitle(title); } //设置返回按钮的Icon final Drawable navIcon = a.getDrawable(R.styleable.Toolbar_navigationIcon); if (navIcon != null) { setNavigationIcon(navIcon); } //设置title文字颜色 if (a.hasValue(R.styleable.Toolbar_titleTextColor)) { setTitleTextColor(a.getColor(R.styleable.Toolbar_titleTextColor, 0xffffffff)); } //设置subtitle文字颜色 if (a.hasValue(R.styleable.Toolbar_subtitleTextColor)) { setSubtitleTextColor(a.getColor(R.styleable.Toolbar_subtitleTextColor, 0xffffffff)); } }</code></pre> <p>Toolbar构造完成后绘制 <strong>ActionBarOverlayLayout</strong> 时,会调用ActionBarOverlayLayout中的pullChildren()与getDecorToolbar()两个方法,在为全局变量mDecorToolbar 赋值时会创建一个Toolbar的包装器 <strong>ToolbarWidgetWrapper</strong> 。</p> <pre> <code class="language-java">void pullChildren() { if (mContent == null) { mContent = (ContentFrameLayout) findViewById(R.id.action_bar_activity_content); mActionBarTop = (ActionBarContainer) findViewById(R.id.action_bar_container); mDecorToolbar = getDecorToolbar(findViewById(R.id.action_bar)); } } private DecorToolbar getDecorToolbar(View view) { if (view instanceof Toolbar) { return ((Toolbar) view).getWrapper(); } } public DecorToolbar getWrapper() { if (mWrapper == null) { mWrapper = new ToolbarWidgetWrapper(this, true); } return mWrapper; }</code></pre> <p>在Toolbar的包装器 <strong>ToolbarWidgetWrapper</strong> 中,会加载actionBarStyle这个style中的属性,如homeAsUpIndicator定义了回退键的图片,titleTextStyle定义了Title的样式。所以ActionBar的样式最后加载的style都是在这个ToolbarWidgetWrapper中完成的。</p> <pre> <code class="language-java">public ToolbarWidgetWrapper(Toolbar toolbar, boolean style,int defaultNavigationContentDescription, int defaultNavigationIcon) { mToolbar = toolbar; mTitle = toolbar.getTitle(); mSubtitle = toolbar.getSubtitle(); mTitleSet = mTitle != null; mNavIcon = toolbar.getNavigationIcon(); final TintTypedArray a = TintTypedArray.obtainStyledAttributes(toolbar.getContext(), null, R.styleable.ActionBar, R.attr.actionBarStyle, 0); mDefaultNavigationIcon = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator); if (style) { final CharSequence title = a.getText(R.styleable.ActionBar_title); if (!TextUtils.isEmpty(title)) { setTitle(title); } final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle); if (!TextUtils.isEmpty(subtitle)) { setSubtitle(subtitle); } final Drawable logo = a.getDrawable(R.styleable.ActionBar_logo); if (logo != null) { setLogo(logo); } final Drawable icon = a.getDrawable(R.styleable.ActionBar_icon); if (icon != null) { setIcon(icon); } if (mNavIcon == null && mDefaultNavigationIcon != null) { setNavigationIcon(mDefaultNavigationIcon); } setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, 0)); final int customNavId = a.getResourceId( R.styleable.ActionBar_customNavigationLayout, 0); if (customNavId != 0) { setCustomView(LayoutInflater.from(mToolbar.getContext()).inflate(customNavId,mToolbar, false)); setDisplayOptions(mDisplayOpts | ActionBar.DISPLAY_SHOW_CUSTOM); } final int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0); if (height > 0) { final ViewGroup.LayoutParams lp = mToolbar.getLayoutParams(); lp.height = height; mToolbar.setLayoutParams(lp); } final int contentInsetStart = a.getDimensionPixelOffset(R.styleable.ActionBar_contentInsetStart, -1); final int contentInsetEnd = a.getDimensionPixelOffset(R.styleable.ActionBar_contentInsetEnd, -1); if (contentInsetStart >= 0 || contentInsetEnd >= 0) { mToolbar.setContentInsetsRelative(Math.max(contentInsetStart, 0), Math.max(contentInsetEnd, 0)); } final int titleTextStyle = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0); if (titleTextStyle != 0) { mToolbar.setTitleTextAppearance(mToolbar.getContext(), titleTextStyle); } final int subtitleTextStyle = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0); if (subtitleTextStyle != 0) { mToolbar.setSubtitleTextAppearance(mToolbar.getContext(), subtitleTextStyle); } final int popupTheme = a.getResourceId(R.styleable.ActionBar_popupTheme, 0); if (popupTheme != 0) { mToolbar.setPopupTheme(popupTheme); } } else { mDisplayOpts = detectDisplayOptions(); } a.recycle(); }</code></pre> <p>最后在调用getSupportActionBar()时会进入initWindowDecorActionBar()方法。ActionBar是一个抽象类, <strong>WindowDecorActionBar</strong> 则是ActionBar的具体实现。在init过程中看到了对elevation的设置。</p> <pre> <code class="language-java">public void initWindowDecorActionBar() { ensureSubDecor(); if (!mHasActionBar || mActionBar != null) { return; } if (mOriginalWindowCallback instanceof Activity) { mActionBar = new WindowDecorActionBar((Activity) mOriginalWindowCallback, mOverlayActionBar); } } public WindowDecorActionBar(Activity activity, boolean overlayMode) { mActivity = activity; Window window = activity.getWindow(); View decor = window.getDecorView(); init(decor); if (!overlayMode) { mContentView = decor.findViewById(android.R.id.content); } } private void init(View decor) { mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById(R.id.decor_content_parent); mDecorToolbar = getDecorToolbar(decor.findViewById(R.id.action_bar)); mContextView = (ActionBarContextView) decor.findViewById(R.id.action_context_bar); mContainerView = (ActionBarContainer) decor.findViewById(R.id.action_bar_container); mContext = mDecorToolbar.getContext(); final int elevation = a.getDimensionPixelSize(R.styleable.ActionBar_elevation, 0); if (elevation != 0) { setElevation(elevation); } a.recycle(); } public void setElevation(float elevation) { ViewCompat.setElevation(mContainerView, elevation); }</code></pre> <p>如果ActionBar上有Menu时,会调用ToolbarWidgetWrapper中setMenu方法,执行ActionMenuPresenter的构造函数,ActionMenuPresenter构造函数中第二个参数是每个menu item的父布局,第三个参数就对应某个menu item的布局。Menu item的xml布局中引用了 <strong>actionMenuTextAppearance actionMenuTextColor</strong> 两个属性,所以知道了,menu item的字体和颜色是由这两个属性控制的。</p> <pre> <code class="language-java">public void setMenu(Menu menu, MenuPresenter.Callback cb) { if (mActionMenuPresenter == null) { mActionMenuPresenter = new ActionMenuPresenter(mToolbar.getContext()); mActionMenuPresenter.setId(R.id.action_menu_presenter); } mActionMenuPresenter.setCallback(cb); mToolbar.setMenu((MenuBuilder) menu, mActionMenuPresenter); } public ActionMenuPresenter(Context context) { super(context, R.layout.abc_action_menu_layout, R.layout.abc_action_menu_item_layout); } <android.support.v7.internal.view.menu.ActionMenuItemView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" android:focusable="true" android:paddingTop="4dip" android:paddingBottom="4dip" android:paddingLeft="8dip" android:paddingRight="8dip" android:textAppearance="?attr/actionMenuTextAppearance" android:textColor="?attr/actionMenuTextColor" style="?attr/actionButtonStyle"/></code></pre> <p>为什么在style.xml中定义toolbar的style无效,只能在toolbar的布局文件中引用呢?是因为Toolbar读取的是layout布局文件中的style,并且在构造ToolbarWidgetWrapper对象时也并不会和ActionBar一样去读取actionbar的属性。因为在调用setSupportActionBar()后会构造一个ToolbarActionBar,ToolbarActionBar中又会构造一个ToolbarWidgetWrapper,而ToolbarWidgetWrapper的构造函数中第二个参数在源码中传入的是false,所以不会在ToolbarWidgetWrapper进行style的加载,只会在创建Toolbar时进行style的加载。</p> <pre> <code class="language-java">public void setSupportActionBar(Toolbar toolbar) { if (toolbar != null) { final ToolbarActionBar tbab = new ToolbarActionBar(toolbar,((Activity) mOriginalWindowCallback).getTitle(), mAppCompatWindowCallback); } invalidateOptionsMenu(); } public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback callback) { mDecorToolbar = new ToolbarWidgetWrapper(toolbar, false); } public ToolbarWidgetWrapper(Toolbar toolbar, boolean style,int defaultNavigationContentDescription, int defaultNavigationIcon) { if (style) { //不执行 final CharSequence title = a.getText(R.styleable.ActionBar_title); if (!TextUtils.isEmpty(title)) { setTitle(title); } final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle); if (!TextUtils.isEmpty(subtitle)) { setSubtitle(subtitle); } ... } else { mDisplayOpts = detectDisplayOptions(); } a.recycle(); }</code></pre> <h3>最后:</h3> <p>更详细的参考 <a href="/misc/goto?guid=4959746110202391214" rel="nofollow,noindex">Demo</a> 在github中,如果有错误也希望大家能够指出,觉得能帮到你的话给个Star吧。</p> <p> </p> <p>项目主页:<a href="http://www.open-open.com/lib/view/home/1490749004792">http://www.open-open.com/lib/view/home/1490749004792</a></p> <p></p>