如何用Toolbar取代ActionBar

来源:互联网 发布:aspen软件介绍 编辑:程序博客网 时间:2024/06/04 17:56

在 Android 3.0 开始,App Bar的功能逐渐被加入到 ActionBar中,而这个 ActionBar 被包含在 Theme 中,不过对于开发者来说,缺乏的灵活性, 而且伴随着每个版本的发布,ActionBar 也表现出差异,因此我们经常自定义一个 Topbar 来取代传统的 ActionBar。伴随着 android.support.v7.widget.Toolbar 的出现,这2个问题得以解决。这篇文章就来探讨如何把之前 ActionBar 上的功能用在 Toolbar 上。


如何添加Toolbar

使用 v7 support library

为了兼容低版本,需要使用v7兼容库,在Android Studio中,通过F4按键为项目添加Dependencies,来添加com.android.support:appcompat-v7。这样,就会在build.gradle中自动添加下面的一段

compile 'com.android.support:appcompat-v7:24.2.1'

然后 Activity 要继承自 AppcompatActivity

MainActivity extends AppCompatActivity

去掉 Theme 中默认包含的 ActionBar

既然要用 Toolbar 取代 ActionBar,就需要设计一个没有 ActionBar 的 Theme

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">        <item name="colorPrimary">@color/colorPrimary</item>        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>        <item name="colorAccent">@color/colorAccent</item>    </style>

为 Activity 添加 Theme

<activity android:name=".MainActivity" android:theme="@style/AppTheme">

布局中添加 Toolbar

    <android.support.v7.widget.Toolbar        android:id="@+id/toolbar"        android:layout_width="match_parent"        android:layout_height="?android:attr/actionBarSize"        android:background="@color/colorPrimary"        android:elevation="4dp"        app:titleTextColor="@android:color/white"/>

既然用 Toolbar 取代 ActionBar,基本的需要做到以下三点:
1. Toolbar 颜色(android:background)与原来一致
2. Toolbar 高度(android:layout_height)与原来一致
3. 按照md风格,我们增加一个投影(android:elvation 一般为 4dp)。


在 Activity 中,用 Toolbar 取代 ActionBar

        mToolbar = (Toolbar) findViewById(R.id.toolbar);        setSupportActionBar(mToolbar);

当我们把上面的做完了之后 ,Toolbar 就显示出来了,效果如下。

Toolbar


怎么使用 Toolbar

既然我们用 Toolbar 取代了 ActionBar,那么我们操作 Toolbar 就应该是操作 ActionBar了。

        mToolbar = (Toolbar) findViewById(R.id.toolbar);        mToolbar.setNavigationIcon(R.mipmap.ic_home_black_24dp);        mToolbar.setLogo(R.mipmap.ic_launcher);        mToolbar.setTitle("HelloToolbar");        mToolbar.setSubtitle("SubTitle");        setSupportActionBar(mToolbar);

得到效果如下

Toolbar1

这是提一下的是这个最左边的导航图标,默认的话就是一个向左的箭头,如果我们不在 Toolbar 中设置,也可以在 ActionBar 中设置

        mToolbar = (Toolbar) findViewById(R.id.toolbar);        //在 Toolbar 中设置返回图标        //mToolbar.setNavigationIcon(R.mipmap.ic_home_black_24dp);        mToolbar.setTitle("HelloToolbar");        setSupportActionBar(mToolbar);        android.support.v7.app.ActionBar actionbar = getSupportActionBar();        //用 ActionBar 设置返回图标        actionbar.setDisplayHomeAsUpEnabled(true);

效果如下

Toolbar2

那么现在问题来了,这个返回按键和硬件上的返回键有什么区别?硬件上的返回键就是如同人的逻辑思维一样,按照操作顺序依次返回。Toolbar 上的返回键一般就是返回到主 Home 页面,当然我们也可以选择让它返回到特定的页面。


使用 Toolbar 上的返回键

先看一个使用 Toolbar 上返回键的图

Back

Activity 设置 Toolbar

SecondActivity 的 Toolbar 设置如下

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar1);        toolbar.setNavigationIcon(R.mipmap.home);        setSupportActionBar(toolbar);        getSupportActionBar().setTitle("SecondActivity");

从这个代码可以看到toolbar.setNavigationIcon(R.mipmap.home); 就是等于 getSupportActionBar().setDisplayHomeAsUpEnabled(true); , 只不过我们就是替换了一个图标。

