Android ActionBar的源代码分析(一)

单波束与多波束声呐:从原理到应用场景的全面解析 本文全面解析了单波束与多波束声呐的工作原理、核心特点及应用场景。单波束声呐如精准的狙击手,适合高精度需求;多波束声呐则像高效的散弹枪,适用于快速测绘和大面积覆盖。文章还提供了选型指南和前沿发展趋势,帮助用户根据预算和需求选择最适合的声呐技术。 阅读详情

ActionBar是android的一个非常重要开发组件,在很多商业应用中到处可见,也是很多android开发人员必须熟练掌握的开发技术,下面就从源码角度来分析ActionBar的实现过程。

从哪里开始呢?

我们回忆一下在Activity中获取ActionBar的方法为getActionBar(),那就从getActionBar()开始研究吧!

打开getActionBar()的代码实现如下:

    public ActionBar getActionBar() {
        initActionBar();
        return mActionBar;
    }

就简简单单的调用了initActionBar()方法,然后返回mActionBar了。那么mActionBar应该是在initActionBar()中进行实例化的了,我们看一下initActionBar()的代码

    private void initActionBar() {
        Window window = getWindow();

        window.getDecorView();

        if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
            return;
        }
        
        mActionBar = new ActionBarImpl(this);
        mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
    }
代码首先是获取窗口类window,然后初始化decorView,接着判断是否嵌套Activity,或者是否使用ActionBar或者mActionBar是否为空,由于是第一次调用,默认情况下这三个条件返回的结果是false;接着就进行mActionBar的实例化操作,可以看到,mActionBar的具体实现交由ActionBarImpl来进行了

抽象类Window的具体实现类是哪个?

这里需要看一下window的具体实现类是哪个呢?我们看到Window这个类是个抽象类,具体实现类是由Activity执行attach()方法的时候才实例化的,我们看一下Activity的attach方法代码

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config) {
        attachBaseContext(context);

        mFragments.attachActivity(this, mContainer, null);
        
        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();
        
        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;

        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }
可以看到window是在Activity在执行attach方法的时候创建并初始化的,这里就调用了一个PolicyManager.makeNewWindow(this)方法进行创建window,跟踪到类PolicyManager的方法makeNewWindow中

public final class PolicyManager {
    private static final String POLICY_IMPL_CLASS_NAME =
        "com.android.internal.policy.impl.Policy";

    private static final IPolicy sPolicy;

