Android Support Library 之 Toolbar

    在 Android 3.0 开始 ,所有的主题默认把 ActionBar 作为默认的 App Bar,但是它很死板,不容易自行设计想要的风格,     在 v7 兼容库中,出现了 Toolbar, 它改善了这个问题,伴随着 Toolbar 取代 传统的 ActionBar ,ActionBar 上的功能要怎么在 ToolBar 上实现,需要我们去探讨下 。
    

如何添加Toolbar

1、我们使用的是 v7 兼容库,所以 Activity 要继承自 AppCompatActivity
MainActivity extends AppCompatActivity

    2、修改主题 ,去掉默认的 ActionBar,v7 兼容库给我们提供了一个默认的没有 ActionBar 的主题
<application
    android:theme="@style/Theme.AppCompat.Light.NoActionBar"
    />
    在这里,我把 application 的设置为没有 ActionBar 的主题 ,这样所有的 Activity 都会应用这个主题 ,但是实际中并不是所有的 Activity 都需要自己设计 ActionBar,因此我们最好把这个主题应用到单独 Activity 上。
    例如   
        <activity
            android:name=".MainActivity"
            android:theme="@style/AppTheme.NoActionbar">

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
 
    <style name="AppTheme.NoActionbar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>



    3、在布局中添加 Toolbar
<android.support.v7.widget.Toolbar
   android:id="@+id/my_toolbar"
   android:layout_width="match_parent"
   android:layout_height="?attr/actionBarSize"
   android:background="?attr/colorPrimary"
   android:elevation="4dp"
   android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
   app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>


    我们可以看到,ToolBar 的高度用的是系统的 ActionBar的高度,在 Material Design 中,需要为一个视图设计 一个投掷阴影 android:elevation 通常为4dp,为了与 Material Design 设计规则一致 ,我们还需要为 ToolBar 设计着色,取的就是 colorPrimary 的颜色 。

    4、在代码中把 Toolbar 设置为 App Bar ,并设置它的 Navigation Icon, Logo,Title,SubTitle
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        toolbar.setNavigationIcon(R.mipmap.ic_action_back);
        toolbar.setLogo(R.mipmap.ic_launcher);
        toolbar.setTitle("Toolbar");
        toolbar.setSubtitle("Toolbar SubTitle");
        setSupportActionBar(toolbar);

捕获.PNG
看下效果

可是标题的颜色和 overflow 的颜色并不统一,全改为白色或许可以
    <style name="AppTheme.NoActionbar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <!-- title -->
        <item name="android:textColorPrimary">@android:color/white</item>
        <!--subtitle-->
        <item name="android:textColorSecondary">@android:color/white</item>
    </style>


再看下效果
Android Support Library 之 Toolbar_第1张图片
捕获.PNG
还可以吧~

如何使用Toolbar

  一旦我们设置了 ToolBar 作为了App Bar,那么就可以使用 v7 兼容包中的 ActionBar 类的方法
        例如
    private void setupActionbar() {
        android.support.v7.app.ActionBar actionBar = getSupportActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);
        actionBar.hide();
    }
         我们在操作ActionBar的时候,也就相当于在操作Toolbar,因此在 Acionbar 上设置 title,Logo 都会覆盖 toolbar 上的显示

在 Toolbar 上增加 action menu 中的 options menu (>=3.0)

  在上面的图中,可以看到 action menu 出现在 toolbar 上,这与之前 action menu 添加到 action bar 上一样操作
         定义menu 菜单
<?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/delete"
        android:icon="@mipmap/ic_action_favorite"
        android:title="Favorite"
        app:showAsAction="ifRoom" />
 
    <item
        android:id="@+id/settings"
        android:icon="@mipmap/ic_launcher"
        android:title="Settings" />
 
</menu>
         显示menu
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_menu, menu);
        return true;
    }
    响应menu事件
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.favorite:
                Toast.makeText(this, "Favorite", Toast.LENGTH_SHORT).show();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

 在 Toolbar 中增加 action menu 中的 contextual menu 之 contextual action mod ( >= 3.0)

         先来看下效果

