话不多说,先上效果图:
看完了效果,我们来分析一下如何实现,并且实现这个我们能学到什么。
仿照腾讯漫画的效果,实现toast从顶部出现,又从顶部消失,代码逻辑比较简单。只需要用到Android的补间动画。另外需要仿照原生的调用方法,一行代码实现调用,降低开发者学习成本。
主要是强调一个良好的封装和适配,在写这个控件的过程中充分理解Android补间动画,以及自定义View的使用。另外需要仿照原生的调用方法,一行代码实现调用。
首先是布局文件,比较简单,不做详细说明:
ToastLayout:
package com.zhhr.custom;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.zhhr.R;
/**
* Created by 皓然 on 2017/8/3.
*/
public class ToastLayout extends RelativeLayout {
private static final int ANIMATION_TIME = 200;
private TextView mContent;
private View view;
private boolean isShow;
private RelativeLayout mWrapper;
private ImageView mIcon;
private int height;
public boolean isShow() {
return isShow;
}
public ToastLayout(Context context) {
this(context, null);
}
public ToastLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ToastLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
view = LayoutInflater.from(getContext()).inflate(R.layout.layout_toast, null);
addView(view);
mContent = view.findViewById(R.id.tv_content);
mWrapper = view.findViewById(R.id.rl_toast);
mIcon = view.findViewById(R.id.iv_icon);
height = 60;
}
public void setTextColor(int color){
mContent.setTextColor(color);
}
public void setBgColor(int color){
mWrapper.setBackgroundColor(color);
}
public void setIconVisible(boolean isShow){
if(isShow){
mIcon.setVisibility(View.VISIBLE);
}else {
mIcon.setVisibility(View.GONE);
}
}
public void setIcon(int resId){
mIcon.setImageResource(resId);
}
public void setHeight(int height) {
this.height = height;
}
public void setContent(String content){
if(mContent!=null){
mContent.setText(content);
}
}
public void showToast(long time){
AnimationSet animationSet = new AnimationSet(true);
TranslateAnimation trans1 = new TranslateAnimation(0, 0 ,-dip2px(getContext(),height) ,0);
TranslateAnimation trans2 = new TranslateAnimation(0,0 , 0 , -dip2px(getContext(),height));
trans1.setDuration(ANIMATION_TIME);
trans2.setStartOffset(ANIMATION_TIME+time);
trans2.setDuration(ANIMATION_TIME);
animationSet.addAnimation(trans1);
animationSet.addAnimation(trans2);
this.startAnimation(animationSet);
animationSet.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
isShow = true;
ToastLayout.this.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animation animation) {
isShow = false;
ToastLayout.this.setVisibility(View.GONE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
/**
* 将dip或dp值转换为px值,保证尺寸大小不变
* @param context
* @param dipValue
* @return
*/
public static int dip2px(Context context, float dipValue) {
float density = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * density + 0.5f);
}
}
主要在showToast方法里写了两个动画,这里用到了Android的补间动画,详细的说明可以参考博客Android开发——View动画、帧动画和属性动画详解,这里使用的是位移动画TranslationAnimation,分别设置了进入动画trans1和退出动画trans2,再通过动画的持续时间来控制toast的弹出和退出。
ZToast:
package com.zhhr.custom;
import android.app.Activity;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import com.zhhr.R;
/**
* Created by zhhr on 2018/3/27.
*/
public class ZToast {
private Activity mActivity;
private RelativeLayout mToastLayout;
private ToastLayout mToast;
private ViewGroup mView;
private String text;
private long times;
private static ZToast mToastInstance;
/**
* 固定参数
*/
public static final long LENGTH_LONG = 3000;
public static final long LENGTH_SHORT = 1000;
/**
* 静态可设置参数
*/
public static int TextColor;
public static int BgColor;
public static boolean isShowIcon = true;
public static int height = 60;
public static int resId;
/**
* 设置小图标
* @param resId
*/
public static void setResId(int resId) {
ZToast.resId = resId;
}
/**
* 设置高度
* @param height
*/
public static void setHeight(int height) {
ZToast.height = height;
}
/**
* 图标是否显示
* @param isShowIcon
*/
public static void setIsShowIcon(boolean isShowIcon) {
ZToast.isShowIcon = isShowIcon;
}
/**
* 背景色
* @param bgColor
*/
public static void setBgColor(int bgColor) {
BgColor = bgColor;
}
/**
* 文字颜色
* @param textColor
*/
public static void setTextColor(int textColor) {
TextColor = textColor;
}
/**
* 初始化
* @param BgColor 背景颜色
* @param TextColor 文字颜色
* @param isIcon 图标是否显示
* @param resId 图标
* @param height 高度
*/
public static void init(int BgColor, int TextColor, boolean isIcon,int resId,int height){
ZToast.BgColor = BgColor;
ZToast.TextColor = TextColor;
ZToast.isShowIcon = isIcon;
ZToast.height = height;
ZToast.resId = resId;
}
/**
* 构造函数,上下文为activity
* @param mActivity
* @param text
* @param times
*/
public ZToast(Activity mActivity, String text, long times){
this.mActivity = mActivity;
this.text = text;
this.times = times;
}
/**
* 构造函数,上下文为View
* @param mView
* @param text
* @param times
*/
public ZToast(ViewGroup mView, String text, long times){
this.mView = mView;
this.text = text;
this.times = times;
}
/**
* 调用方法,上下文为activity
* @param mActivity
* @param text
* @param times
* @return
*/
public static ZToast makeText(Activity mActivity, String text, long times){
mToastInstance = new ZToast(mActivity,text,times);
return mToastInstance;
}
/**
* 调用方法,上下文为view
* @param mView
* @param text
* @param times
* @return
*/
public static ZToast makeText(ViewGroup mView, String text, long times){
mToastInstance = new ZToast(mView,text,times);
return mToastInstance;
}
/**
* 展示
*/
public void show(){
if(mActivity!=null){
mToastLayout = (RelativeLayout) mActivity.findViewById(R.id.rl_toast);
if(mToastLayout==null){//判断是否已经添加进母VIEW里,没有则添加进去
mToast = new ToastLayout(mActivity);
initToast(mToast);
mActivity.addContentView(mToast,new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ToastLayout.dip2px(mActivity,height)));
}else{//如果有,则直接取出
mToast = (ToastLayout) mToastLayout.getParent();
}
mToast.setContent(text);
mToast.showToast(times);
return;
}else if(mView!=null){
mToastLayout = (RelativeLayout) mView.findViewById(R.id.rl_toast);
if(mToastLayout==null){
mToast = new ToastLayout(mView.getContext());
initToast(mToast);
mView.addView(mToast,new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ToastLayout.dip2px(mView.getContext(),height)));
}else{
mToast = (ToastLayout) mToastLayout.getParent();
}
mToast.setContent(text);
mToast.showToast(times);
}
}
/**
* 设置各个参数
* @param mToast
*/
private void initToast(ToastLayout mToast) {
if(TextColor!=0){
mToast.setTextColor(TextColor);
}
if(BgColor!=0){
mToast.setBgColor(BgColor);
}
if(resId!=0){
mToast.setIcon(resId);
}
mToast.setIconVisible(isShowIcon);
mToast.setHeight(height);
}
private boolean isShowToast(){
if(mToast == null){
return false;
}
return mToast.isShow();
}
/**
* 是否在展示
* @return
*/
public static boolean isShow(){
if(mToastInstance == null){
return false;
}else{
boolean isShow = mToastInstance.isShowToast();
mToastInstance = null;
return isShow;
}
}
}
这个类主要是做了一些基本的封装,我们要基本仿照原生toast的写法,降低开发者的学习成本,所以采用ZToast.makeText(MainActivity.this,"文字",ZToast.LENGTH_SHORT).show(); 这样的方法来进行实现。首先判断上下文是activity还是自定义view,这里提供了两套构造方法。如果是activity,则通过addContentView()的方法把我们刚刚写完的自定义view添加进activity里;如果是view,则通过addview()方法把自定义view添加进去,再调用时候自定义view的showToast()方法来进行动画的播放。
还可以通过init方法和各种set方法来手动设置自定义view的属性。
ZToast.makeText(MainActivity.this,"文字",ZToast.LENGTH_SHORT).show();
ZToast.makeText(getActivity(), "文字",1000).show();
ZToast.makeText((ViewGroup) getParent(),"文字"",1000).show();
在调用makeText方法之前调用init方法来设置参数
ZToast.init(Color.parseColor("#000000"),Color.parseColor("#ff00ff"),true,R.mipmap.item_pasue,90);//参数为 背景色 文字颜色 是否有图标 图标资源 高度
//也可以单独设置参数,如高度
ZToast.setHeight(200);
//最后调用toast方法
ZToast.makeText(MainActivity.this,"点击事件",ZToast.LENGTH_SHORT).show();
ZToast.isShow()
当isShow()为true时,则说明正在显示中,可以用来做双击退出
代码如下:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if((keyCode == KeyEvent.KEYCODE_BACK)){
if(ZToast.isShow()){
return super.onKeyDown(keyCode, event);
}else{
ZToast.makeText(MainActivity.this,"再按一次返回键退出",1000).show();
return false;
}
}else{
return super.onKeyDown(keyCode, event);
}
}
强烈建议搭配无actionbar的主题来使用
在style.xml中:
最好再设置一下statusbar的颜色
在activity中:
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
自定义view的使用已经仿照原生控件的封装方式,极大的简化了我们代码,为项目中今后所有需要用到toast的地方都提供了完整的一套方案。掌握的并不是说代码的技术含量,而是一种封装的思路。
完整的DEMO代码已经提交到GitHub和Jcenter。GITHUB地址:ZToastDemo
安装方法:
compile 'com.zhhr:ztoast:1.0.0'