    static {
        try {
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
            sPolicy = (IPolicy)policyClass.newInstance();
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(
                    POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
        } catch (InstantiationException ex) {
            throw new RuntimeException(
                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(
                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
        }
    }

    private PolicyManager() {}

    public static Window makeNewWindow(Context context) {
        return sPolicy.makeNewWindow(context);
    }

    public static LayoutInflater makeNewLayoutInflater(Context context) {
        return sPolicy.makeNewLayoutInflater(context);
    }

    public static WindowManagerPolicy makeNewWindowManager() {
        return sPolicy.makeNewWindowManager();
    }

    public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {
        return sPolicy.makeNewFallbackEventHandler(context);
    }
}
原来策略管理器PolicyManager在static <init>中使用Class.forName()方法动态加载了“com.android.internal.policy.impl.Policy”这个类,并进行实例化,后面的几个静态方法都交由这个类来实现,我们看一下里面的代码:

public class Policy implements IPolicy {
    private static final String TAG = "PhonePolicy";

    private static final String[] preload_classes = {
        "com.android.internal.policy.impl.PhoneLayoutInflater",
        "com.android.internal.policy.impl.PhoneWindow",
        "com.android.internal.policy.impl.PhoneWindow$1",
        "com.android.internal.policy.impl.PhoneWindow$DialogMenuCallback",
        "com.android.internal.policy.impl.PhoneWindow$DecorView",
        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",
        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",
    };

    static {
        for (String s : preload_classes) {
            try {
                Class.forName(s);
            } catch (ClassNotFoundException ex) {
                Log.e(TAG, "Could not preload class for phone policy: " + s);
            }
        }
    }

    public Window makeNewWindow(Context context) {
        return new PhoneWindow(context);
    }

    public LayoutInflater makeNewLayoutInflater(Context context) {
        return new PhoneLayoutInflater(context);
    }

    public WindowManagerPolicy makeNewWindowManager() {
        return new PhoneWindowManager();
    }

    public FallbackEventHandler makeNewFallbackEventHandler(Context context) {
        return new PhoneFallbackEventHandler(context);
    }
}
看到这里我们终于有一种恍然大悟的感觉,我们回忆一下,Activity在attach方法调用PolicyManager.makeNewWindow(this)实例化window类,其实PolicyManager是个stub,真正的实现是交由Policy来进行的,最后的结果应该是 Window mWindow = new PhoneWindow(this)

上面类关系如下图:



调用时序图如下:

new ActionBarImpl()到底干了什么?

回到上面,我们在Activity.initActionBar()方法中,调用类ActionBarImpl的构造方法进行了实例化,并把实例化对象赋值给了mActionBar,这里看一下new ActionBarImpl(this)到底做了什么。

public ActionBarImpl(Activity activity) {
        mActivity = activity;
        Window window = activity.getWindow();
        View decor = window.getDecorView();
        init(decor);
        if (!mActivity.getWindow().hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY)) {
            mContentView = decor.findViewById(android.R.id.content);
        }
    }
这里传入Activity参数,并调用activity.getWindow()获取到窗口对象window,从上面可以知道,这个window就是PhoneWindow;接着调用window.getDecorView()获取decorView并调用init()方法进行初始化,我们看一下init()方法做了哪些初始化操作吧

    private void init(View decor) {
        mContext = decor.getContext();
        mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById(
                com.android.internal.R.id.action_bar_overlay_layout);
        if (mOverlayLayout != null) {
            mOverlayLayout.setActionBar(this);
        }
        mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
        mContextView = (ActionBarContextView) decor.findViewById(
                com.android.internal.R.id.action_context_bar);
        mContainerView = (ActionBarContainer) decor.findViewById(
                com.android.internal.R.id.action_bar_container);
        mTopVisibilityView = (ViewGroup)decor.findViewById(
                com.android.internal.R.id.top_action_bar);
        if (mTopVisibilityView == null) {
            mTopVisibilityView = mContainerView;
        }
        mSplitView = (ActionBarContainer) decor.findViewById(
                com.android.internal.R.id.split_action_bar);

        if (mActionView == null || mContextView == null || mContainerView == null) {
            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
                    "with a compatible window decor layout");
        }

        mActionView.setContextView(mContextView);
        mContextDisplayMode = mActionView.isSplitActionBar() ?
                CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;

        // This was initially read from the action bar style
        final int current = mActionView.getDisplayOptions();
        final boolean homeAsUp = (current & DISPLAY_HOME_AS_UP) != 0;
        if (homeAsUp) {
            mDisplayHomeAsUpSet = true;
        }

        ActionBarPolicy abp = ActionBarPolicy.get(mContext);
        setHomeButtonEnabled(abp.enableHomeButtonByDefault() || homeAsUp);
        setHasEmbeddedTabs(abp.hasEmbeddedTabs());
    }

这里就是根据布局文件查找相应的控件并赋值给相应的成员变量,这里的关键是:布局文件是哪个呢?

decorView的布局

这就要看一下 Window.getDecorView()里是怎么实现的了,也就是PhoneWindow.getDecorView()的实现代码如下:

    @Override
    public final View getDecorView() {
        if (mDecor == null) {
            installDecor();
        }
        return mDecor;
    }
初始的时候mDecor=null,就是调用installDecor()方法进行初始化。

    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            mDecor.makeOptionalFitsSystemWindows();
<span style="white-space:pre">	</span>......

这里首先调用generateDecor()方法进行实例化DecorView,其实类DecorView是FrameLayout的派生类,也就是说是一个ViewGroup;然后调用generateLayout()获取布局文件,代码如下:

    protected ViewGroup generateLayout(DecorView decor) {
		......

        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = com.android.internal.R.layout.screen_title_icons;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
            // System.out.println("Title Icons!");
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            // Special case for a window with only a progress bar (and title).
            // XXX Need to have a no-title version of embedded windows.
            layoutResource = com.android.internal.R.layout.screen_progress;
            // System.out.println("Progress!");
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = com.android.internal.R.layout.screen_custom_title;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            // If no other features and not embedded, only need a title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {
                    layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;
                } else {
                    layoutResource = com.android.internal.R.layout.screen_action_bar;
                }
            } else {
                layoutResource = com.android.internal.R.layout.screen_title;
            }
            // System.out.println("Title!");
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = com.android.internal.R.layout.screen_simple;
            // System.out.println("Simple!");
        }

        mDecor.startChanging();

        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

        ......
    }
这个方法代码很长,我们只看关键部分的代码;从上面代码可以看出,根据window中不同的features加载不同的布局文件,比如当features=FEATURE_ACTION_BAR时,加载的布局文件com.android.internal.R.layout.screen_action_bar,即screen_action_bar.xml,这个文件在哪呢?其实就在对应的sdk版本下的data\res\layout下,比如我的是在E:\android-dev\sdk\platforms\android-17\data\res\layout目录下,打开该文件:

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
          http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<!--
This is an optimized layout for a screen with the Action Bar enabled.
-->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:fitsSystemWindows="true"
    android:splitMotionEvents="false">
    <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/actionBarStyle">
        <com.android.internal.widget.ActionBarView
            android:id="@+id/action_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            style="?android:attr/actionBarStyle" />
        <com.android.internal.widget.ActionBarContextView
            android:id="@+id/action_context_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:visibility="gone"
            style="?android:attr/actionModeStyle" />
    </com.android.internal.widget.ActionBarContainer>
    <FrameLayout android:id="@android:id/content"
        android:layout_width="match_parent" 
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foregroundGravity="fill_horizontal|top"
        android:foreground="?android:attr/windowContentOverlay" />
    <com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  style="?android:attr/actionBarSplitStyle"
                  android:visibility="gone"
                  android:gravity="center"/>
</LinearLayout>

从这个布局文件中可以看到,整个window的根布局为DecorView,然后DecorView里又分为ActionBar区域、Content区域和SplitActionBar区域,其中ActionBar区域放置ActionBarView或者ActionBarContextView,Content区域放置的就是我们使用Activity时自己设计的Layout,SplitActionBar区域放置的是Split形式的ActionBar;整个结构如下图:


到现在为止,我们已经知道了整个Activity的布局,那么ActionBarView的布局又是什么样的呢?

ActionBarView的布局

