Android ActionBar的源代码分析(二)

郭霖所写的关于ActionBar解析和实战博客中的源码 立即下载

前面已经对ActionBar的初始化过程以及ActionBarView的布局进行比较详细的分析,没有看过的朋友可以移步android中ActionBar的源代码分析(一)。本章接着对ActionBar菜单的构造过程进行分析。

关于actionbar的菜单机制,网上已经有很多大牛写过类似的文章,个人认为写的不错的,有兴趣的朋友可以看一下这个:ANDROID的ActionBar及菜单机制

不过这篇文章从ActionBar所涉及的UML类图出发,概括了ActionBar的运行机制,可能有朋友看得云里雾里,下面我就详细地对相关点进行分析。

ActionBar的菜单构造过程还是很复杂的,中间涉及的类非常多,为了把这个过程说明白,我先把涉及到的类图画出来吧:


是不是被这张图吓了一跳,个人感觉这样的设计过于复杂,类与类之间的耦合度很高。概括来说,就是MenuInflater负责解析xml获取菜单信息,MenuBuilder负责维护菜单信息列表,ActionMenuPresenter负责根据菜单信息构建菜单控件ActionMenuItemView(其实就是一个TextView),并给ActionBarView返回一个菜单容器ActionMenuView(其实就是一个LinearLayout),ActionBarView负责把ActionMenuView显示出来,详细代码跟踪如下:

首先是从Activity.onCreate()开始吧,在Activity.onCreate中我们会经常看到调用这么一个方法:setContentView(),那么setContentView()里面到底干了啥东东呢?我们打开代码瞅瞅

    public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID);
        initActionBar();
    }
很简单,就调用了window.setContentView()方法,并且进行初始化ActionBar操作,initActionBar()的调用过程在 android中ActionBar的源代码分析(一)已经做过介绍,这里不再赘述。我们看看window.setContentView()做了什么东西吧,我们知道Window是一个抽象类,具体的实现类是PhoneWindow,PhoneWindow.setContentView()有三个同名方法,基本代码都差不多,我们拿其中一个进行分析:

    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }
首先判断ContentView是否为空,如果为空则进行初始化最顶层View的操作,否则移除ContentView下所有的子控件;然后对布局执行inflater操作,最后通知Activity说ContentView的子控件发生了改变;

这里的installDecor()方法在上一章节已经做过简单介绍,我们再来回顾一下:

    private void installDecor() {
        if (mDecor == null) {
	    // 进行decorView的实例化操作
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        if (mContentParent == null) {
	    // 获取DecorView的布局
            mContentParent = generateLayout(mDecor);
			......
            
 
                    mDecor.post(new Runnable() {
                        public void run() {
                            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
                            if (!isDestroyed() && (st == null || st.menu == null)) {
				// 构建菜单面板
                                invalidatePanelMenu(FEATURE_ACTION_BAR);
                            }
                        }
                    });
                }
            }
        }
    }

首先是进行DecorView的实例化操作,从上一章中我们知道,DecorView是Activity的顶层视图。接着获取DecorView的布局文件进行解析,并添加DecorView的子控件;最后是执行构建菜单面板操作,我们看一下invalidatePanelMenu()方法的实现逻辑:

    @Override
    public void invalidatePanelMenu(int featureId) {
        mInvalidatePanelMenuFeatures |= 1 << featureId;

        if (!mInvalidatePanelMenuPosted && mDecor != null) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            mInvalidatePanelMenuPosted = true;
        }
    }

首先判断传入参数featureId是否为空,不为空则调用mInvalidatePanelMenuRunnable的run()方法如下:

    private final Runnable mInvalidatePanelMenuRunnable = new Runnable() {
        @Override public void run() {
            for (int i = 0; i <= FEATURE_MAX; i++) {
                if ((mInvalidatePanelMenuFeatures & 1 << i) != 0) {
                    doInvalidatePanelMenu(i);
                }
            }
            mInvalidatePanelMenuPosted = false;
            mInvalidatePanelMenuFeatures = 0;
        }
    };