AndroidManifest.xml 声明返回的 Activity

在 AndroidManifest.xml 文件中,设置 activity的 parentActivityName 属性(meta-data 为了兼容低版本)

        <activity            android:name=".MyChildActivity"            android:parentActivityName=".MainActivity">            <!--meta-data兼容低版本-->            <meta-data                android:name="android.support.PARENT_ACTIVITY"                android:value=".MainActivity"/>        </activity>

Toolbar 添加 Menu

基本的Menu

这与原来设置 Menu 的方式一致

    /**     * 为Toolbar设置Menu     */    @Override    public boolean onCreateOptionsMenu(Menu menu) {        getMenuInflater().inflate(R.menu.toolbar_menu, menu);        return super.onCreateOptionsMenu(menu);    }    /**     * 处理选中的菜单     */    @Override    public boolean onOptionsItemSelected(MenuItem item) {        switch (item.getItemId()) {            case R.id.delete1:                break;        }        return super.onOptionsItemSelected(item);    }
<?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        android:id="@+id/settings"        android:icon="@mipmap/ic_menu_settings_holo_light"        android:title="设置"        app:showAsAction="never" />    <item        android:id="@+id/sync"        android:icon="@mipmap/sync_settings"        android:title="同步"        app:showAsAction="never" />    <item        android:id="@+id/share"        app:actionProviderClass="android.support.v7.widget.ShareActionProvider"        android:icon="@mipmap/ic_menu_share"        android:title="分享"        app:showAsAction="ifRoom" />    <item        android:id="@+id/action_search"        android:icon="@mipmap/ic_menu_search"        android:title="搜索"        app:actionViewClass="android.support.v7.widget.SearchView"        app:showAsAction="ifRoom|collapseActionView" /></menu>

这里特别提一下,关于 android.support.v7.widget.SearchView,我之前在 xml 文件中,用的是 android:actionViewClass,然后无论怎么点搜索按钮都没有用。 正确的写法是 app:actionViewClass

效果如下
这里写图片描述

这里写图片描述

从第二张图中,我们可以看到,在 overflow 中,默认是不显示图标的,那么我们怎么让它默认显示图标呢?

    /**     * 显示 OverFlow 中的图标     */    @Override    protected boolean onPrepareOptionsPanel(View view, Menu menu) {        if (menu != null) {            if (menu.getClass().getSimpleName().equals("MenuBuilder")) {                try {                    Method m = menu.getClass().getDeclaredMethod(                            "setOptionalIconsVisible", Boolean.TYPE);                    m.setAccessible(true);                    m.invoke(menu, true);                } catch (Exception e) {                }            }        }        return super.onPrepareOptionsPanel(view, menu);    }

通过反射,我们让图标显示了,现在的效果是这样的

这里写图片描述

发现图标不对称,这是因为图片大小不一致导致的。

添加ActionProvider

上面我们添加过 ShareActionProvider , 但是点击是没有效果的,因为我们还没有给它设置 Intent

    @Override    public boolean onCreateOptionsMenu(Menu menu) {        getMenuInflater().inflate(R.menu.main_menu, menu);        MenuItem shareItem = menu.findItem(R.id.share);        ShareActionProvider shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(shareItem);        Intent shareIntent = new Intent(Intent.ACTION_SEND);        shareIntent.setType("image/*");        //shareIntent.putExtra(Intent.EXTRA_STREAM, myImageUri);        shareActionProvider.setShareIntent(shareIntent);        return super.onCreateOptionsMenu(menu);    }

上面的代码中注释了一段,既然是分享,要有分享的东西,一般为图片或者文字信息,注释的代码是代表分享图片

当我们点击后,效果如下

这里写图片描述

添加ActionView

上面的代码中,我们添加过 SearchView

    <item        android:id="@+id/action_search"        android:icon="@mipmap/ic_menu_search"        android:title="搜索"        app:actionViewClass="android.support.v7.widget.SearchView"        app:showAsAction="ifRoom|collapseActionView" />

我们再监听下 SearchView 的展开收缩事件

        MenuItem searchItem = menu.findItem(R.id.action_search);        SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);        MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {            @Override            public boolean onMenuItemActionExpand(MenuItem item) {                return false;            }            @Override            public boolean onMenuItemActionCollapse(MenuItem item) {                return false;            }        });

Toolbar 上实现 PopMenu