我们打开ActionBarView的代码来看看。从ActionBarView的代码看出,ActionBarView的布局并不是通过LayoutInflater.inflate xml的方式来创建的,而是通过addView的方式来创建的,通过在代码ActionBarView.java中搜索addView方法,可以看到有以下几个方法调用了addView:

  • initProgress(),这是初始化水平进度条的方法,初始状态下ProgressBar的visibility为View.GONE,即不可见,它是在PhoneWindow的installDecor方法中调用的,关于installDecor方法,前面已经提到。有朋友问,那怎么使用ActionBar上的水平ProgressBar呢?很简单,直接调用Activity.setProgressBarVisibility(true)就可以显示ProgressBar,Activity.setProgressBarVisibility(false)就隐藏ProgressBar了,下图是setProgressBarVisibility的调用过程:
            
  • initIndeterminateProgress(),这是初始化圆形进度条的方法,初始状态下ProgressBar的visibility为View.GONE,即不可见。使用ActionBar上圆形的ProgressBar方式就是在Activity.setProgressBarIndeterminateVisibility(true),隐藏进度条的方式为Activity.setProgressBarIndeterminateVisibility(false),该方法的调用过程跟setProgressBarVisibility()一样;
  • setMenu(),这是设置ActionBar上菜单的方法。在这个方法中,系统会把menuView放到ActionBar中,这个menuView的类型为ActionMenuView,而ActionMenuView继承于类LinearLayout,也就是说ActionMenuView是一个视图容器,它里面的菜单项menu是由类ActionMenuPresenter来构造的,关于ActionMenuPresenter和ActionMenuView构造菜单的过程,后面章节再进行介绍;这个方法的调用过程如下:
  • setSplitActionBar(),这是初始化splitActionBar的方法,它是在PhoneWindow的installDecor方法中调用的;从上面我们知道,这个splitActionBar是放在decorView的最下面。在这个方法中,系统会把menuView放到splitActionBar中,这个menuView就是上面setMenu提到的menuView。当我们在AndroidManifest.xml中设置Activity的属性android:uiOptions="splitActionBarWhenNarrow"时(对于API<14时,需要在activity节点中添加如下节点: <meta-dataandroid:name="android.support.UI_OPTIONS"android:value="splitActionBarWhenNarrow">),menuView就放置到splitActionBar中,否则就会放在ActionBar上;
  • setEmbeddedTabView(),当NavigationMode=NAVIGATION_MODE_TABS(页签)时,这个方法才有效,它负责把Tab容器添加到ActionBarView中,传入参数tabs是一个ScrollingTabContainerView类型的对象,ScrollingTabContainerView继承于HorizontalScrollView,可以看出它是一个具备水平滚动条的tab容器;当ActionBarPolicy的方法hasEmbeddedTabs返回true时,tab就会embed到ActionBarView上,否则就会放到ActionBarContainer上,这个方法的调用过程如下:
  • setCustomNavigationView(),这个方法是把自定义view放置到ActionBarView上,它是由ActionBarImpl.setCustomView()方法调用的;调用过程如下:
  • setNavigationMode(),这是设置ActionBar导航模式的方法,对于mode的值,有三个值可选:NAVIGATION_MODE_STANDARD(标准)、NAVIGATION_MODE_LIST(列表)、NAVIGATION_MODE_TABS(页签),当mode=NAVIGATION_MODE_LIST时,就会把spinner控件放置到ActionBarView上;当mode=NAVIGATION_MODE_TABS时,就会把TabScrollView控件放置到ActionBarView上;调用过程如下:
  • setDisplayOptions(),这是设置ActionBar显示选项的方法。其中当传入参数options为DISPLAY_SHOW_CUSTOM(即显示自定义视图)时,就会把主题上定义的自定义视图布局放置到ActionBarView上。这里啰嗦一句,主题上怎么定义ActionBarView的自定义视图呢?这里先把结果告诉大家,至于actionbar主题风格的原理,后面章节在做介绍:
        <style name="custom" parent="android:Theme.Holo.Light.DarkActionBar">
            <item name="android:actionBarStyle">@style/MyActionBar</item>
        </style>
        
        <style name="MyActionBar" parent="<span style="font-family: Arial, Helvetica, sans-serif;">android:</span><span style="font-family: Arial, Helvetica, sans-serif;">Widget.Holo.Light.ActionBar.Solid.Inverse"></span>
            <item name="android:customNavigationLayout">@layout/activity_normal</item>
        </style>
    就是设置customNavigationLayout属性为你自定义的布局,然后在AndroidManifest.xml中的Activity添加android:theme="@style/custom"即可

  • onFinishInflate(),这是对布局进行inflate后回调的方法,在这个方法中,会把mHomeLayout先添加到mUpGoerFive,然后再把mUpGoerFive放置到ActionBarView中,这个mUpGoerFive是一个ViewGroup,它里面包含两个视图:mHomeLayout和mTitleLayout,其中mHomeLayout包含两个ImageView:mUpView(即返回的指示图标)和mIconView(默认情况下是应用程序图标),这两个View分别可以通过getActionBar().setDisplayHomeAsUpEnabled()和getActionBar().setDisplayShowHomeEnabled()来设置是否显示;而mTitleLayout是一个LinearLayout,它的组成(见文件\sdk\platforms\android-17\data\res\layout\action_bar_title_item.xml)如下:
    <?xml version="1.0" encoding="utf-8"?>
    <!-- Copyright (C) 2010 The Android Open Source Project
    
         Licensed under the Apache License, Version 2.0 (the "License");
         you may not use this file except in compliance with the License.
         You may obtain a copy of the License at
      
              http://www.apache.org/licenses/LICENSE-2.0
      
         Unless required by applicable law or agreed to in writing, software
         distributed under the License is distributed on an "AS IS" BASIS,
         WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         See the License for the specific language governing permissions and
         limitations under the License.
    -->
    
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="wrap_content"
                  android:layout_height="match_parent"
                  android:orientation="horizontal"
                  android:paddingEnd="8dip"
                  android:enabled="false">
    
        <ImageView android:id="@android:id/up"
                   android:src="?android:attr/homeAsUpIndicator"
                   android:layout_gravity="center_vertical|start"
                   android:visibility="gone"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content" />
    
        <LinearLayout android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:layout_gravity="center_vertical|start"
                      android:orientation="vertical">
            <TextView android:id="@+id/action_bar_title"
                      android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:singleLine="true"
                      android:ellipsize="end" />
            <TextView android:id="@+id/action_bar_subtitle"
                      android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:layout_marginTop="@dimen/action_bar_subtitle_top_margin"
                      android:singleLine="true"
                      android:ellipsize="end"
                      android:visibility="gone" />
        </LinearLayout>
    </LinearLayout>
    可以看到标题包含一个ImageView和两个TextView,这两个TextView分别为主标题视图和子标题视图,以下就是这个mUpGoerFive的组成:
  • 内部类ExpandedActionViewMenuPresenter的方法expandItemActionView(),这是当点击MenuItem时调用expandItemActionView()方法,在这个方法中,首先获取到MenuItem的ActionView,然后把ActionView放置到ActionBarView中,通俗一点讲,就是当为菜单项配置ActionView后,在点击该菜单时,就会调用expandItemActionView()方法把这个ActionView显示到ActionBar中;对于菜单装载的详细机制,后面章节会提到。
