咳咳,好久不见,因为土豆某天突发奇想,觉得作为一只猿,还是应该有自己的爱好,所以就去买了一把吉他,然后开始了长达三个多月的扰民生活,然后就没空写文章啦。。。扯远了,上一次说这章原本应该是讲popupWindow的,我是想用使用popupWindow选取相册以及调用摄像头来做案例的,不过安卓6.0之后,为了让用户的手机更加安全,所以将一些比较重要的权限比如开启摄像头,需要动态获得权限,后来土豆觉得如何优雅的动态获取权限也是比较重要的,所以只能先放着。今天本章就讲讲snackBar吧
废话不多说,先看图吧,效果图展示的是系统样式的snackBar以及我们自己封装的snackBar

想必大家都是知道toast的,这里我没有使用原生的toast,而是用的toasty。(土豆前面有讲哦,感兴趣的可以看看,不感兴趣的也不用管,不影响功能实现)今天的主角SnackBar是谷歌取代推出来取代toast的一个组件,为了不让toast伤心,土豆都没有在这个案例中使用toast,暖心吧。。
那么这时候有好事者又要问了,土豆土豆,那snackBar有什么优点呢,我觉得toast很好用的啊。要说snackBar的优点的话,我们要先讲讲toast的缺陷:
第一:toast会重复显示,比如这样一个场景,我们点击一个控件,会弹出一个toast,但是如果一直点,不断的点,会发现,toast的提示会显示很久很久很久,这是因为系统将toast放进一个队列中,前一个toast关闭后,后一个toast才显示。网上也有很多解决这个问题的办法,大多是采用单例来实现,土豆后面也会讲。那么作为竞争对手的snackBar,自然抓住了toast的痛处,不会犯这样的错误啦,snackBar在重复点击时,会立即关闭上一个snackBar,显示新的snackBar,这样就提升了用户体验
第二:toast作为提示的组件,是不能与用户进行交互的,就像一个大喇叭,只会把消息传递给你,至于你有啥反应,它通通不管,而snackBar呢,就像一个知心大姐姐,不仅把消息传递给你,还可以接收你的反馈,并作出反应。举个小例子,我们使用recyclerView的时候,在没数据的时候需要通知用户,这次请求没有数据,toast只会原封不动的告诉用户没有数据,但是使用snackBar的话,可以增加一个“重试”的按钮,这样一来,用户不仅知道了这条消息,还可以与这条消息进行交互,好感度upupup
第三:颜值高,Em。。颜值高,存在感就高,存在感高,用户就高兴。。。
安利了这么多,该咋用呢,相信有心急的同学已经迫不及待了,那么久正式进入snackBar的使用吧。
第一步:snackBar是support design下的一个组件,我们自然要先导入这个啦,在app级别的gradle中导入它
compile 'com.android.support:design:26.1.0'
值得注意的是,26.1.0是土豆当前用的sdk,你的不一样也是正常的
第二步:导入成功之后,就是开始使用啦,首先讲讲系统的snackbar
Snackbar.make(toolbar, "这是系统的snackBar", Snackbar.LENGTH_INDEFINITE)
.setAction("你瞅啥", new View.OnClickListener() {
@Override
public void onClick(View view) {
ToastMessage.toastWarn("瞅你咋地", true);
}
})
.show();
看到这个结构,是不是想起了这个
Toast.makeText(UseSnackBarActivity.this,"测试的toast",Toast.LENGTH_SHORT)
.show();
没错,几乎一样的结构,不得不说snackBar可谓是把取其精华,去其槽粕理解的很透彻,这样也方便我们的学习啦,我们依次来说说这些属性的作用:
第一个参数是一个view,以这个view作为父布局,从而控制snackBar显示的位置,土豆这里是以我的toorBar控件作父布局的
第二个参数自然是snackBar要显示的内容啦
第三个属性是snackBar显示的时间,分别有LENGTH_SHORT,LENGTH_LONG和LENGTH_INDEFINITE,前两个属性和toast一致,后一个是表示一直在这页面显示的
第四个是setAction,这就是snackBar的交互特性啦,通过这个可以设置当用户点击后的事件,onClick中的 ToastMessage.toastWarn("瞅你咋地", true);则是土豆自己的工具类啦,有兴趣的可以看看土豆前面写的第二章哦
可以看到,使用snackBar也是超级方便的,但是有个问题,系统默认的snackBar是黑色的样式,这样很容易与我们的APP样式冲突,因为,我们需要自己来封装一下snackBar啦
在实际项目中,我们的activity基本都是直接继承我们的基类BaseActivity的,因此,将snackBar封装在BaseActivity中,方便又实在。至于如何封装BaseActivity,我上一篇有说过一点,但是对我们本章影响不大,此处不提,文章最后也会把我目前为止封装的BaseActivity贴出来,仅供参考,下面是关于snackBar部分的代码
/**
* 展示snackBar
*
* @param view view
* @param msg 消息
* @param isDismiss 是否自动消失
* @param action 事件名
* @param iSnackBarClickEvent 事件处理接口
*/
protected void showSnackBar(@NonNull View view, @NonNull String msg, boolean isDismiss, String action, final ISnackBarClickEvent iSnackBarClickEvent) {
//snackBar默认显示时间为LENGTH_LONG
int duringTime = Snackbar.LENGTH_LONG;
if (isDismiss) {
duringTime = Snackbar.LENGTH_LONG;
} else {
duringTime = Snackbar.LENGTH_INDEFINITE;
}
Snackbar snackbar;
snackbar = Snackbar.make(view, msg, duringTime)
.setAction(action, new View.OnClickListener() {
@Override
public void onClick(View view) {
//以接口方式发送出去,便于使用者处理自己的业务逻辑
iSnackBarClickEvent.clickEvent();
}
});
//设置snackBar和toorBar颜色一致
snackbar.getView().setBackgroundColor(getResources().getColor(R.color.toolbar_color));
//设置action文字的颜色
snackbar.setActionTextColor(getResources().getColor(R.color.normal_white));
//设置snackBar图标 这里是获取到snackBar的textView 然后给textView增加左边图标的方式来实现的
View snackBarView = snackbar.getView();
TextView textView = (TextView) snackBarView.findViewById(R.id.snackbar_text);
Drawable drawable = getResources().getDrawable(R.drawable.icon_vector_notification);//图片自己选择
drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
textView.setCompoundDrawables(drawable, null, null, null);
//增加文字和图标的距离
textView.setCompoundDrawablePadding(20);
//展示snackBar
snackbar.show();
}
/**
* snackBar的action事件
*/
public interface ISnackBarClickEvent {
void clickEvent();
}
注释也写的很详细了,我就不再每行代码进行解释,但是值得注意的是,我们给snackbar开头增加了一个图标,采用的是给textView增加内部图标一样。另外,我们还以内部接口的形式将snackBar的action事件派发出去,这样,任何一个activity都可以通过接口实现各自的业务逻辑了,接下来给出本章案例的代码
布局文件代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.administrator.potato.activity.UseSnackBarActivity">
<include layout="@layout/app_toolbar"/>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout"
android:layout_marginTop="36dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/buttonNormal"
android:text="使用系统snackBar"
android:layout_width="match_parent"
android:layout_height="50dp" />
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinatorCustom"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/buttonCustom"
android:text="使用封装的snackbar"
android:layout_width="match_parent"
android:layout_height="50dp" />
</android.support.design.widget.CoordinatorLayout>
</LinearLayout>
相应的activity代码:
package com.example.administrator.potato.activity;
import android.os.Bundle;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.Toolbar;
import android.view.View;
import com.example.administrator.potato.R;
import com.example.administrator.potato.utils.ToastMessage;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class UseSnackBarActivity extends BaseActivity {
@Bind(R.id.toolbar)
Toolbar toolbar;
@Bind(R.id.coordinatorLayout)
CoordinatorLayout coordinatorLayout;
@Bind(R.id.coordinatorCustom)
CoordinatorLayout coordinatorCustom;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_use_snack_bar);
ButterKnife.bind(this);
initView();
initData();
}
@Override
protected void initView() {
initToolBar(toolbar, "使用SnackBar并封装", true, new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
}
@Override
protected void initData() {
}
@OnClick(R.id.buttonNormal)
public void onViewClicked() {
Snackbar.make(toolbar, "这是系统的snackBar", Snackbar.LENGTH_INDEFINITE)
.setAction("你瞅啥", new View.OnClickListener() {
@Override
public void onClick(View view) {
ToastMessage.toastWarn("瞅你咋地", true);
}
})
.show();
}
@OnClick(R.id.buttonCustom)
public void onButtonCustomClicked() {
showSnackBar(coordinatorCustom, "这是我们自己封装的snackBar", false, "再瞅一个试试", new ISnackBarClickEvent() {
@Override
public void clickEvent() {
ToastMessage.toastWarn("试试就试试", true);
}
});
}
}
最后是BaseActivity代码,由于我平时将所有的案例都写在一个项目中,所以你会看到很多与snackBar无关的代码(才不是为了凑字数呢),不过没关系,这些东西我以后也会讲的,封装BaseActivity的作用在于方便日常开发,所以大家平时也可以将自己觉得有用的代码封装进去,避免写重复代码,写程序,如果不是为了偷懒,那将毫无意义,嘿嘿嘿
package com.example.administrator.potato.activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.MenuRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AlertDialog;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import com.example.administrator.potato.application.MyApplication;
import com.example.administrator.potato.interfaces.ConfirmDialogInterface;
import com.example.administrator.potato.R;
import com.lzy.okgo.OkGo;
public abstract class BaseActivity extends AppCompatActivity {
//上下文对象
protected Context mContext;
//耗时操作的进度提示
private ProgressDialog progressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//获取上下文对象
mContext = this;
initStateBar();
initProgressDialog();
}
@Override
protected void onDestroy() {
//统一关闭OK HTTP请求
OkGo.getInstance().cancelTag(this);
super.onDestroy();
}
//初始化ProgressDialog
private void initProgressDialog() {
progressDialog = new ProgressDialog(mContext);
//无标题
progressDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
//设置手指点击dialog不消失
progressDialog.setCanceledOnTouchOutside(false);
//设置dialog样式
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
}
/**
* 设置状态栏的颜色与toolbar一致 只有安卓5.0以上才能用
*/
private void initStateBar() {
Window window = getWindow();
//设置状态栏为透明
//5.0以上使用原生方法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//设置状态栏的颜色与toolBar的颜色一致
window.setStatusBarColor(getResources().getColor(R.color.toolbar_color));
}
}
/**
* 初始视图
*/
protected abstract void initView();
/**
* 初始数据
*/
protected abstract void initData();
/**
* 显示确认框形式的dialog
*
* @param title dialog的标题
* @param msg dialog的消息
* @param confirmDialogInterface 监听dialog确认键以及取消键的点击事件
*/
protected void showConfirmDialog(@Nullable String title, @Nullable String msg, @NonNull final ConfirmDialogInterface confirmDialogInterface) {
final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
//加载布局
View view = View.inflate(mContext, R.layout.dialog_confirm, null);
//获取组件实例
TextView textTitle = view.findViewById(R.id.textTitle);
TextView textContent = view.findViewById(R.id.textContent);
TextView textConfirm = view.findViewById(R.id.textConfirm);
TextView textCancel = view.findViewById(R.id.textCancel);
//设置标题
textTitle.setText(title);
//设置消息内容
textContent.setText(msg);
//设置需要显示的view
builder.setView(view);
//赋值给其父类以获取dismiss方法
final AlertDialog alertDialog = builder.create();
//显示dialog
alertDialog.show();
//设置确定按钮内容
textConfirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//确认键业务逻辑处理接口
confirmDialogInterface.onConfirmClickListener();
//业务逻辑处理完毕使dialog消失
alertDialog.dismiss();
}
});
//设置取消按钮内容
textCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//取消键业务逻辑处理接口
confirmDialogInterface.onCancelClickListener();
//业务逻辑处理完毕使dialog消失
alertDialog.dismiss();
}
});
}
protected void showProgressDialog(@NonNull String msg) {
if (progressDialog != null && !progressDialog.isShowing()) {
progressDialog.setMessage(msg);
progressDialog.show();
}
}
protected void closeProgressDialog() {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
/**
* 初始化toolBar 完整版 左右两边都需要设置
*
* @param toolbar toolbar
* @param title toolbar标题
* @param isShowLeft 是否显示左边图标
* @param navigationOnClickListener 左边图标监听事件
* @param isShowRight 是否显示右边menu
* @param menuId menu的id
* @param onMenuItemClickListener menu的子项监听事件
*/
protected void initToolBar(android.support.v7.widget.Toolbar toolbar, @NonNull String title, Boolean isShowLeft, View.OnClickListener navigationOnClickListener, Boolean isShowRight
, @MenuRes Integer menuId, android.support.v7.widget.Toolbar.OnMenuItemClickListener onMenuItemClickListener) {
//使用toolbar替代actionBar
//setSupportActionBar(toolbar); 这行代码删除后 toolbar.inflateMenu才能生效
toolbar.setTitle(title);
//设置左边icon
if (isShowLeft) {
toolbar.setNavigationIcon(R.drawable.icon_vector_back);
if (navigationOnClickListener != null) {
toolbar.setNavigationOnClickListener(navigationOnClickListener);
}
}
//设置右边menu
if (isShowRight) {
if (onMenuItemClickListener != null) {
toolbar.setOnMenuItemClickListener(onMenuItemClickListener);
}
if (menuId != null) {
//如果要使用toolbar.inflateMenu 则不能使用setSupportActionBar
toolbar.inflateMenu(menuId);
} else {
//不传菜单文件时使用默认的menu
toolbar.inflateMenu(R.menu.app_toolbar_menu);
}
}
}
/**
* 初始toolbar 简略版 因为大多数的活动是不需要设置右边的 所以可以简化操作
*
* @param toolbar toolBar
* @param title toolBar的标题
* @param isShowLeft 是否显示左边图标
* @param navigationOnClickListener 左边图标的点击事件
*/
protected void initToolBar(android.support.v7.widget.Toolbar toolbar, @NonNull String title, Boolean isShowLeft, View.OnClickListener navigationOnClickListener) {
//使用toolbar替代actionBar
setSupportActionBar(toolbar);
toolbar.setTitle(title);
//设置左边icon
if (isShowLeft) {
toolbar.setNavigationIcon(R.drawable.icon_vector_back);
if (navigationOnClickListener != null) {
toolbar.setNavigationOnClickListener(navigationOnClickListener);
}
}
}
/**
* 活动跳转
*
* @param clazz 要跳转的活动
*/
protected void gotoActivity(Class<?> clazz) {
Intent intent = new Intent(MyApplication.getContext(), clazz);
startActivity(intent);
}
/**
* 活动跳转
*
* @param clazz 目标跳转活动
* @param bundle 参数
*/
protected void gotoActivity(Class<?> clazz, Bundle bundle) {
Intent intent = new Intent(MyApplication.getContext(), clazz);
if (bundle != null) {
intent.putExtras(bundle);
}
startActivity(intent);
}
/**
* 活动跳转
*
* @param clazz 目标跳转活动
* @param bundle 参数
* @param action action
*/
protected void gotoActivity(Class<?> clazz, Bundle bundle, String action) {
Intent intent = new Intent(MyApplication.getContext(), clazz);
if (bundle != null) {
intent.putExtras(bundle);
}
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
}
startActivity(intent);
}
/**
* 展示snackBar
*
* @param view view
* @param msg 消息
* @param isDismiss 是否自动消失
* @param action 事件名
* @param iSnackBarClickEvent 事件处理接口
*/
protected void showSnackBar(@NonNull View view, @NonNull String msg, boolean isDismiss, String action, final ISnackBarClickEvent iSnackBarClickEvent) {
//snackBar默认显示时间为LENGTH_LONG
int duringTime = Snackbar.LENGTH_LONG;
if (isDismiss) {
duringTime = Snackbar.LENGTH_LONG;
} else {
duringTime = Snackbar.LENGTH_INDEFINITE;
}
Snackbar snackbar;
snackbar = Snackbar.make(view, msg, duringTime)
.setAction(action, new View.OnClickListener() {
@Override
public void onClick(View view) {
//以接口方式发送出去,便于使用者处理自己的业务逻辑
iSnackBarClickEvent.clickEvent();
}
});
//设置snackBar和titleBar颜色一致
snackbar.getView().setBackgroundColor(getResources().getColor(R.color.toolbar_color));
//设置action文字的颜色
snackbar.setActionTextColor(getResources().getColor(R.color.normal_white));
//设置snackBar图标 这里是获取到snackBar的textView 然后给textView增加左边图标的方式来实现的
View snackBarView = snackbar.getView();
TextView textView = (TextView) snackBarView.findViewById(R.id.snackbar_text);
Drawable drawable = getResources().getDrawable(R.drawable.icon_vector_notification);//图片自己选择
drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
textView.setCompoundDrawables(drawable, null, null, null);
//增加文字和图标的距离
textView.setCompoundDrawablePadding(20);
//展示snackBar
snackbar.show();
}
/**
* snackBar的action事件
*/
public interface ISnackBarClickEvent {
void clickEvent();
}
}
那么本章内容也讲得差不多了,接下来进行一下总结
一、有喜新厌旧的同学就会问了,snackBar这么方便,那么是不是可以抛弃Toast了呢,其实不然,所谓存在即合理,虽然Toast有一些缺点,但是它依旧可以传达一些不那么重要的消息,两者配合使用,这样可以使程序与用户进行交互时,方式变得多样化,毕竟天天大鱼大肉,也会腻的撒
二、刚刚忘讲了,snackBar还有一个特性是可以滑动删除,相信大家在我的布局文件中看到了这个
<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout"
android:layout_marginTop="36dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/buttonNormal"
android:text="使用系统snackBar"
android:layout_width="match_parent"
android:layout_height="50dp" />
</android.support.design.widget.CoordinatorLayout>
CoordinatorLayout的作用是什么呢,我只能告诉你的是,snackBar+CoordinatorLayout=可以滑动删除的snackBar,这个暂时不讲
3、今天发现平时后排睡觉的同学竟然全部跑光了,估计以为土豆要饭去了
4、下一章讲讲进程通信神器,EventBus吧,下次见,白白~
网友评论