PopMenu 是锚到 View 的菜单 ,如果空间足够,就显示在 View 的下方,否则显示在上方。有时候我们想定制自己的 Menu,我们需要在 ActionBar 上加上一个控件,如 ImageView,然后做出一个自己想要的弹出菜单的样式 。 在 ActionBar 上是很难定制的,我们往往选择自己写一个 Topbar,但是现在,我们用 Toolbar 可以实现。

在 Toolbar 上添加 ImageView,并实现 Popmenu 效果

        mToolbar = (Toolbar) findViewById(R.id.toolbar);        //在Toolbar上添加ImageView        ImageView button = new ImageView(this);        button.setImageResource(R.mipmap.ic_input_add);        mToolbar.addView(button);        //为ImageView设置参数        Toolbar.LayoutParams layoutParams = (Toolbar.LayoutParams) button.getLayoutParams();        layoutParams.gravity = GravityCompat.END;        layoutParams.rightMargin = 20;        //监听点击事件实现Popmenu效果        button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                PopupMenu popup = new PopupMenu(MainActivity.this, v);                MenuInflater inflater = popup.getMenuInflater();                inflater.inflate(R.menu.toolbar_menu, popup.getMenu());                popup.show();            }        });        setSupportActionBar(mToolbar);

res/menu/toolbar_menu.xml

<?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        android:icon="@mipmap/ic_launcher"        android:title="设置"        app:showAsAction="never"/>    <item        android:icon="@mipmap/ic_search_black_24dp"        android:title="搜索"        app:showAsAction="never"/></menu>

看下效果

popmenu

这个显示效果相比较 overflow 的显示方式,是否看起来舒服一点?因为现在的显示方式是在 ImageView 的下方,而 overflow 的显示方式是覆盖了 overflow 图标了。 不过还有一点,我们还要给它设置图标了,那又如何设置了,这里需要用到反射。

        button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                PopupMenu popup = new PopupMenu(MainActivity.this, v);                try {                    Field[] fields = popup.getClass().getDeclaredFields();                    for (Field field : fields) {                        if ("mPopup".equals(field.getName())) {                            field.setAccessible(true);                            Object menuPopupHelper = field.get(popup);                            Class<?> classPopupHelper = Class.forName(menuPopupHelper                                    .getClass().getName());                            Method setForceIcons = classPopupHelper.getMethod(                                    "setForceShowIcon", boolean.class);                            setForceIcons.invoke(menuPopupHelper, true);                            break;                        }                    }                } catch (Exception e) {                    e.printStackTrace();                }                MenuInflater inflater = popup.getMenuInflater();                inflater.inflate(R.menu.toolbar_menu, popup.getMenu());                popup.show();            }        });

现在我们来看下自己实现的 overflow 和 系统原本的 overflow的对比图

这里写图片描述

从效果看,是不是我们自定义的 overflow 已经没有了系统 overflow的缺点了?

当然最后我们还可以添加 Popmenu 的点击事件

                popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {                    @Override                    public boolean onMenuItemClick(MenuItem item) {                        return false;                    }                });

Toolbar 实现 ActionMode Menu

之前由于都是采用 ListView,这篇文章我将用 RecyclerView,与时俱进吧。

效果如下

这里写图片描述

在写代码前,我们考虑如何做

  • Adapter 提供长按和短按接口给 Activity,用于开启 ActionMode
  • Activity实现两个接口,并在长按和短按中保存postion,并更新RecyclerView的Item背景色
  • 开启ActionMode后,点击 delete menu,删除选中的选项

思路大致就是这样,我试过细分每个步骤的代码,但是整体性不强,所以直接上了所有代码