通过上面的分析,ActionBarView的布局还是挺复杂的,大致分为这几种情况:
  • 普通情况下的布局:

  • 有tab情况下的布局(非嵌入到ActionBarView中):
  • 有tab情况下的布局(嵌入到ActionBarView中)
  • 有导航列表的情况:
  • 有分离式ActionBar的情况:

至此,ActionBar的初始化过程及布局介绍就到处为止,下一篇根据ActionBar涉及的类图来分析ActionBar的运行机制,敬请期待


Plant Simulation 双深位立库开发系列教程-货架参数化 为了方便用户进行货架参数化,我们需要在交互界面上进行参数调整,右键打开RackLane,打开Dialog,在Rack_tab的tab下创建相应的对象,并创建NumberOfRightRack和NumberOfLeftRack作为数据连接对象。通过Rack._3D.VisibleGraphicGroups := ["default"]来切换需要显示的Rack,当不需要显示则可以Rack._3D.VisibleGraphicGroups := [""]即可。首先,加载默认的HBW库到模型中,确保结构树当中有。 阅读详情

相关推荐

k8s架构、工作流程、集群组件详解

Kubernetes(简称K8s,是因为 k 和 s 之间有八个字符)是一个开源的容器编排平台,用于管理云平台中多个主机上的容器化应用程序。它提供了一种机制,用于自动化部署、扩展和管理容器化应用程序的集群。Kubernetes的目标是让部署容器化应用程序变得简单高效。它提供了应用程序部署、规划、更新和维护的基本机制,同时具备强大的扩展性和可靠性。Kubernetes采用主从设备模型(Master-Slave架构),其中Master节点负责核心的调度、管理和运维任务,而Slave节点负责运行应用程序的容器。

