最近趕項目比較忙,趁週末補點文章。
效果預覽:
開發需求:
- 這個彈窗一般用於新消息展示,需要是全局的,所以採用WindowManager來做,可以顯示在其他應用之上。
- 一般消息彈窗會在幾秒內消息,否則會影響用戶體驗,所以還需要延時關閉。
- 消息彈出後要給用戶選擇的機會,所以用戶可以手動移除彈窗,這裏採用手勢來達到取消顯示的目的。
擼碼:
1.先擼一個類,構造傳入上下文,因爲待會會用。
public class TitleTextWindow {
private Context mContext;
public TitleTextWindow(Context context) {
mContext = context;
}
}
2.抽取幾個必要的方法,然後進行代碼編寫。
/**
* 向外部暴露顯示的方法
*/
public void show(){
}
/**
* 向外部暴露關閉的方法
*/
public void dismiss(){
}
/**
* 視圖創建方法
*/
private void createTitleView(){
}
3.將僞代碼功能實現。
/**
* 向外部暴露顯示的方法
*/
public void show(){
createTitleView();
animShow();
//3S後自動關閉
mHander.sendEmptyMessageDelayed(20, 3000);
}
/**
* 向外部暴露關閉的方法
*/
public void dismiss(){
animDismiss();
}
/**
* 視圖創建方法
*/
private void createTitleView(){
//準備Window要添加的View
linearLayout = new LinearLayout(mContext);
final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
linearLayout.setLayoutParams(layoutParams);
View titleView = View.inflate(mContext, R.layout.header_toast, null);//這裏是你彈窗的UI
// 爲titleView設置Touch事件
linearLayout.setOnTouchListener(this);
linearLayout.addView(titleView);
// 定義WindowManager 並且將View添加到WindowManagar中去
wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams wm_params = new WindowManager.LayoutParams();
wm_params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
//這裏需要注意,因爲不同系統版本策略不一,所以需要根據版本判斷設置type,否則會引起崩潰。
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {//大於android SDK 7.1.1
wm_params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
wm_params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
wm_params.gravity = Gravity.TOP;
wm_params.x = 0;
wm_params.y = 0;
wm_params.format = -3; // 會影響Toast中的佈局消失的時候父控件和子控件消失的時機不一致,比如設置爲-1之後就會不同步
wm_params.alpha = 1f;
linearLayout.measure(0, 0);
wm_params.height = linearLayout.getMeasuredHeight();
wm.addView(linearLayout, wm_params);
}
4.處理手勢,讓用戶可以手指上撥,劃出屏幕。
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) event.getRawY();
if (moveY - downY < 0) {//如果是向上滑動
linearLayout.setTranslationY(moveY - downY);
}
break;
case MotionEvent.ACTION_UP:
//達到一定比例後,鬆開手指將關閉彈窗
if (Math.abs(linearLayout.getTranslationY()) > linearLayout.getMeasuredHeight() / 1.5) {
Log.e("TAG", "回彈");
animDismiss();
} else {
linearLayout.setTranslationY(0);
}
break;
default:
break;
}
return true;
}
5.調用。
TitleTextWindow textWindow=new TitleTextWindow(this);
textWindow.show();
6.完整代碼:
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.os.Build;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
import view.peakchao.view.R;
/**
* Created by Chao on 2018-09-08.
*/
public class TitleTextWindow implements View.OnTouchListener {
private Context mContext;
private WindowManager wm;
private LinearLayout linearLayout;
private int downY;
public TitleTextWindow(Context context) {
mContext = context;
}
private android.os.Handler mHander = new android.os.Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
animDismiss();
}
};
/**
* 動畫,從頂部彈出
*/
private void animShow(){
//使用動畫從頂部彈出
ObjectAnimator animator = ObjectAnimator.ofFloat(linearLayout, "translationY", -linearLayout.getMeasuredHeight(), 0);
animator.setDuration(600);
animator.start();
}
/**
* 動畫,從頂部收回
*/
private void animDismiss(){
if (linearLayout == null || linearLayout.getParent() == null) {
return;
}
ObjectAnimator animator = ObjectAnimator.ofFloat(linearLayout, "translationY", linearLayout.getTranslationY(), -linearLayout.getMeasuredHeight());
animator.setDuration(600);
animator.start();
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
//移除HeaderToast (一定要在動畫結束的時候移除,不然下次進來的時候由於wm裏邊已經有控件了,所以會導致卡死)
if (null != linearLayout && null != linearLayout.getParent()) {
wm.removeView(linearLayout);
}
}
@Override
public void onAnimationRepeat(Animator animation) {
super.onAnimationRepeat(animation);
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
}
@Override
public void onAnimationPause(Animator animation) {
super.onAnimationPause(animation);
}
@Override
public void onAnimationResume(Animator animation) {
super.onAnimationResume(animation);
}
});
}
/**
* 向外部暴露顯示的方法
*/
public void show(){
createTitleView();
animShow();
//3S後自動關閉
mHander.sendEmptyMessageDelayed(20, 3000);
}
/**
* 向外部暴露關閉的方法
*/
public void dismiss(){
animDismiss();
}
/**
* 視圖創建方法
*/
private void createTitleView(){
//準備Window要添加的View
linearLayout = new LinearLayout(mContext);
final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
linearLayout.setLayoutParams(layoutParams);
View titleView = View.inflate(mContext, R.layout.header_toast, null);//這裏是你彈窗的UI
// 爲titleView設置Touch事件
linearLayout.setOnTouchListener(this);
linearLayout.addView(titleView);
// 定義WindowManager 並且將View添加到WindowManagar中去
wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams wm_params = new WindowManager.LayoutParams();
wm_params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
//這裏需要注意,因爲不同系統版本策略不一,所以需要根據版本判斷設置type,否則會引起崩潰。
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {//大於android SDK 7.1.1
wm_params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
wm_params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
wm_params.gravity = Gravity.TOP;
wm_params.x = 0;
wm_params.y = 0;
wm_params.format = -3; // 會影響Toast中的佈局消失的時候父控件和子控件消失的時機不一致,比如設置爲-1之後就會不同步
wm_params.alpha = 1f;
linearLayout.measure(0, 0);
wm_params.height = linearLayout.getMeasuredHeight();
wm.addView(linearLayout, wm_params);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) event.getRawY();
if (moveY - downY < 0) {//如果是向上滑動
linearLayout.setTranslationY(moveY - downY);
}
break;
case MotionEvent.ACTION_UP:
//達到一定比例後,鬆開手指將關閉彈窗
if (Math.abs(linearLayout.getTranslationY()) > linearLayout.getMeasuredHeight() / 1.5) {
Log.e("TAG", "回彈");
animDismiss();
} else {
linearLayout.setTranslationY(0);
}
break;
default:
break;
}
return true;
}
}