/** * Created by David Chow on 2016/10/9. */public class MainActivity extends AppCompatActivity implements MyAdapter.onItemListener {    private Toolbar mToolbar;    private RecyclerView mRecyclerView;    private MyAdapter mAdapter;    private List<String> mDatas;    private ActionMode mActionMode;    private ActionMode.Callback mCallback;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mToolbar = (Toolbar) findViewById(R.id.toolbar);        setSupportActionBar(mToolbar);        ActionBar supportActionBar = getSupportActionBar();        supportActionBar.setTitle("Toolbar");        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));        mDatas = new ArrayList<>();        for (int i = 0; i < 20; i++) {            mDatas.add(String.valueOf(i));        }        mAdapter = new MyAdapter(mDatas);        mAdapter.setOnItemClickListener(this);        mRecyclerView.setAdapter(mAdapter);        mCallback = new ActionMode.Callback() {            @Override            public boolean onCreateActionMode(ActionMode mode, Menu menu) {                getMenuInflater().inflate(R.menu.main_menu, menu);                return true;            }            @Override            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {                return false;            }            @Override            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {                switch (item.getItemId()) {                    case R.id.delete:                        Toast.makeText(MainActivity.this, "Delete~~~", Toast.LENGTH_SHORT).show();                        // 删除数据                        mAdapter.removeSelectedItems();                        // 退出ActionMode                        mActionMode.finish();                        return true;                }                return false;            }            @Override            public void onDestroyActionMode(ActionMode mode) {                // 重置ActionMode                mActionMode = null;                // 取消所有选中状态                mAdapter.clearAllSelectedItems();            }        };    }    @Override    public void onClick(int position) {        if (mActionMode != null) {            toggleSelection(position);        }    }    private void toggleSelection(int position) {        // 根据position ,用Adapter更新Item选中状态和背景色        mAdapter.toggleSelection(position);        // 更新 ActionMode 上的计数        mActionMode.setTitle(String.valueOf(mAdapter.getSelectedCount()));    }    @Override    public boolean onLongClick(int position) {        //开启 ActionMode        if (mActionMode == null) {            mActionMode = startSupportActionMode(mCallback);        }        toggleSelection(position);        return true;    }}
/** * Created by David Chow on 2016/10/9. */public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {    private List<String> mDatas;    private onItemListener mListener;    private SparseBooleanArray mSelectedItems;    public MyAdapter(List<String> mDatas) {        this.mDatas = mDatas;        mSelectedItems = new SparseBooleanArray();    }    @Override    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false));    }    @Override    public void onBindViewHolder(ViewHolder holder, int position) {        holder.mTextView.setText(mDatas.get(position));        holder.mCardView.setBackgroundColor(mSelectedItems.get(position) ? Color.parseColor("#ff575bd4") : Color.parseColor("#ffffffff"));    }    /**     * 获取选中Item的数量     */    @Override    public int getItemCount() {        return mDatas.size();    }    /**     * 更新选中Item的状态和背景色     */    public void toggleSelection(int position) {        // 更新选中的状态        if (mSelectedItems.get(position)) {            mSelectedItems.delete(position);        } else {            mSelectedItems.put(position, true);        }        // 更新Item        notifyItemChanged(position);    }    /**     * 获取选中Item的个数     */    public int getSelectedCount() {        return mSelectedItems.size();    }    /**     * 取消所有选中的状态并更新背景     */    public void clearAllSelectedItems() {        List<Integer> positions = getPositions();        //清除所有的状态        mSelectedItems.clear();        // 更新背景        for (int i = 0; i < positions.size(); i++) {            notifyItemChanged(positions.get(i));        }    }    /**     * 删除选中的Item     */    public void removeSelectedItems() {        List<Integer> positions = getPositions();        // 从大到小排序        Collections.sort(positions, new Comparator<Integer>() {            @Override            public int compare(Integer o1, Integer o2) {                return o2 - o1;            }        });        for (int i = 0; i < positions.size(); i++) {            int position = positions.get(i);            mDatas.remove(position);            notifyItemRemoved(position);        }    }    /**     * 获取选中Item的positions     */    @NonNull    private List<Integer> getPositions() {        List<Integer> positions = new ArrayList<>();        // 获取选中的position集合        for (int i = 0; i < mSelectedItems.size(); i++) {            positions.add(mSelectedItems.keyAt(i));        }        return positions;    }    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {        TextView mTextView;        CardView mCardView;        public ViewHolder(View itemView) {            super(itemView);            mTextView = (TextView) itemView.findViewById(R.id.item_tv);            mCardView = (CardView) itemView.findViewById(R.id.cardView);            itemView.setOnClickListener(this);            itemView.setOnLongClickListener(this);        }        @Override        public void onClick(View v) {            if (mListener != null) {                mListener.onClick(getLayoutPosition());            }        }        @Override        public boolean onLongClick(View v) {            if (mListener != null) {                return mListener.onLongClick(getLayoutPosition());            }            return false;        }    }    public interface onItemListener {        void onClick(int position);        boolean onLongClick(int position);    }    public void setOnItemClickListener(onItemListener listener) {        mListener = listener;    }}

项目地址:https://github.com/buxiliulian/Toolbar-RecyclerView

1 0
原创粉丝点击