qq_51545656的博客 4382

Android学习——ActionBar

ActionBar 1.ActionBar介绍 Android3.0之后,Google对UI导航设计上进行了一系列的改革,其中有一个非常好用的新功能就是引入的ActionBar,他用于取代3.0之前的标题栏,并提供更为丰富的导航效果。 Action bar的主要目的是: 提供一个用于识别应用程序的标示和用户的位置的专用空间。 在不同的应用程序之间提供-致的导航和视觉体验。 突出Activity的...

UUUUUltraman的博客 519

SpringBoot实现电子发票生成

电子发票是近年来比较流行的一种票据形式,它取代了以往传统的纸质发票,具有减少纸张浪费、方便有效查验等优点。本文将介绍如何使用Spring Boot和Vue.js构建一个仿真电子发票生成应用程序,借助微信二维码扫描功能和第三方库,实现用户扫描二维码获取发票信息并填写相关开票信息,最后生成电子发票。在本文中,我们介绍了如何使用Spring Boot和Vue.js构建一个仿真电子发票生成应用程序,借助微信二维码扫描功能和第三方库,实现用户扫描二维码获取发票信息并填写相关开票信息,最后生成电子发票。

不愧是暮言的博客 6093

android开发actionbar,详解Android ActionBar之一:ActionBar概述与创建

前面一个系列中讲的是Fragment的内容,Android 3.0中除Fragment外,Action Bar同样也是一个很重要的知识点。我们经常使用Action Bar来替代传统的标题栏。如果是Android平板设备的话,屏幕越大,通过Action Bar设计的标题可以展示的内容越丰富,操作更加便捷。Action Bar主要功能包含:1. 显示选项菜单。2. 提供标签页的切换方式的导航功能,可以...

weixin_30845365的博客 498

C#:WPF格式转Winform格式

WPF格式转Winform格式

Hello C++ 1295

故障处理:Oracle一体机磁盘故障时磁盘组重平衡失败的故障处理