这个方法就是调用doInvalidatePanelMenu()方法来构建菜单面板的,接着跟踪:

    void doInvalidatePanelMenu(int featureId) {
        PanelFeatureState st = getPanelState(featureId, true);
        Bundle savedActionViewStates = null;
        if (st.menu != null) {
            savedActionViewStates = new Bundle();
            st.menu.saveActionViewStates(savedActionViewStates);
            if (savedActionViewStates.size() > 0) {
                st.frozenActionViewState = savedActionViewStates;
            }
            // This will be started again when the panel is prepared.
            st.menu.stopDispatchingItemsChanged();
            st.menu.clear();
        }
        st.refreshMenuContent = true;
        st.refreshDecorView = true;
        
        // Prepare the options panel if we have an action bar
        if ((featureId == FEATURE_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL)
                && mActionBar != null) {
            st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
            if (st != null) {
                st.isPrepared = false;
                preparePanel(st, null);
            }
        }
    }
在doInvalidatePanelMenu()这个方法中,首先是根据featureId查找面板的状态,然后判断st.menu是否为空,初始状态下st.menu就是为空的,并且传入的参数featureId是等于FEATURE_ACTION_BAR的,因此就会执行preparePane()方法:

    public final boolean preparePanel(PanelFeatureState st, KeyEvent event) {
		......
		// 这里的callback就是Activity
        final Callback cb = getCallback();

        if (cb != null) {
            st.createdPanelView = cb.onCreatePanelView(st.featureId);
        }

        if (st.createdPanelView == null) {
            if (st.menu == null || st.refreshMenuContent) {
                if (st.menu == null) {
                    if (!initializePanelMenu(st) || (st.menu == null)) {
                        return false;
                    }
                }

                if (mActionBar != null) {
                    if (mActionMenuPresenterCallback == null) {
                        mActionMenuPresenterCallback = new ActionMenuPresenterCallback();
                    }
                    mActionBar.setMenu(st.menu, mActionMenuPresenterCallback);
                }

                st.menu.stopDispatchingItemsChanged();
                if ((cb == null) || !cb.onCreatePanelMenu(st.featureId, st.menu)) {
                    st.setMenu(null);

                    if (mActionBar != null) {
                        mActionBar.setMenu(null, mActionMenuPresenterCallback);
                    }

                    return false;
                }
                
                st.refreshMenuContent = false;
            }

			......

            if (!cb.onPreparePanel(st.featureId, st.createdPanelView, st.menu)) {
                if (mActionBar != null) {
                    mActionBar.setMenu(null, mActionMenuPresenterCallback);
                }
                st.menu.startDispatchingItemsChanged();
                return false;
            }

        ......

        return true;
    }

preparePanel()方法的代码比较多,我们就挑重点的看,首先是通过getCallback()获取PhoneWindow的回调对象,这个回调对象就是Activity,怎么知道呢?有兴趣的朋友可以看看类Activity的attach()方法的代码,在这个方法中,首先是实例化Window类,然后接着就通过window.setCallback(this)方法,把自身传入到对象Window中了(注:类Activity是实现接口Window.Callback的)。

言归正传,在preparePanel()方法中获取回调对象Activity后,接着就是回调了Activity.onCreatePanelView()方法创建面板视图,Activity.onCreatePanelView()方法默认情况下是返回null的,因此接着就会调用initializePanelMenu()方法初始化菜单面板,下面是initializePanelMenu()方法的代码:

    protected boolean initializePanelMenu(final PanelFeatureState st) {
<span style="white-space:pre">	</span>......

        final MenuBuilder menu = new MenuBuilder(context);

        menu.setCallback(this);
        st.setMenu(menu);

        return true;
    }
在这个方法中,是实例化MenuBuilder类,并把menu赋值给传入参数st.menu了,并且返回值为true;回到方法preparePanel()中,继续往下跟踪代码,由于mActionBar已经通过installDecor()方法进行了赋值,因此mActionBar是不为null的,系统就会执行mActionBar.setMenu(st.menu, mActionMenuPresenterCallback)方法,忘了说了,这个mActionBar是ActionBarView类的对象,且st.menu是MenuBuilder的实例对象;因此我们看一下ActionBarView.setMenu()方法:

<pre name="code" class="java">    public void setMenu(Menu menu, MenuPresenter.Callback cb) {
		......

        MenuBuilder builder = (MenuBuilder) menu;
        mOptionsMenu = builder;
        if (mMenuView != null) {
            final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
            if (oldParent != null) {
                oldParent.removeView(mMenuView);
            }
        }
        if (mActionMenuPresenter == null) {
            mActionMenuPresenter = new ActionMenuPresenter(mContext);
            mActionMenuPresenter.setCallback(cb);
            mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter);
            mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
        }

        ActionMenuView menuView;
        final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.MATCH_PARENT);
        if (!mSplitActionBar) {
            mActionMenuPresenter.setExpandedActionViewsExclusive(
                    getResources().getBoolean(
                    com.android.internal.R.bool.action_bar_expanded_action_views_exclusive));
            configPresenters(builder);
            menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
            final ViewGroup oldParent = (ViewGroup) menuView.getParent();
            if (oldParent != null && oldParent != this) {
                oldParent.removeView(menuView);
            }
            addView(menuView, layoutParams);
        } else {
            .......
        }
        mMenuView = menuView;
    }
	
    private void configPresenters(MenuBuilder builder) {
        if (builder != null) {
            builder.addMenuPresenter(mActionMenuPresenter);
            builder.addMenuPresenter(mExpandedMenuPresenter);
        } else {
            mActionMenuPresenter.initForMenu(mContext, null);
            mExpandedMenuPresenter.initForMenu(mContext, null);
            mActionMenuPresenter.updateMenuView(true);
            mExpandedMenuPresenter.updateMenuView(true);
        }
    }


 在setMenu()方法中,实例化了ActionMenuPresenter类,这是一个负责展现menu的类,接着调用configPresenters()方法配置menu的展现类,然后调用ActionMenuPresenter.getMenuView()获取到菜单容器ActionMenuView,并把它添加到ActionBarView中展现出来。这里有很多细节都没有介绍,比如ActionMenuPresenter是怎么展现菜单项的,又是如何管理菜单容器ActionMenuView的生命周期以及怎么往菜单容器添加菜单View的呢?那么我们下面逐一进行介绍。 