GIF.gif
    传统的是用 ListView,但是为了跟上时代的脚步,我想是时候用 RecyclerView 了,但是它没有提供如 ListView 一样的方法
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener();


    突然发现 ,RecyclerView没有这个方法,只能自己来做了,那么我们就需要想想如何设计这个问题。
    首先,长按事件,可以在 ViewHolder 中提供接口
    然后在 Activity 实现,通过实现长按来开启contextual action mode
    这个时候,我们需要在 adapter 中改变 item 的颜色 ,我们可以用 notifyItemChanged(position) 来让     RecyclerView 重新调用 onBindViewHolder() 方法来给 item 设置背景色,然后,我们可以选择其它的 item 并同时让它们变色,再响应 contextual action mode 的 delete 响应事件来删除 item。这样看来一切很完美 ,那我们来一步一步实现。
    1、先实现 RecyclerView
    item 布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_cardview"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:layout_margin="2dp"
    app:cardUseCompatPadding="true">
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
 
        <TextView
            android:id="@+id/maintitle"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="123"
            android:textSize="20sp" />
    </LinearLayout>
 
</android.support.v7.widget.CardView>
    实现 Recycler.Adapter
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private recyclerViewListener mListener;
    private List<String> mDatas;
 
    public MyAdapter(List<String> list, MyAdapter.recyclerViewListener listener) {
        mDatas = list;
        mListener = listener;
    }
 
    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
        ViewHolder viewHolder = new ViewHolder(v);
        return viewHolder;
    }
 
    @Override
    public void onBindViewHolder(MyAdapter.ViewHolder holder, int position) {
        holder.title.setText(mDatas.get(position));
    }
 
    @Override
    public int getItemCount() {
        return mDatas.size();
    }
 
    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
        TextView title;
        CardView cardView;
 
        public ViewHolder(View itemView) {
            super(itemView);
            title = (TextView) itemView.findViewById(R.id.maintitle);
            cardView = (CardView) itemView.findViewById(R.id.item_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 recyclerViewListener {
        void onClick(int position);
        boolean onLongClick(int position);
    }
}

在MainActivity.java中添加Adapter,并添加空的 onClick() 和 onLongClick() 实现
    private void setupRecyclerView() {
        mDatas = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            mDatas.add(String.valueOf(i));
        }
        mRecyclerView = (RecyclerView) findViewById(R.id.my_recyclerView);
        mAdapter = new MyAdapter(mDatas, this);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        mRecyclerView.setAdapter(mAdapter);
    }

这样,RecyclerView就已经显示出来了
Android Support Library 之 Toolbar_第2张图片
捕获.PNG

    2、实现长按和短按
        mCallback = new ActionMode.Callback() {
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                getMenuInflater().inflate(R.menu.action_mode_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:
                        mActionMode.finish();
                        return true;
                }
                return false;
            }
 
            @Override
            public void onDestroyActionMode(ActionMode mode) {
                mActionMode = null;
            }
        };

   @Override
    public void onClick(int position) {
        toggleSelection(position);
    }
    
    @Override
    public boolean onLongClick(int position) {
        if (mActionMode == null) {
            mActionMode = startSupportActionMode(mCallback);
        }
        toggleSelection(position);
 
        return true;
    }
 
    private void toggleSelection(int position) {
        mAdapter.toggleSelection(position);
        int count = mAdapter.getSelectedItemCount();
        if (count == 0) {
            mActionMode.finish();
        } else {
            mActionMode.setTitle(String.valueOf(count));
        }
    }

3、在 Adapter 中添加实现变色 
    mSelectedItems = new SparseBooleanArray();
    public void toggleSelection(int position) {
        if (mSelectedItems.get(position, false)) {
            mSelectedItems.delete(position);
        } else {
            mSelectedItems.put(position, true);
        }
        notifyItemChanged(position);
    }

通过调用  notifyItemChanged() 来让 RecyclerView 重新调用 onBindViewHolder(),然后让背景变色
    @Override
    public void onBindViewHolder(MyAdapter.ViewHolder holder, int position) {
        holder.title.setText(mDatas.get(position));
        holder.cardView.setBackgroundColor(mSelectedItems.get(position, false) ? Color.parseColor("#883a44b7") : Color.parseColor("#ffffffff"));
    }