摘要: Oracle Exadata一体机出现磁盘故障案例,存储节点故障灯未亮但磁盘已产生大量坏块,导致重平衡失败。文章强调了一体机硬件巡检需检查6个关键维度:节点messages日志、存储cell告警日志、CellCLI日志、ASM告警日志、ILOM日志及服务器信号灯。故障分析显示硬盘物理故障导致磁盘组冗余受损,自动修复流程受阻。文中详细记录了通过CellCLI工具排查损坏磁盘的过程,并提供了完整的故障处理流程和命令输出。文章同步发布于微信公众号"IT民工的龙马人生"和博客网站(www

提供数据库技术支持(系统优化,故障处理,安装升级,数据恢复等) 1231

ActionBar导航功能使用介绍

显示和隐藏ActionBar 在清单文件的Application中设置整个项目的主题 在清单文件的Activity中设置单个窗体的主题 android:theme="@android:style/Theme.Holo.NoActionBar"隐藏ActionBar 代码控制ActionBar的显示和隐藏 ActionBar bar = getActionBar()//

张小贝的博客 994

UNITY2022 Cardboard环境搭建

必须安装 Git,并且 git 可执行文件必须位于 PATH 环境变量中。考文档:https://developers.google.cn/cardboard/develop/unity/quickstart?转到 Assets/Samples/Google Cardboard//Hello Cardboard/Scenes/HelloCardboard 场景中。粘贴https://github.com/googlevr/cardboard-xr-plugin.git到文本输入字段中。

weixin_39510925的博客 1211

安卓学习日记——ActionBar

ActionBar介绍 Action bar 的主要目的是: 1、提供一个用于识别应用程序的标示和用户的位置的专用空间。 2、在不同的应用程序之间提供一致的导航和视觉体验。 3、突出Activity的关键操作(如“搜索”、“创建”、“共享”等),并且在可预见的方法内给用户提供快捷的访问。 获得ActionBar的方法很简单:ActionBar actionBar=this.getActionB...

chen_xiaoke的博客 244

使用 MATLAB/Simulink + Reinforcement Learning Toolbox 构建一个基于强化学习的永磁同步电机(PMSM)自适应控制仿真系统

随着人工智能(AI)技术的快速发展,强化学习(Reinforcement Learning, RL)在控制系统中的应用日益广泛。在电机控制领域,传统的PID控制虽然成熟稳定,但在面对非线性、时变、不确定负载等复杂工况时,其控制性能可能受限。而强化学习通过与环境的交互学习,能够实现在线自适应控制策略优化,显著提升系统的鲁棒性与响应速度。本教程将带你使用构建一个基于强化学习的永磁同步电机(PMSM)自适应控制仿真系统,实现从控制策略设计、环境建模、训练到仿真的全过程。当前转速(ω)

amy_mhd的博客 407

【CVPR2025 DEIM】超详细!手把手训练自己的数据集教学:从源码下载,配置虚拟环境,准备数据集、训练、验证、推理测试 ,实现0到1的完整教学过程。本文在win系统上训练,最强实时目标检测算法!

本文详细介绍了CVPR2025 DEIM目标检测模型的完整使用流程。内容包含:1)源码下载与虚拟环境配置;2)Visdrone2019数据集准备与配置文件修改;3)训练参数设置及Windows系统常见问题解决方案;4)模型验证、推理测试及热力图生成方法。特别针对Windows用户提供了虚拟环境配置指导,并详细说明了数据集路径设置、参数调整等关键步骤。通过本文的指导,小伙伴可以快速掌握DEIM模型的使用方法,用于学术论文研究和实验分析。

Ai缝合怪、助力小伙伴高效发论文 7653

Android Action Bar

1、Action Bar隐藏 如果想整个程序没有Action Bar,把Application的theme设置成NoActionBar即可,如果只是想某个activity没有Action Bar,把Activity的theme设置成NoActionBar即可。 2、Java隐藏显示 actionBar = getSupportActionBar(); //获取ActionBar动作栏 action_show = (Button) findViewById(R.id.act

小小的熊的博客 1561

R语言机器学习算法实战系列(十一)MLP算法分类器 (Multi-Layer Perceptrons)

本文介绍了多层感知器(MLP)分类算法的原理和计算步骤,并通过R语言实现MLP模型。MLP是一种前馈神经网络,包含输入层、隐藏层和输出层,通过激活函数引入非线性,利用前向传播和反向传播进行训练。文章详细描述了数据预处理、数据切割、模型构建、预测和评估等步骤,并提供了乳腺癌数据集(BreastCancer_clean.csv)的下载链接。通过R包如neuralnet和caret,读者可以逐步实现MLP模型,并评估其分类性能。

专注生信领域 935

子墨庖丁Android的ActionBar源代码分析 (一)实例化

假设你从事过Androidclient开发,相信你对ActionBar这套框架并不陌生,或者说你并不了解它,可是你应该时不时的要跟它打交道。抛开ActionBar的实现不说,ActionBar实际上是对Android的TitleBar行为的抽象,这样的框架能够适用于这样的模式的应用,是对须要的行为视图的抽象。当然也许你也和我一样,对ActionBar的实现效率并不惬意...

weixin_30273763的博客 111

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Android ActionBar的源代码分析(二)

前面已经对ActionBar的初始换过程进行比较详细的分析,没有看过的朋友可以移步android中ActionBar的源代码分析(一)。本章接着对ActionBar的运行机制进行分析。

呼啸而过的专栏 2532

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Android ActionBar的源代码分析(四)