首先ActionMenuPresenter是怎么展现菜单项的呢?其实我们在进行配置menu的xml时,有一个属性叫showAsAction,当我们配置它的值为ifRoom时,表示当ActionBar宽度允许的情况下,就会显示到ActionBar中,否则就会放到OverflowMenu中(如果策略允许的话),这就需要计算ActionBar允许放置menu的最大宽度了,这个计算的过程是在ActionMenuPresenter.initForMenu()方法中执行的,这个方法是在哪个时机开始调用的呢?回到configPresenters()方法,这个方法首先判断builder是否为空,由于builder在上面已经做过实例化,因此是不为空的,这样就会调用MenuBuilder.addMenuPresenter()方法

    public void addMenuPresenter(MenuPresenter presenter) {
        mPresenters.add(new WeakReference<MenuPresenter>(presenter));
        presenter.initForMenu(mContext, this);
        mIsActionItemsStale = true;
    }

嗯,终于看到initForMenu方法了,这个方法首先根据标题栏策略类ActionBarPolicy的配置来决定是否显示OverflowButton,然后计算显示菜单的最大宽度,详细代码就不贴出来,有兴趣的朋友可以看看

然后就是构造ActionMenuView的过程了,这个可以看看ActionMenuPresenter.getMenuView()的实现过程了:

    @Override
    public MenuView getMenuView(ViewGroup root) {
        MenuView result = super.getMenuView(root);
        ((ActionMenuView) result).setPresenter(this);
        return result;
    }

代码很简单,可以看出主要的实现逻辑应该在super.getMenuView()上,ActionMenuPresenter的父类是BaseMenuPresenter,我们就看一下BaseMenuPresenter.getMenuView()的实现过程吧

    public MenuView getMenuView(ViewGroup root) {
        if (mMenuView == null) {
            mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false);
            mMenuView.initialize(mMenu);
            updateMenuView(true);
        }

        return mMenuView;
    }
这个方法通过inflate的方法创建了ActionMenuView,然后调用initialize()方法初始化这个View,最后调用updateMenuView()方法往ActionMenuView添加菜单控件,由于此时菜单项还没从xml上解析并实例化,也就是说MenuBuilder里的菜单信息还是空的,因此这个方法暂时没有任何效果;

