动手实现自己的Crouton

动手实现自己的Crouton
给个图没图说个jb
这里写图片描述
自己菜逼一个,想找个开源的代码看,没有一个是我能看懂的,干!看见Crouton的源码比较少,尽管代码写的比较好,
可扩展性比较好,但是用不到,我就需要一个显示的功能就可以了。所以仿照它的实现了一个Crouton.
Crouton 和DialogPlus开源的控件的原理都是在Activity上添加一个view,在给这个view设置动画效果,和监听事件。
自己定义一个控件CustomView
在调用Activity addContentView 将view显示在Acitvity上
activity.addContentView(CustoView, params);
设置一个动画效果,弹出CustomView,然后删除CustomView调用CustomView.getParentView.removeView(Crouton)设置一个消失的动画。
这样一个最简单的弹出消失的toast完成了。
你还需要考虑到一点,就是你同时弹出多个toast的情况,Crouton处理的方式,用一个队列存储CroutonView。然后按照先进先出的顺序去显示toast
不bibi了。下面介绍详细的实现方式。
我分析源码喜欢从它调用方式着实,或者查看某个类再几处调用了。一步一步分析。
当然现在分析的不是Crouton,而是GacToast,复制粘贴是我的特长,因为我是菜逼,只能抄和模仿。
调用GacToast 弹出提示框只需要调用下面这一句
GacToast.makeText(this,content,GacToast.ERROR).show();