这个时候,可以看下效果
GIF.gif

现在出现两个问题,出现2个 "actionbar" ,而且我们要操作的 action bar 背景颜色与我们设计的主题不一致 。另外,当我们点击了 delete 菜单的时候 ,被选中的 item 颜色并没有撤消?
如何解决出现2个 "actionbar" 问题
    <style name="AppTheme.NoActionbar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <!-- title -->
        <item name="android:textColorPrimary">@android:color/white</item>
        <!--subtitle-->
        <item name="android:textColorSecondary">@android:color/white</item>
        <item name="windowActionModeOverlay">true</item>
        <item name="actionModeBackground">@color/colorPrimary</item>
    </style>
windowActionModeOverlay 是表示 action mode 覆盖 actionbar,actionModeBackground 当然就是 action mode的背景啦,别问我为什么知道,因为我就是随便试试就出来了。

如何解决在action mode消失后,选中的 item 背景颜色没有撤消?我们可以想想,在 ActionModeCallback 消失后,这个时候我们应该通过 notifyItemChanged() 再次让 recyclerview 来调用 onBindViewHolder() 来刷新 item
            @Override
            public void onDestroyActionMode(ActionMode mode) {
                mAdapter.clearSelectedItems();
                mActionMode = null;
            }
    public void clearSelectedItems() {
        for (int i = 0; i < mSelectedItems.size(); i++) {
            notifyItemChanged(mSelectedItems.keyAt(i));
        }
        mSelectedItems.clear();
    }

onBindViewHolder()就可以再次刷新了
    @Override
    public void onBindViewHolder(MyAdapter.ViewHolder holder, int position) {
        holder.title.setText(mDatas.get(position));
        holder.cardView.setBackgroundColor(mSelectedItems.get(position, false) ? Color.parseColor("#883a44b7") : Color.parseColor("#ffffffff"));
    }

看下效果
Android Support Library 之 Toolbar_第3张图片
GIF.gif
是不是很 perfect ~ 


4、最后一步,我们要做到删除选中的 item,这个操作当然是在 ActionMode.Callback中的
            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.delete:
                        mAdapter.removeSelectedItems();
                        mActionMode.finish();
                        return true;
                }
                return false;
            }
常规的想,我们可以根据
mSelectedItems = new SparseBooleanArray();
mSelectedItems中的 key 值 ,也就是 item 的 postion ,来一个一个删除 ,但是并不能很好如我们如愿,这就是涉及到 recyclerview 的一个 update 过程 ,当我们删除了其中一个 item 时候 ,它会重新排列 item ,这个时候第个 item 的 positon 就变了,也就是说,如果 key 值依次为 [1,4,3] ,那么实际中我们要删除的 positon 为 [1,3,2],当时我有点懵B了,后来 google 了下,在 这个项目中找到答案 ,我们可以对这个 key 值进行从大到小排序 ,这样 [1,.4,3] 就变为了 [4,3,1] ,这样我们在删除第5个的时候 ,前面的顺序还是没变,这样没不影响了,实在是屌 ~
    public void removeItem(int position) {
        mDatas.remove(position);
        notifyItemRemoved(position);
    }
 
    public void removeSelectedItems() {
        List<Integer> positions = new ArrayList<>();
        for (int i = 0; i < mSelectedItems.size(); i++) {
            positions.add(mSelectedItems.keyAt(i));
        }
 
        Collections.sort(positions, new Comparator<Integer>() {
            @Override
            public int compare(Integer lhs, Integer rhs) {
                return rhs - lhs;
            }
        });
 
        for (int i = 0; i < positions.size(); i++) {
            removeItem(positions.get(i));
        }
    }

          从这篇我们可以看到了 RecyclerView 和 Toolbar 的灵活性,但是这里讲述的也只是一角,后续我会继续完善,并对Android support Library 的新控件进行探讨~

你可能感兴趣的:(android,toolbar)