那么MenuBuilder是什么时候才有菜单信息的呢?最后又是如果根据菜单信息创建菜单View并放到ActionMenuView上的呢?回到PhoneWindow.preparePanel()方法中,我们在调用ActionBarView.setMenu()后,系统接着就调用了cb.onCreatePanelMenu(),也就是Activity.onCreatePanelMenu()方法

    public boolean onCreatePanelMenu(int featureId, Menu menu) {
        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
            boolean show = onCreateOptionsMenu(menu);
            show |= mFragments.dispatchCreateOptionsMenu(menu, getMenuInflater());
            return show;
        }
        return false;
    }

好了,有一个方法我想大家应该是非常熟悉的了:onCreateOptionMenu(),没错,这个就是我们为了显示菜单,必须要在Activity中override的方法,一般的写法如下:

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

这就是采用inflate的方式创建菜单的,在MenuInflater.inflate()方法中,主要是调用方法parseMenu()去解析xml并创建菜单的,代码如下:

    private void parseMenu(XmlPullParser parser, AttributeSet attrs, Menu menu)
            throws XmlPullParserException, IOException {
        MenuState menuState = new MenuState(menu);
		......
        boolean reachedEndOfMenu = false;
        while (!reachedEndOfMenu) {
            switch (eventType) {
                case XmlPullParser.START_TAG:
					......
                    
                case XmlPullParser.END_TAG:
                    tagName = parser.getName();
                    if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) {
                        lookingForEndOfUnknownTag = false;
                        unknownTagName = null;
                    } else if (tagName.equals(XML_GROUP)) {
                        menuState.resetGroup();
                    } else if (tagName.equals(XML_ITEM)) {
                        if (!menuState.hasAddedItem()) {
                            if (menuState.itemActionProvider != null &&
                                    menuState.itemActionProvider.hasSubMenu()) {
                                menuState.addSubMenuItem();
                            } else {
                                menuState.addItem();
                            }
                        }
                    } else if (tagName.equals(XML_MENU)) {
                        reachedEndOfMenu = true;
                    }
                    break;
                    
                case XmlPullParser.END_DOCUMENT:
                    throw new RuntimeException("Unexpected end of document");
            }
            
            eventType = parser.next();
        }
    }

在parseMenu方法中,采用Pull的方式解析xml,并调用menuState.readItem()和menuState.addItem()方法读取xml中菜单的配置属性,并把菜单添加到MenuBuilder中,这里就调用到了MenuBuilder.add方法,而MenuBuilder.add方法又是调用addInternal()方法来执行具体操作的,我们看一下addInternal()方法的实现逻辑吧:

    private MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
        final int ordering = getOrdering(categoryOrder);

        final MenuItemImpl item = createNewMenuItem(group, id, categoryOrder, ordering, title,
                mDefaultShowAsAction);

        if (mCurrentMenuInfo != null) {
            // Pass along the current menu info
            item.setMenuInfo(mCurrentMenuInfo);
        }

        mItems.add(findInsertIndex(mItems, ordering), item);
        onItemsChanged(true);

        return item;
    }

在这个方法中,首先是创建了菜单项信息类MenuItemImpl,然后把新创建的菜单信息类添加到列表中,最后调用onItemChanged()方法通知菜单项发生改变的消息,onItemsChanged()方法又是调用dispatchPresenterUpdate()方法执行具体操作的:

    private void dispatchPresenterUpdate(boolean cleared) {
        if (mPresenters.isEmpty()) return;

        stopDispatchingItemsChanged();
        for (WeakReference<MenuPresenter> ref : mPresenters) {
            final MenuPresenter presenter = ref.get();
            if (presenter == null) {
                mPresenters.remove(ref);
            } else {
                presenter.updateMenuView(cleared);
            }
        }
        startDispatchingItemsChanged();
    }