上一篇已经对ActionBar的菜单项的执行过程进行了分析,有兴趣的朋友可以看一下android中ActionBar的源代码分析(三),本章对ActionBar的OverflowMenu的运行机制进行分析。 ActionBar的OverflowMenu,就是ActionBar右边的三个小点点,点击以后就出现一个下拉菜单项,如图 OverflowMenu是为了解决ActionBar的菜单项太

呼啸而过的专栏 2502

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Android ActionBar的源代码分析(三)

上一篇已经对ActionBar菜单构造过程进行比较详细的分析,没有看过的朋友可以移步android中ActionBar的源代码分析(二)。本章接着对ActionBar菜单的执行过程进行分析。 在介绍ActionBar菜单的执行过程之前,首先我们需要了解android的消息处理机制,我们知道activity的activity等组件和view控件都是运行在主线程上的,这个主线程我们也称为UI线程,U

呼啸而过的专栏 2552

安卓9.0ActionBar点击无响应事件

安卓9.0ActionBar点击无响应事件

loveyouloveyourcat的博客 1957

Android开发之ActionBar结合Fragment的导航功能的使用(源代码分享)

标签在操作栏方便用户去探索,在你的应用程序在不同视图之间切换。ActionBar提供的标签是最理想的选择,因为他们适应不同的屏幕尺寸。一旦你确定了Fragment出现在布局里,添加标签的步骤如下:一:继承ActionBar.TabListener接口,控制哪个标签被选中,进而切换布局;二:对于每一个您想要添加标签,实例化一个ActionBar,选项卡调用setTabListener TabList

林泓成的专栏 3172

Android技术——视图切换(一)利用ActionBar实现Tab导航

一、滑动菜单有两种实现方法:借助ActionBar;借助FragmentTabHost 二、借助ActionBar实现滑动菜单

YongYu_IT的专栏 1393

android ActionBar-PullToRefresh 下拉刷新的使用方法

Shilslas 这个最终效果,  图片来自http://a.code4app.com/android/ActionBar-PullToRefresh/524f9a156803faab3c000000 由于对于导包不是很了解,这个下拉刷新花费了我很多时间。 其实很简单,也就是两个类,首先你要下载ActionBar-PullToRefresh    点击打开链接 然后还要下载

what is name 5180

Attempt to invoke virtual method 'void android.app.ActionBar.setTitle的解决方法

在安卓4.4.2的关于蓝牙开发的一个demo,

jasonkent27的专栏 1万+

直播app源代码,android中actionbar的显示操作

直播app源代码,android中actionbar的显示操作相关的代码 <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <!--这个位置的话是我们的的菜单资源文件--> <item an

云豹网络科技 181

关于fragment、ActionBar与ViewPager的一些总结

今天花了大半天去研究ActionBar获取指针为空,fragment嵌套失败等问题,在此做一些总结,以免之后再犯错: 今天主要工作为在atyA中填充fragmentA,而在fragmentA中初始化ActionBar与ViewPager,而ViewPager实现FragmentPagerAdapter来填充内容 分别使用了: import android.support.v4.app

shw372029857的博客 1187

Android ActionBar详解(二)

最近一直在研究Android ActionBar和Fragment,参看Google的官方文档和网上一些demo源代码,记录一下主要的知识点,也跟大家分享一下! 具体内容请参看链接:http://developer.android.com/guide/topics/ui/actionbar.html 一、添加Action Items 在Activity或者Fragment 的 onC

Ricky 4714

[android ui]ActionBar的Action图标区添加

ActionBar的隐藏和现实 ActionBar bar = getActionBar(); bar.hide();  //隐藏Action Bar bar.show(); //显示Action Bar ActionBar的action图标区 ActionBar的右侧就是action图标区域,如下图所示 这是ActionBar的menu区域,作为options

超级方的专栏 1475
上一篇: Android 2.x版本使用ActionBar-强制显示OverflowButton
下一篇: Android ActionBar的源代码分析(二)
呼啸而过11
博客等级 码龄19年 120粉丝 13原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值