这里面有两个函数,所以看一下makeText函数,还有一个show函数。
先分析makeText方法:
public static GacToast makeText(Activity activity, CharSequence text, int style) {
return new GacToast(activity, text, style);
}
调用了new GacToast():蛋疼,现在的东西,就是调来调去好麻烦啊,
private GacToast(Activity activity,CharSequence text,int style){
this.activity = activity;
this.viewGroup = null;
this.text = text;
if(style == INFO){
color = holoBlueLight;
}else if(style == ERROR){
color = holoRedLight;
}else{
color = holoGreenLight;
}
}
这个构造函数就是一些给成员变量赋值的作用,并没有实际的感兴趣的东西。
Acitvity 保存当前toast依赖的acitvity
Text 显示的内容
Style 根据不同的类型给toast设置不同的背景颜色。
viewGroup 应该是toast的根布局,其实我自己也不知道,往下看,看不懂的就是猜。
makeText函数就到此结束了。看来主要的东西还是在show方法里面。
下面看一下show方法:
public void show() {
Log.e(“gac”, “show method”);
GacManager.getInstance().add(this);

我擦嘞!就一句话,感觉有些失望。现在又多了两个方法,和一个类:
GacManager getInstance add
先看看getInstance搞什么鬼!
static synchronized GacManager getInstance() {
if (null == INSTANCE) {
INSTANCE = new GacManager();
}

return INSTANCE;
}
好高端的东西,一个单例模式。我tmd到现在都搞不懂单例模式的异步什么同步,不想去思考为什么。费脑子。因为没有尝试过单例模式出错。出了错就知道了。我相信我有一天会栽在单例模式手上的。
看看GacManager初始化干了什么,
private GacManager() {
queue = new LinkedBlockingQueue();
}
一个队列存储GacToast的队列。前面提过,一个activity显示多个toast的时候,采用队列。
至于这个队列LinkedBlockingQueue 链式阻塞队列,我自己都不知道是干嘛的,懒得百度。要不我菜逼呢,不懂也不想懂。用到的时候再看吧。
太累了。反正就是初始化了一个存储toast的队列。
getInstance基本就到头了,下面看看add方法
void add(GacToast crouton) {
queue.add(crouton);
Log.e(“gac”,”add queue”);
//displayCrouton();
displaytost();
}
Add方法嘛!顾名思义就是添加一个东西。把toast添加到队列里去。下面调用了一个displaytoast,好直接,马上就要显示toast了,看看这个方法
到底怎么显示的。跟着代码走不信找不到源头。
private void displaytost(){
首先检查队列是否为空,为空就return,队列没个东西显示个毛啊!!
if (queue.isEmpty()) {
return;
}

Queue.peek取出队列头部的一个元素,仅仅是取出来,不删除的。
GacToast toast = queue.peek();
下面语句判断toast里面的activity,刚才初始化的时候就给activity赋值了,怎么可能为空呢?扯淡呢?
当然了万一为空了,就从队列中去掉这个toast
if (null == toast.getActivity()) {
queue.poll();
}
Log.e(“gac”, “ADD_CROUTON_TO_VIEW”);
Toast isShowing 方法根据ViewGroup 和activity是否为空来返回值 所以第一次调用的时候返回fasle 因为viewgroup为null
if(!toast.isShowing()){
sendMessage(toast, Messages.ADD_CROUTON_TO_VIEW);
}else{
Log.e(“gac”, “DISPLAY_CROUTON”);
sendMessageDelayed(toast, Messages.DISPLAY_CROUTON, calculateCroutonDuration(toast));

我擦看见了两个sendmessage方法,你想到了什么,当然是线程 和handler了。我还没介绍GacManager是什么东西。
GacManager extends Handler
也就是GacManager 通过发送不同的消息,再调用自己内部的handlemessage方法处理不同的结果

private void sendMessage(GacToast toast, final int messageId) {
final Message message = obtainMessage(messageId);
message.obj = toast;
sendMessage(message);
}
看见了 sendMessage 把toast 和一个messageId发过去。

sendMessage(toast, Messages.ADD_CROUTON_TO_VIEW);
sendMessageDelayed(toast, Messages.DISPLAY_CROUTON, calculateCroutonDuration(toast));
光从displaytoast方法里面,我们就发现了两个MessageId 一个AddCrouton_TO_View 和DSIPLAY_CROUTON
这个都是Crouton里的变量,我名字懒得改了,要不别人不知道我直接抄的Crouton.
从这两个MessageID 我们发现一个是添加toastview到acitvity中去,另一个是显示toast的。我瞎猜的别问我为什么。看到这个名字就瞎猜。
我们看一下handlemessage怎么搞的。

public void handleMessage(Message message) {
final GacToast toast = (GacToast) message.obj;
if (null == toast) {
return;
}
switch (message.what) {

case Messages.ADD_CROUTON_TO_VIEW: {
  addToastToView(toast);
  break;

}
case Messages.REMOVE_CROUTON:
  removeCrouton(toast);
  break;
case Messages.DISPLAY_CROUTON:
  displaytost();
  break;

}

}
这个方法里的displytoast方法我们已分析了viewgroup==null 是发送ADD_CROUTON_TO_VIEW消息
Viewgroup !=null 发送REMOVE_CROUTON消息。其实本质上就是执行两个方法。
当然了第一次执行的时候我们发送的是ADD消息:执行addToastView方法。所以我们首先分析这个方法。
private void addToastToView(final GacToast toast){
if (toast.isShowing()) {
return;
}
//toast.getView方法主要是初始toast的view 并且设置背景颜色 文本内容,以及动画效果
final View view = toast.getView();
如果view的父布局为空就给他设置一个布局
if (null == view.getParent()) {
ViewGroup.LayoutParams params = view.getLayoutParams();
if (null == params) {
params =
new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
// display Crouton in ViewGroup if it has been supplied

  Activity activity = toast.getActivity();
  if (null == activity || activity.isFinishing()) {
    return;
  }

//如果你用的actionbar请将我注释的方法打开,这个计算actionbar的高度来设置toast的margintop值
//我用的toolbar actionbar 设置为false 所以我手动给他设置了一个值,我是一个菜逼,我从来不关注代码的扩展性
再牛逼的代码我也能给他改废了。
//toolbar 不需要设置ActionBar 直接指定ToolBar的高度
// handleTranslucentActionBar((ViewGroup.MarginLayoutParams) params, activity);
//handleActionBarOverlay((ViewGroup.MarginLayoutParams) params, activity);
((ViewGroup.MarginLayoutParams) params).topMargin = ActivityUtils.dip2px(toast.getActivity(),80.0f);
这个方法就是我上面提到的将toastview添加到acitvity中去
activity.addContentView(view, params);

}
你认为addToastToView方法到此 结束了你就错了,下面还有一大堆,
view.requestLayout(); // This is needed so the animation can use the measured with/height
关于这个ViewTreeObserver是干嘛的真心不知道,有知道的跟我说一下,我很好奇,因为自定义view经常见到,
ViewTreeObserver observer = view.getViewTreeObserver();
if (null != observer) {
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
@TargetApi(16)
public void onGlobalLayout() {

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
给toast的view设置动画并且启动动画,你可以联想一下toast弹出来的场景了。
view.startAnimation(toast.getInAnimation());
下面这个方法别问我我也不知道,有知道的跟我说一下我很好奇。
announceForAccessibilityCompat(toast.getActivity(), toast.getText());
Toast显示出来后 发送一个remove消息。下面执行remove方法了。
//延迟显示的时间发送删除消息
sendMessageDelayed(toast, Messages.REMOVE_CROUTON,
TIME_DURATION + toast.getInAnimation().getDuration());
}
});
}

protected void removeCrouton(GacToast crouton) {
// If the crouton hasn’t been displayed yet a Crouton.hide() will fail to hide
// it since the DISPLAY message might still be in the queue. Remove all messages
// for this crouton.
将这个toast里面的message删除 貌似是handler里的机制
removeAllMessagesForCrouton(crouton);
下面这两句获取toast的view 和它的父布局。
View croutonView = crouton.getView();
ViewGroup croutonParentView = (ViewGroup) croutonView.getParent();

if (null != croutonParentView) {
//croutonView.setAlpha(0f);
Toast的父布局不为空的话,就开始设置退出的动画,
croutonView.startAnimation(crouton.getOutAnimation());

// Remove the Crouton from the queue.
GacToast removed = queue.poll();

// Remove the crouton from the view's parent.
croutonParentView.removeView(croutonView);这一条语句就是我说的从activity中删除这个toast
if (null != removed) {
  removed.detachActivity();
  removed.detachViewGroup();

}

// Send a message to display the next crouton but delay it by the out
// animation duration to make sure it finishes
//延迟当前toast退出的动作时间继续发送显示时间 

退出之后就要发生显示信息,做人不能太自私万一队列里还有toast 你不发送这个其它toast怎么显示呢。 还有你发送显示toast的消息时候的,
你需要等当前toast退出去吧。
sendMessageDelayed(crouton, Messages.DISPLAY_CROUTON, crouton.getOutAnimation().getDuration());
}
}

不想写了00:41了。大致流程就是这样,剩下的自己看代码吧。程序员坑程序员,程序员勾搭程序员。我越来感觉这个真是个真理了。
源码地址:看评论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值