在dispatchPresenterUpdate()方法中,这里的presenter其实就是ActionMenuPresenter,只不过这里使用了WeakReference进行了引用,也就是说调用了ActionMenuPresenter.updateMenuView()方法执行了更新菜单控件的操作:

    public void updateMenuView(boolean cleared) {
        final ViewGroup menuViewParent = (ViewGroup) ((View) mMenuView).getParent();
        if (menuViewParent != null) {
            ActionBarTransition.beginDelayedTransition(menuViewParent);
        }
        super.updateMenuView(cleared);

        ((View) mMenuView).requestLayout();

        if (mMenu != null) {
            final ArrayList<MenuItemImpl> actionItems = mMenu.getActionItems();
            final int count = actionItems.size();
            for (int i = 0; i < count; i++) {
                final ActionProvider provider = actionItems.get(i).getActionProvider();
                if (provider != null) {
                    provider.setSubUiVisibilityListener(this);
                }
            }
        }

        final ArrayList<MenuItemImpl> nonActionItems = mMenu != null ?
                mMenu.getNonActionItems() : null;

        boolean hasOverflow = false;
        if (mReserveOverflow && nonActionItems != null) {
            final int count = nonActionItems.size();
            if (count == 1) {
                hasOverflow = !nonActionItems.get(0).isActionViewExpanded();
            } else {
                hasOverflow = count > 0;
            }
        }

        if (hasOverflow) {
            if (mOverflowButton == null) {
                mOverflowButton = new OverflowMenuButton(mSystemContext);
            }
            ViewGroup parent = (ViewGroup) mOverflowButton.getParent();
            if (parent != mMenuView) {
                if (parent != null) {
                    parent.removeView(mOverflowButton);
                }
                ActionMenuView menuView = (ActionMenuView) mMenuView;
                menuView.addView(mOverflowButton, menuView.generateOverflowButtonLayoutParams());
            }
        } else if (mOverflowButton != null && mOverflowButton.getParent() == mMenuView) {
            ((ViewGroup) mMenuView).removeView(mOverflowButton);
        }

        ((ActionMenuView) mMenuView).setOverflowReserved(mReserveOverflow);
    }
这个方法首先是调用父类的updateMenuView()方法执行更新菜单的操作,然后是判断是否需要显示overflowbutton,如果需要显示则把overflowbutton添加到ActionMenuView中,主要的菜单控件的创建过程以及菜单控件与菜单信息的绑定过程应该是在父类的updateMenuView()中进行的:

    public void updateMenuView(boolean cleared) {
        final ViewGroup parent = (ViewGroup) mMenuView;
        if (parent == null) return;

        int childIndex = 0;
        if (mMenu != null) {
            mMenu.flagActionItems();
            ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
            final int itemCount = visibleItems.size();
            for (int i = 0; i < itemCount; i++) {
                MenuItemImpl item = visibleItems.get(i);
                if (shouldIncludeItem(childIndex, item)) {
                    final View convertView = parent.getChildAt(childIndex);
                    final MenuItemImpl oldItem = convertView instanceof MenuView.ItemView ?
                            ((MenuView.ItemView) convertView).getItemData() : null;
                    final View itemView = getItemView(item, convertView, parent);
                    if (item != oldItem) {
                        // Don't let old states linger with new data.
                        itemView.setPressed(false);
                        ViewCompat.jumpDrawablesToCurrentState(itemView);
                    }
                    if (itemView != convertView) {
                        addItemView(itemView, childIndex);
                    }
                    childIndex++;
                }
            }
        }

        // Remove leftover views.
        while (childIndex < parent.getChildCount()) {
            if (!filterLeftoverView(parent, childIndex)) {
                childIndex++;
            }
        }
    }
BaseMenuPresenter.updateMenuView()方法首先是执行了MenuBuilder.flagActionItems()方法,该方法根据配置的可见菜单项是否需要显示到ActionBar中对菜单信息进行分类:一类是需要到ActionBar中的,一类是需要显示到OverflowMenu中的;如果是需要显示到ActionBar中(也就是MenuItem.isActionButton()==true),则通过调用addItemView()方法把它添加到ActionMenuView中。

至此,整个ActionBar的菜单项就算是构造完成并添加进来了,是不是感觉特别的绕,恩,总结一下就是下图的调用过程:


  

关于ActionBar的菜单构造过程分析到此为止,下一篇就ActionBar菜单的执行过程进行分析,敬请期待!


