精华内容
下载资源
问答
  • Android Toolbar菜单动态切换item的图标

    千次阅读 2016-07-28 08:46:39
    大家都知道,Fragment启动速度比Activity快很多,因此在...可是由于每个Fragment都对应一个功能界面,因此每个Fragment顶部工具栏都应该是不同,但是ActionBar或者ToolBar都是属于Activity,这时候我们就需要

    警示:本文所述的方法过于繁琐,逻辑混乱,代码难以适应复杂情况,并且几乎不可扩展,本文之所以没删是我为了记录自己的踩坑记录,因此如果有人想参照这方面的知识,请参考我的另一篇文章:

    Toolbar菜单动态改变item的图标(二)



    大家都知道,Fragment的启动速度比Activity快很多,因此在开发中如果每一个界面都使用一个Activity显然不那么好,这时候我们一般用Activity来充当管理的角色,界面的内容都放在Fragment中。可是由于每个Fragment都对应一个功能界面,因此每个Fragment的顶部工具栏都应该是不同的,但是ActionBar或者ToolBar都是属于Activity的,这时候我们就需要在切换Fragment的时候使Toolbar也做出相应的动态切换。

    我们来举一个简单的例子,打开个人信息Activity(MyInformationActivity),布局文件如下。

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".myinformation.MyInformationActivity">
    
        <android.support.v7.widget.Toolbar
            android:id="@+id/my_information_toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:theme="@style/WhiteToolBar">
        </android.support.v7.widget.Toolbar>
    
        <FrameLayout
            android:id="@+id/alter_my_information_frame"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <fragment
                android:id="@+id/my_information"
                android:name="com.icon.app.myinformation.MyInformationFragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
    
        </FrameLayout>
    
    </LinearLayout>
    布局很简单,里面只放置了一个Toolbar和第一个Fragment (MyInformationFragment),因此,只要这个Activity一启动,首先展示在用户眼前并和用户交互的就是MyInformationFragment,MyInforamtionFragment的布局文件我就不放了,直接放效果图,如下:

    展示的用户信息除了头像以外,主要就是姓名,性别,友好度这三项。大家可以注意到 ,toolbar的标题是“我的信息”,右上角的MenuItem是一支笔的图片。我们要实现的是点击右上角的menuitem,创建第二个Fragment (AlterMyInformationFragment) 使其到达Activity的最顶端,覆盖当前的MyInforamtionFragment。于是,我们在onOptionsItemSelected(MenuItem item)方法中进行监听,当用户点击右上角的笔图标时,启动新的Fragment,MyInformationActivity的代码如下:

    public class MyInformationActivity extends AppCompatActivity {
    
        private Toolbar toolbar;
        private AlterMyInformationFragment fragment;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_my_information);
            toolbar = (Toolbar) findViewById(R.id.my_information_toolbar);
            toolbar.setTitle("我的信息");
            setSupportActionBar(toolbar);
            getSupportActionBar().setHomeButtonEnabled(true);
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        }
    
        private void setAlterMyInformationFragment() {
            fragment = new AlterMyInformationFragment();
            FragmentManager fragmentManager = getFragmentManager();
            FragmentTransaction transaction = fragmentManager.beginTransaction();
            transaction.replace(R.id.alter_my_information_frame, fragment);
            transaction.addToBackStack(null);
            transaction.commit();
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.menu_my_information, menu);
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
                case android.R.id.home:
                    break;
                case R.id.alter_information:
                    setAlterMyInformationFragment();
                    break;
                default:
                    break;
            }
            return super.onOptionsItemSelected(item);
        }
    
    
    }

    注:android.R.id.home是左上角后退按钮的id。


    MyInformationActivity的Menu布局文件如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        tools:context=".myinformation.MyInformationActivity">
    
        <item
            android:id="@+id/alter_information"
            android:orderInCategory="100"
            app:showAsAction="ifRoom"
            android:icon="@drawable/ic_create_white_24dp"/>
    
    </menu>
    可以看到,MyInformationActivity的Toolbar中只有一个item。




    现在,我们在动态变更菜单前还要做一个事情,一般toolbar最左边的后退按钮相当于back键的功能,即点击以后销毁当前的Activity,但是,我们现在的问题是,如果当前Activity展示的是MyInformationFragment,那自然没有问题,单击后退键,即销毁当前活动,但是如果我们当前处在和AlterMyInforamtionFragment的交互状态,单击后退键把当前Activity销毁了,显然是不合理的,正确的情况是应该销毁AlterMyInforamtionFragment,使用户回到和MyInformationFragment交互的状态。因此,我们需要在Activity中设置一个变量status来进行标记,用来反映当前和用户交互的Fragment到底是哪一个。因此,新增代码后Activity的代码如下所示:

    public class MyInformationActivity extends AppCompatActivity {
    
        private static final int MY_FRAGMENT = 0;
        private static final int ALTER_FRAGMENT = 1;
    
        private int status;
    
        private Toolbar toolbar;
        private AlterMyInformationFragment fragment;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_my_information);
            toolbar = (Toolbar) findViewById(R.id.my_information_toolbar);
            toolbar.setTitle("我的信息");
            setSupportActionBar(toolbar);
            getSupportActionBar().setHomeButtonEnabled(true);
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            status = MY_FRAGMENT;
        }
    
        private void setAlterMyInformationFragment() {
            fragment = new AlterMyInformationFragment();
            FragmentManager fragmentManager = getFragmentManager();
            FragmentTransaction transaction = fragmentManager.beginTransaction();
            transaction.replace(R.id.alter_my_information_frame, fragment);
            transaction.addToBackStack(null);
            status = ALTER_FRAGMENT;
            transaction.commit();
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.menu_my_information, menu);
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
                case android.R.id.home:
                    switch (status) {
                        case MY_FRAGMENT:
                            finish();
                            break;
                        case ALTER_FRAGMENT:
                            FragmentManager fragmentManager = getFragmentManager();
                            FragmentTransaction transaction = fragmentManager.beginTransaction();
                            transaction.remove(fragment);
                            status = MY_FRAGMENT;
                            transaction.commit();
                            break;
                        default:
                            break;
                    }
                    break;
                case R.id.alter_information:
                    setAlterMyInformationFragment();
                    break;
                default:
                    break;
            }
            return super.onOptionsItemSelected(item);
        }
    
    }
    我们用两个常量来表示当前到底处在哪一个Fragment,MY_FRAGMENT表示MyInformationFragment,ALTER_FRAGMENT表示AlterMyInformationFragment。我们可以看到Activity启动的时候自动给status赋值MY_FRAGMENT,在setAlterMyInformationFragment执行的时候给status赋值ALTER_FRAGMENT。于是我们在监听后退键的时候就可以进行判断了,如果当前和用户交互的是MyInformationFragment,执行finish()销毁Activity,如果和用户交互的是AlterMyInformationFragment,则销毁Fragment,让用户回到和MyInformationFragment交互的状态。


    我们处理完了后退键的问题,就该来解决动态变更toolbar的问题了。切换了Fragment,Toolbar如何相应的发生改变呢?大家平常使用菜单的时候,主要使用两个方法onCreateOptionView(Menu menu)和onOptionsItemSelected(MenuItem item),前者是加载菜单布局的,只会在Activity启动的时候调用一次,后者是对Menu中的item进行事件监听。也就是说,我们是不可能在onCreateOptionView(Menu menu)中对menu进行动态变更的。那我们来试试在onOptionsItemSelected(MenuItem item)中进行。于是我在启动AlterMyInforamtionFragment的代码后面加了这么两行:

    item.setIcon(R.drawable.ic_send_white_24dp);
    toolbar.setTitle("修改个人信息");
    这样启动AlterMyInforamtionFragment后的界面如下图所示:


    demo的界面还是很简单,用户可以自己修改自己的姓名和性别,看起来我们貌似实现了,这时候我们遇到一个问题,现在右上角的item的功能不再是启动新的Fragment了,而应该是用户修改好个人信息后进行提交,于是我们再次修改代码,让onOptionsItemSelected(MenuItem item)中的代码变成这样:

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
                case android.R.id.home:
                    switch (status) {
                        case MY_FRAGMENT:
                            finish();
                            break;
                        case ALTER_FRAGMENT:
                            FragmentManager fragmentManager = getFragmentManager();
                            FragmentTransaction transaction = fragmentManager.beginTransaction();
                            transaction.remove(fragment);
                            status = MY_FRAGMENT;
                            transaction.commit();
                            break;
                        default:
                            break;
                    }
                    break;
                case R.id.alter_information:
                    switch (status) {
                        case MY_FRAGMENT:
                            setAlterMyInformationFragment();
                            item.setIcon(R.drawable.ic_send_white_24dp);
                            toolbar.setTitle("修改个人信息");
                            break;
                        case ALTER_FRAGMENT:
                            fragment.postAlterInformation();
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
            return super.onOptionsItemSelected(item);
        }
    我们再次通过status来判断当前和用户交互的Fragment,从而决定这个item在不同状态下的功能。在ALTER_FRAGMENT状态下调用AlterMyInformationFragment类中的postAlterInformation()方法把修改好的信息通过网络发送给服务器。

    看起来我们已经实现这个功能了,但是还有很重要的一点没有做,那就是点击后退键的时候我们的toolbar应该变回之前的样子,但是我们现在还没有实现。那应该怎么做呢?修改toolbar的title很容易执行,只要在监听android.R.id.home的ALTER_FRAGMENT的switch分支下加一条

    toolbar.setTitle("我的信息");
    就可以了,但是变更图标似乎不那么容易,有些人说,再加一条
    item.setIcon("R.drawable.ic_create_white_24dp");
    不就行了吗,但是我们仔细看,我们之所以在启动AlterMyInforamtionFragment的时候能通过这条代码改变item的图标,是因为我们这行代码写在case R.id.alter_information这条switch分支下,此时的item代表的是右上角我们要修改的item。但是如果你要android.R.id.home这条switch分支下使用参数item,这个item代表的是左上角的后退键,即使执行上面那条代码,也不会有任何效果。但是,我们确实必须在点击后退键的时候改变右上角的item的图标,这就麻烦了,我找了item能调用的所有方法,没有发现能通过什么方式指定它的id,让它在非case R.id.alter_information分支下也代表这个item。后来我在网上查阅后发现,我们应该改变一种思路,因为onCreateOptionView(Menu menu)和onOptionsItemSelected(MenuItem item)这两个方法可能不够用了。

    我在网上看到很多文章发现,onPrepareOptionsMenu(Menu menu)这个方法也可以被调用多次,那我们就通过它来改变item的图标。于是MyInformationActivity的代码就变成了如下这样

    public class MyInformationActivity extends AppCompatActivity {
    
        private static final int MY_FRAGMENT = 0;
        private static final int ALTER_FRAGMENT = 1;
    
        private int status;
    
        private Toolbar toolbar;
        private AlterMyInformationFragment fragment;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_my_information);
            toolbar = (Toolbar) findViewById(R.id.my_information_toolbar);
            toolbar.setTitle("我的信息");
            setSupportActionBar(toolbar);
            getSupportActionBar().setHomeButtonEnabled(true);
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            status = MY_FRAGMENT;
        }
    
        private void setAlterMyInformationFragment() {
            fragment = new AlterMyInformationFragment();
            FragmentManager fragmentManager = getFragmentManager();
            FragmentTransaction transaction = fragmentManager.beginTransaction();
            transaction.replace(R.id.alter_my_information_frame, fragment);
            transaction.addToBackStack(null);
            status = ALTER_FRAGMENT;
            transaction.commit();
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.menu_my_information, menu);
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
                case android.R.id.home:
                    switch (status) {
                        case MY_FRAGMENT:
                            finish();
                            break;
                        case ALTER_FRAGMENT:
                            FragmentManager fragmentManager = getFragmentManager();
                            FragmentTransaction transaction = fragmentManager.beginTransaction();
                            transaction.remove(fragment);
                            status = MY_FRAGMENT;
                            transaction.commit();
                            invalidateOptionsMenu();
                            toolbar.setTitle("我的信息");
                            break;
                        default:
                            break;
                    }
                    break;
                case R.id.alter_information:
                    switch (status) {
                        case MY_FRAGMENT:
                            setAlterMyInformationFragment();
                            item.setIcon(R.drawable.ic_send_white_24dp);
                            toolbar.setTitle("修改个人信息");
                            break;
                        case ALTER_FRAGMENT:
                            fragment.postAlterInformation();
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
            return super.onOptionsItemSelected(item);
        }
    
        @Override
        public boolean onPrepareOptionsMenu(Menu menu) {
            menu.findItem(R.id.alter_information).setIcon(R.drawable.ic_create_white_24dp);
            return super.onPrepareOptionsMenu(menu);
        }
    
    }

    调用invalidateOptionsMenu()方法的地方,onPrepareOptionsMenu(Menu menu)就会被调用一次,值得说明的是,网上许多文章的教程写的是,如果你使用的是系统原生的ActionBar,应调用mActivity.getWindow().invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL); 如果使用的是ActionBarSherlock就应调用invalidateOptionsMenu(),目前大家可能使用ActionBar的逐渐减少,都逐步转向了Toolbar,Toolbar和ActionBarSherlock一样,都是调用invalidateOptionsMenu()方法。


    2016年10月7日最新更新:又经过了两个月的学习,我又遇到了更为复杂的问题,比如一个activity可能会管理三个甚至更多的Fragment,这时候我这篇博客写的解决方法就不太灵了,会让代码既复杂又臃肿,而且难以阅读。不过我已经找到了新的解决方法,通过冲分利用Fragment的返回栈模拟以及Fragment的生命周期回调,可以更简单易懂的解决这个问题,过段时间我会把最新的方法再写一篇博客。




    展开全文
  • 如果一个控件库button只支持设置一个图标,这显然是不够灵活实 用。有人可能会说“把几张图片,切图时做到一起就可以了么,反正一个控件也可以理解只有一个背景。” 如果涉及 到色调调整,这种做法会...
  • 如果一个控件库button只支持设置一个图标,这显然是不够灵活实 用。有人可能会说“把几张图片,切图时做到一起就可以了么,反正一个控件也可以理解只有一个背景。” 如果涉及 到色调调整,这种做法会...
  • 这时可以适当调整两次点击时间间隔,但是能设置过小否则会倒置软件失灵,最好大于100ms,调整好后进行测试,在空白的位置不停的双击,然后点下任务栏的图标,上面会显示出已屏蔽双击次数,如果这个数值跟你双击次数很接近...
  • 网站Rank值是从1到100分,如果在50分以下,就属于不安全网站了。 网站ICP证书查询 根据国务院令第292号《互联网信息服务管理办法》和信息产业部令第33号《非经营性互联网信息服务备案管理办法》规定,国家对...
  • 侧边工具栏除了基本和一些流程节点按钮外,还自定义新的节点按钮,自定义节点都可以有自有的图标、类型名称,定义后在使用可可在工作区内增加这些自定义节点。 ? 顶部栏可显示流程图数据组的标题,也可提供一些...
  • 基于JQueryGooFlow流程设计器

    热门讨论 2013-08-03 16:25:23
     侧边工具栏除了基本和一些流程节点按钮外,还自定义新的节点按钮,自定义节点都可以有自有的图标、类型名称,定义后在使用可可在工作区内增加这些自定义节点。  顶部栏可显示流程图数据组的标题,也可提供一些...
  •  侧边工具栏除了基本和一些流程节点按钮外,还自定义新的节点按钮,自定义节点都可以有自有的图标、类型名称,定义后在使用可可在工作区内增加这些自定义节点。  顶部栏可显示流程图数据组的标题,也可提供一些...
  •  侧边工具栏除了基本和一些流程节点按钮外,还自定义新的节点按钮,自定义节点都可以有自有的图标、类型名称,定义后在使用可可在工作区内增加这些自定义节点。  顶部栏可显示流程图数据组的标题,也可提供一些...
  • 当然,通常这些地方都会提供贴心登录信息记忆功能来方便您使用,可您有没有想过,万一不慎遗失了手机那捡到它并怀有恶意人也会因此而轻松使用本来只属于东西呢?另外,不同地方对用户名和密码长度、...
  • 当然,通常这些地方都会提供贴心登录信息记忆功能来方便您使用,可您有没有想过,万一不慎遗失了手机那捡到它并怀有恶意人也会因此而轻松使用本来只属于东西呢?另外,不同地方对用户名和密码长度、...
  • 侧边工具栏除了基本和一些流程节点按钮外,还自定义新的节点按钮,自定义节点都可以有自有的图标、类型名称,定义后在使用可可在工作区内增加这些自定义节点 顶部栏可显示流程图数据组的标题,也可提供一些常用操作...
  • 删除页眉和页脚(包括分隔线),选择“视图︱页眉和页脚”,首先删除文字,然后点击页眉页脚工具栏的“页面设置”按钮,在弹出的对话框上点“边框”,在“页面边框”选项卡,边框设置为“无”,应用范围为“本节”;...
  • 会计理论考试题

    2012-03-07 21:04:40
    31.启动Windows98中文版后,下列中___C___的图标不是常见的图标。 A、我的电脑 B、回收站 C、资源管理器 D、收件箱 32.多媒体电脑是指 __B___ 。 A、专供家庭娱乐用的电脑 B、能处理文字、图形、影像与声音等信息的...
  • 网际畅游 MyIE 3.0 源代码

    热门讨论 2004-10-11 09:11:06
    本地文件浏览:在工具栏上选择文件按钮,会出现一个类似文件管理器窗口,其中列出了你目录和文件, 双击文件即可进行浏览了。你可改变下部文件类型列表以显示不同文件类型。改变后可按旁边刷新按钮刷新...
  • 库存量是对商品库存的统计,工具栏的查询可以设置自己想要查询的仓库以及商品的查询。 收银员权限设置 鸿威台球厅计费管理系统收银员权限设置细分了一百多项,拒绝一切收银漏洞,让您放心把球房交给别人打理。鸿威...
  • 隐藏系统窗口

    2013-06-05 10:16:32
    任务显示图标:指右下角的图标,默认是显示的。 随系统启动:可以在系统运行时跟着运行 隐藏时静音:隐藏同时暂停一切声音 使用声音效果:隐藏和显示窗口时的音效。 隐藏进程托盘图标:在隐藏窗口的同时隐藏同一...
  • flash shiti

    2014-03-14 10:32:41
    15. 下列那几个属性是flash mx 建议使用属性 □ A. scroll □ B. maxscroll □ C. _droptarget □ D. _highquality 16. 下面语句说法正确是: □ A. 目前Flash 最新创作平台是Flash MX,播放插件是Flash ...
  • JS 流程图 流程图插件

    2015-06-11 22:33:31
     侧边工具栏除了基本和一些流程节点按钮外,还自定义新的节点按钮,自定义节点都可以有自有的图标、类型名称,定义后在使用可可在工作区内增加这些自定义节点。  顶部栏可显示流程图数据组的标题,也可提供一些...
  • PC网站导航在页面顶部,且不会保持在窗口顶部,当用户看完页面,想使用导航切换页面时,需要滚轮滑动多次,返回顶部,非常方便。而Scroll To Top Button这款工具,就可以一键返回页面顶部,或页面底部,非常...
  • 10、下列(D)不属于Outlook Express功能。 A、可以设置发送邮件优先级 B、查看已发送邮件 C、转发收到邮件 D、在线聊天 二、填空题 1、世界上第一台电子计算机名为ENIAC。 2、第四代计算机逻辑元件...
  • 由于Css属性设置问题,导致界面完整等等。设计算法都是自己编写,可能存在不是最优算法情况。 二、设计正文 1 需求分析  建立一个用户可以自由交易平台,通过ajax实现局部刷新,实现网站更具人性化,...
  • WP主题:HotNewspro 2.72

    2013-10-05 09:45:41
    首页分类文章列表,自动排除上面已显示最新日志,支持父子分类和一篇文章属于多分类。 ■ 横向滚动图片模块 默认显示,开启后,通过为文章添加自定义栏目,名称:recommend,值:可任意,添加什么都行,...
  • 计算机应用技术 实用手册 Xnllz 2011.7.29 ...有时候在系统任务你见到小喇叭有可能在这里被关闭了,即:[DISABLED],遇到这种情况可从新进入COMS把此项打开即可,即:[ENABLED]。 SATA...
  • iPhone开发秘籍(第2版)--源代码

    热门讨论 2012-12-11 13:51:22
    4.3.2 导航栏、工具栏和选项卡栏 118 4.3.3 键盘和拾取器 119 4.3.4 文本字段 120 4.3.5 UIScreen类 120 4.4 构建界面 120 4.5 实战演练:使用IB构建温度单位转换器 121 4.5.1 新建项目 121 4.5.2 添加媒体 ...

空空如也

空空如也

1 2
收藏数 38
精华内容 15
关键字:

一般不属于工具栏的图标