基于STM32的DS1302电子时钟电路与程序设计 这个示例代码中,我们使用DS1302_GetTime函数从DS1302芯片获取当前时间,并使用DS1302_GetDate函数获取当前日期。在本文中,我们将介绍如何使用STM32微控制器和DS1302实时时钟芯片来构建一个基于STM32的DS1302电子时钟,并提供相应的程序设计。请注意,以上示例代码仅供参考,具体的实现细节可能会根据使用的开发环境和硬件组件有所差异。在实际应用中,你可能需要根据自己的需求进行适当的调整和修改。为了完整实现电子时钟的功能,你还可以添加其他功能,例如闹钟设置、温度显示等。 阅读详情

相关推荐

FIFO Generate IP核使用——FIFO写操作详解及Status Flags页配置

本文介绍了FIFO的写操作及Status Flags页的配置信息。

u011565038的博客 2285

Android ActionBar的源代码分析(一)

ActionBar是android的一个非常重要开发组件,在很多商业应用中到处可见,也是很多android开发人员必须熟练掌握的开发技术,下面就从源码角度来分析ActionBar的实现过程。 从哪里开始呢? 我们回忆一下在Activity中获取ActionBar的方法为getActionBar(),那就从getActionBar()开始研究吧! 打开getActionBar()的代码实现如下

呼啸而过的专栏 5041

scRNA-seq的fastq文件处理流程——持续更新

获得了一批新的scRNA-seq的fastq.gz文件,每个样本的数据形式为: 我打算使用10X cellranger处理fastq.gz文件,下载及安装方式在官网:https://support.10xgenomics.com/single-cell-gene-expression/software/downloads/2.0这个网址写了10X cellranger处理fastq.gz文件的命名规则https://support.10xgenomics.com/single-cell-gene-expr

watermel__的博客 3198

Android ActionBar的基本用法

本文翻译了这篇文章:Using the Android action bar (ActionBar) - Tutorial 1、ActionBar的简介 ActionBar位于Activity的顶部,可用来显示activity的标题、Icon、Actions和一些用于交互的View。它也可被用于应用的导航。 ActionBar 是在Android 3.0(API 11)中加入到SK中的,

bigconvience的专栏 11万+

kali破解WIFI密码

今天我们来学习一下通过kali和一个好的字典来破解WIFI密码。 声明:此教程仅供学习研究使用,严禁使用此技术进行非法行为 首先我们需要一个具有监听功能的无线网卡(这里我给大家推荐:rt3070) 本章实验通过虚拟机VMware进行: 先通过services.msc来打开VMware USB Arbitration Service 插上监听网卡,连接USB到虚...

#的博客 5810

3D视觉引导技术在机器人焊接中的应用

通过对比传统的2D视觉和3D视觉技术在焊接领域的应用差异,探讨了3D视觉引导技术在提高焊接精度、适应不规则工件形状和增强自适应能力方面的优势。本文对3D视觉引导技术在机器人焊接中的应用进行了深入探讨,总结了其在提高焊接精度、适应不规则工件形状和增强自适应能力方面的优势,并通过应用案例展示了其在焊接路径规划、焊缝检测和焊接质量控制等方面的重要作用。1. 提高焊接精度:3D视觉引导技术能够精确测量焊接工件的三维形状和位置,为机器人提供精准的焊接路径规划和跟踪,从而提高焊接精度。三、3D视觉引导技术概述。

2302_79273122的博客 1836

源代码解说ActionBar的各种使用方法

1. Navigation Drawer 很多应用程序都使用了Navigation Drawer,如网易邮箱client。该控件位于 android.support.v4.widget.DrawerLayout ,使用方法例如以下,点击下载源代码: &lt;android.support.v4.widget.DrawerLayout xmlns:android="http:...

weixin_33910460的博客 125

YOLOv11改进缝合FasterBlock | 一种新型轻量化卷积结构C3k2的设计与实证:兼顾精度与速度的改进方法(附代码)

YOLO系列一直是目标检测领域的明星算法,凭借其实时性和准确性在众多应用中占据一席之地。随着YOLO版本的不断迭代,网络架构逐渐变得更为复杂,尤其是在卷积模块的设计上。本文将介绍如何利用FasterNet的FasterBlock创新,并通过二次创新C3k2提出一种全新的卷积结构。该结构旨在提高YOLOv11的计算效率和准确性,特别是在视觉任务中的表现。我们的创新在保证高精度的前提下,有效降低了计算量和内存需求,特别适合在资源受限的设备上运行。

一键难忘的博客 907

android 代码混搭,Android代码之路:ActionBar的使用

ActionBar简介ActionBar是安卓3.0以后新增的导航栏功能,用以代替传统屏幕顶端的标题栏。它标识了用户当前操作界面的位置,并提供了额外的用户动作、界面导航等功能。通过ActionBar可以方便的让系统自动的适配不同尺寸的屏幕。actionbar.png1.App Icon:可显示APP的icon,也可用其他图标代替。2.视图切换3.Action Buttons:放置一些重要的软件功能...

weixin_39841002的博客 174

GEE土地分类:基于sentinel-2的ResNet深度学习模型的土地分类

本文提出了一种基于深度学习的土地覆盖分类方法,通过集成Google Earth Engine和PyTorch框架实现自动化监测系统。技术方案包括:1) 使用Sentinel-2和Landsat-8卫星影像作为数据源,结合EuroSAT标准数据集;2) 采用ResNet50和U-Net构建深度学习模型;3) 建立端到端处理流程,包含数据预处理、模型训练和评估体系。实验结果表明,该方法在EuroSAT数据集上达到90%的分类准确率,成功应用于农作物监测、城市扩张分析等场景。系统配置基于Python 3.8+环境

此星光明博客 372

笔试题-2023-星宸-数字IC设计【纯净题目版】

4 笔试题-2023-星宸-数字IC设计【纯净题目版】

w0shishabi的博客 1172

android toolbar源码解析,[Android]Toolbar使用详解(三)——源码解析

更多关于Toolbar的使用请移步Toolbar使用详解系列从Toolbar的使用一步步解析Toolbar源码大体架构API 0.设置导航图标mToolbar.setNavigationIcon(R.drawable.ic_actionbar_flow);源码如下public void setNavigationIcon(int resId) {this.setNavigationIcon(thi...

weixin_30443729的博客 238

基于多传感器融合的智身四足机器狗楼梯环境SLAM系统设计与实现

本文设计了一种基于多传感器融合的四足机器狗楼梯环境SLAM系统。系统采用Jetson Orin NX为主控制器,集成RealSense D435i深度相机和Livox Mid360激光雷达,在ROS2框架下实现平面定位精度<5cm、楼梯环境下导航不漂移的目标。通过多层次传感器融合策略、运动学约束优化和状态切换机制,解决了楼梯环境下的定位累积误差问题,实现了平面与楼梯环境的自动识别切换及精度复位功能。硬件设计注重传感器布局优化和精确标定,软件架构采用分层设计,确保实时性能。该系统为四足机器人在复杂地形中

huanghm88的专栏 244

Android ActionBar的源代码分析(四)

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

呼啸而过的专栏 2502

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Android ActionBar的源代码分析(三)

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

呼啸而过的专栏 2552

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

安卓9.0ActionBar点击无响应事件

安卓9.0ActionBar点击无响应事件

loveyouloveyourcat的博客 1957

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

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

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

林泓成的专栏 3172

Android ActionBar详解(二)

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

Ricky 4714

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与Fragment结合使用Demo

本文介绍ActionBar与Fragment结合使用的一个实例,ActionBar是一个标识应用程序和用户位置的窗口功能,并且给用户提供操作和导航模式。  Action bar的主要用途是:         1.  提供一个用于识别应用程序的标示和用户的位置的专用空间。          这个空间的左边是应用的图标或logo,以及Activity的标题。但是,如果

红日 2254

[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 actionbar 使用简介

1、ActionBar的简介 ActionBar位于Activity的顶部,可用来显示activity的标题、Icon、Actions和一些用于交互的View。它也可被用于应用的导航。 ActionBar 是在Android 3.0(API 11)中加入到SK中的,想在低版本中使用ActionBar有两种选择:使用http://actionbarsherlock.com 或使用Support

licong0716的专栏 459

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

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

weixin_30273763的博客 111
上一篇: Android ActionBar的源代码分析(一)
下一篇: Android ActionBar的源代码分析(三)
呼啸而过11
博客等级 码龄19年 120粉丝 13原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值