|
@@ -0,0 +1,839 @@
|
|
|
+/*
|
|
|
+ * Copyright (c) 2016, Shanghai YUEWEN Information Technology Co., Ltd.
|
|
|
+ * All rights reserved.
|
|
|
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
|
+ *
|
|
|
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
|
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
|
+ * Neither the name of Shanghai YUEWEN Information Technology Co., Ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
|
+ *
|
|
|
+ * THIS SOFTWARE IS PROVIDED BY SHANGHAI YUEWEN INFORMATION TECHNOLOGY CO., LTD. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
+ *
|
|
|
+ */
|
|
|
+package cn.yyxx.commsdk.impl.merge.platform.hnqj.floating;
|
|
|
+
|
|
|
+import android.animation.Animator;
|
|
|
+import android.animation.ValueAnimator;
|
|
|
+import android.app.Activity;
|
|
|
+import android.content.Context;
|
|
|
+import android.content.DialogInterface;
|
|
|
+import android.content.SharedPreferences;
|
|
|
+import android.graphics.Bitmap;
|
|
|
+import android.graphics.PixelFormat;
|
|
|
+import android.graphics.drawable.Drawable;
|
|
|
+import android.os.Build;
|
|
|
+import android.os.CountDownTimer;
|
|
|
+import android.os.Handler;
|
|
|
+import android.os.Looper;
|
|
|
+import android.util.DisplayMetrics;
|
|
|
+import android.util.TypedValue;
|
|
|
+import android.view.Gravity;
|
|
|
+import android.view.LayoutInflater;
|
|
|
+import android.view.MotionEvent;
|
|
|
+import android.view.View;
|
|
|
+import android.view.View.OnTouchListener;
|
|
|
+import android.view.WindowManager;
|
|
|
+import android.view.animation.Interpolator;
|
|
|
+import android.view.animation.LinearInterpolator;
|
|
|
+import android.widget.LinearLayout;
|
|
|
+
|
|
|
+import cn.yyxx.commsdk.core.utils.ResourceUtil;
|
|
|
+import cn.yyxx.commsdk.impl.merge.platform.hnqj.dialog.CustomExitGameDialog;
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Created by wengyiming on 2017/7/20.
|
|
|
+ */
|
|
|
+public class FloatLogoMenu {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录 logo 停放的位置,以备下次恢复
|
|
|
+ */
|
|
|
+ private static final String LOCATION_X = "hintLocation";
|
|
|
+ private static final String LOCATION_Y = "locationY";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 悬浮球 坐落 左 右 标记
|
|
|
+ */
|
|
|
+ public static final int LEFT = 0;
|
|
|
+ public static final int RIGHT = 1;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录系统状态栏的高度
|
|
|
+ */
|
|
|
+ private int mStatusBarHeight;
|
|
|
+ /**
|
|
|
+ * 记录当前手指位置在屏幕上的横坐标值
|
|
|
+ */
|
|
|
+ private float mXInScreen;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录当前手指位置在屏幕上的纵坐标值
|
|
|
+ */
|
|
|
+ private float mYInScreen;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录手指按下时在屏幕上的横坐标的值
|
|
|
+ */
|
|
|
+ private float mXDownInScreen;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录手指按下时在屏幕上的纵坐标的值
|
|
|
+ */
|
|
|
+ private float mYDownInScreen;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录手指按下时在小悬浮窗的View上的横坐标的值
|
|
|
+ */
|
|
|
+ private float mXInView;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录手指按下时在小悬浮窗的View上的纵坐标的值
|
|
|
+ */
|
|
|
+ private float mYinview;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录屏幕的宽度
|
|
|
+ */
|
|
|
+ private int mScreenWidth,screenHeigth;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 来自 activity 的 wManager
|
|
|
+ */
|
|
|
+ private WindowManager wManager;
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 为 wManager 设置 LayoutParams
|
|
|
+ */
|
|
|
+ private WindowManager.LayoutParams wmParams,wmParams2;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 带透明度动画、旋转、放大的悬浮球
|
|
|
+ */
|
|
|
+ private DotImageView mFloatLogo;
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用于 定时 隐藏 logo的定时器
|
|
|
+ */
|
|
|
+ private CountDownTimer mHideTimer;
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * float menu的高度
|
|
|
+ */
|
|
|
+ private Handler mHandler = new Handler(Looper.getMainLooper());
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 悬浮窗左右移动到默认位置 动画的 加速器
|
|
|
+ */
|
|
|
+ private Interpolator mLinearInterpolator = new LinearInterpolator();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 标记是否拖动中
|
|
|
+ */
|
|
|
+ private boolean isDraging = false;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用于恢复悬浮球的location的属性动画值
|
|
|
+ */
|
|
|
+ private int mResetLocationValue;
|
|
|
+
|
|
|
+ private LinearLayout ll_hide;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ private boolean isShowing = false;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 手指离开屏幕后 用于恢复 悬浮球的 logo 的左右位置
|
|
|
+ */
|
|
|
+ private Runnable updatePositionRunnable = new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ isDraging = true;
|
|
|
+ checkPosition();
|
|
|
+
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 这个事件用于处理移动、自定义点击或者其它事情,return true可以保证onclick事件失效
|
|
|
+ */
|
|
|
+ private OnTouchListener touchListener = new OnTouchListener() {
|
|
|
+ @Override
|
|
|
+ public boolean onTouch(View v, MotionEvent event) {
|
|
|
+ switch (event.getAction()) {
|
|
|
+ case MotionEvent.ACTION_DOWN:
|
|
|
+ floatEventDown(event);
|
|
|
+ break;
|
|
|
+ case MotionEvent.ACTION_MOVE:
|
|
|
+ floatEventMove(event);
|
|
|
+ break;
|
|
|
+ case MotionEvent.ACTION_UP:
|
|
|
+ floatEventUp();
|
|
|
+ case MotionEvent.ACTION_CANCEL:
|
|
|
+ floatEventCancel();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ private View createHideView(Context context){
|
|
|
+ hideView = LayoutInflater.from(context).inflate(ResourceUtil.getLayoutId(context,
|
|
|
+ "qj_hide_float_view"), null);
|
|
|
+ ll_hide = hideView.findViewById(ResourceUtil.getId(context,
|
|
|
+ "ll_hide"));
|
|
|
+ return hideView;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * R.drawable.game_logo
|
|
|
+ *
|
|
|
+ * @param floatItems
|
|
|
+ */
|
|
|
+ private Bitmap mLogoRes;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用于显示在 mActivity 上的 mActivity
|
|
|
+ */
|
|
|
+ private Context mActivity;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 停靠默认位置
|
|
|
+ */
|
|
|
+ private int mDefaultLocation = RIGHT;
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 悬浮窗 坐落 位置
|
|
|
+ */
|
|
|
+ private int mHintLocation = mDefaultLocation;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ private ValueAnimator valueAnimator;
|
|
|
+
|
|
|
+
|
|
|
+ private View hideView;
|
|
|
+
|
|
|
+ private OnFloatClickListener mListener;
|
|
|
+
|
|
|
+ private FloatLogoMenu(Builder builder) {
|
|
|
+
|
|
|
+
|
|
|
+ mLogoRes = builder.mLogoRes;
|
|
|
+ mActivity = builder.mActivity;
|
|
|
+
|
|
|
+ mDefaultLocation = builder.mDefaultLocation;
|
|
|
+ mListener = builder.listener;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if (mLogoRes == null) {
|
|
|
+ throw new IllegalArgumentException("No logo found,you can setLogo/showWithLogo to set a FloatLogo ");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ initFloatWindow();
|
|
|
+ initTimer();
|
|
|
+ initFloat();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化悬浮球 window
|
|
|
+ */
|
|
|
+ private void initFloatWindow() {
|
|
|
+ wmParams = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT,
|
|
|
+ WindowManager.LayoutParams.WRAP_CONTENT,
|
|
|
+ WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
|
|
|
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
|
|
+ | WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
|
|
+ PixelFormat.TRANSLUCENT);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if (mActivity instanceof Activity) {
|
|
|
+ Activity activity = (Activity) mActivity;
|
|
|
+ wManager = activity.getWindowManager();
|
|
|
+ //类似dialog,寄托在activity的windows上,activity关闭时需要关闭当前float
|
|
|
+ wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
|
|
|
+
|
|
|
+
|
|
|
+ } else {
|
|
|
+ wManager = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
|
+ if (Build.VERSION.SDK_INT > 23) {
|
|
|
+ //在android7.1以上系统需要使用TYPE_PHONE类型 配合运行时权限
|
|
|
+ wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
|
|
|
+ } else {
|
|
|
+ wmParams.type =WindowManager.LayoutParams.TYPE_TOAST;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ DisplayMetrics dm = new DisplayMetrics();
|
|
|
+ wManager.getDefaultDisplay().getMetrics(dm);
|
|
|
+ mScreenWidth = dm.widthPixels;
|
|
|
+ screenHeigth = dm.heightPixels;
|
|
|
+
|
|
|
+ //判断状态栏是否显示 如果不显示则statusBarHeight为0
|
|
|
+ mStatusBarHeight = dp2Px(0, mActivity);
|
|
|
+
|
|
|
+ wmParams.format = PixelFormat.RGBA_8888;
|
|
|
+ wmParams.gravity = Gravity.LEFT | Gravity.TOP;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
|
|
+ | WindowManager.LayoutParams.FLAG_FULLSCREEN;
|
|
|
+ mHintLocation = getSetting(LOCATION_X, mDefaultLocation);
|
|
|
+ int defaultY = ((screenHeigth - mStatusBarHeight) / 2) / 3;
|
|
|
+ int y = getSetting(LOCATION_Y, defaultY);
|
|
|
+ if (mHintLocation == LEFT) {
|
|
|
+ wmParams.x = 0;
|
|
|
+ } else {
|
|
|
+ wmParams.x = mScreenWidth;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (y != 0 && y != defaultY) {
|
|
|
+ wmParams.y = y;
|
|
|
+ } else {
|
|
|
+ wmParams.y = defaultY;
|
|
|
+ }
|
|
|
+
|
|
|
+ wmParams.alpha = 1;
|
|
|
+ wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
|
|
|
+ wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ wmParams2 = new WindowManager.LayoutParams();
|
|
|
+ wmParams2.format = PixelFormat.RGBA_8888;
|
|
|
+ wmParams2.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
|
|
+ wmParams2.width = WindowManager.LayoutParams.WRAP_CONTENT;
|
|
|
+ wmParams2.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
|
|
+ wmParams2.y=-screenHeigth / 4;
|
|
|
+ wmParams2.gravity = Gravity.CENTER_HORIZONTAL;
|
|
|
+ wmParams2.type = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化悬浮球
|
|
|
+ */
|
|
|
+ private void initFloat() {
|
|
|
+
|
|
|
+ mFloatLogo = new DotImageView(mActivity, mLogoRes);
|
|
|
+ mFloatLogo.setLayoutParams(new WindowManager.LayoutParams(dp2Px(50, mActivity), dp2Px(50, mActivity)));
|
|
|
+ mFloatLogo.setDrawDarkBg(true);
|
|
|
+ mFloatLogo.setVisibility(View.GONE);
|
|
|
+ mFloatLogo.setOnTouchListener(touchListener);//恢复touch事件
|
|
|
+
|
|
|
+
|
|
|
+ try {
|
|
|
+
|
|
|
+ hideView = createHideView(mActivity);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ mHideTimer.start();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化 隐藏悬浮球的定时器
|
|
|
+ */
|
|
|
+ private void initTimer() {
|
|
|
+ mHideTimer = new CountDownTimer(2000, 10) { //悬浮窗超过5秒没有操作的话会自动隐藏
|
|
|
+ @Override
|
|
|
+ public void onTick(long millisUntilFinished) {
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onFinish() {
|
|
|
+
|
|
|
+ if (!isDraging) {
|
|
|
+ if (mHintLocation == LEFT) {
|
|
|
+ mFloatLogo.setStatus(DotImageView.HIDE_LEFT);
|
|
|
+ mFloatLogo.setDrawDarkBg(true);
|
|
|
+ } else {
|
|
|
+ mFloatLogo.setStatus(DotImageView.HIDE_RIGHT);
|
|
|
+ mFloatLogo.setDrawDarkBg(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 悬浮窗touch事件的 down 事件
|
|
|
+ */
|
|
|
+ private void floatEventDown(MotionEvent event) {
|
|
|
+ isDraging = false;
|
|
|
+ mHideTimer.cancel();
|
|
|
+ if (mFloatLogo.getStatus() != DotImageView.NORMAL) {
|
|
|
+ mFloatLogo.setStatus(DotImageView.NORMAL);
|
|
|
+ }
|
|
|
+ if (!mFloatLogo.mDrawDarkBg) {
|
|
|
+ mFloatLogo.setDrawDarkBg(true);
|
|
|
+ }
|
|
|
+ if (mFloatLogo.getStatus() != DotImageView.NORMAL) {
|
|
|
+ mFloatLogo.setStatus(DotImageView.NORMAL);
|
|
|
+ }
|
|
|
+ mXInView = event.getX();
|
|
|
+ mYinview = event.getY();
|
|
|
+ mXDownInScreen = event.getRawX();
|
|
|
+ mYDownInScreen = event.getRawY();
|
|
|
+ mXInScreen = event.getRawX();
|
|
|
+ mYInScreen = event.getRawY();
|
|
|
+
|
|
|
+ hideView.setVisibility(View.VISIBLE);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 悬浮窗touch事件的 move 事件
|
|
|
+ */
|
|
|
+ private void floatEventMove(MotionEvent event) {
|
|
|
+ mXInScreen = event.getRawX();
|
|
|
+ mYInScreen = event.getRawY();
|
|
|
+
|
|
|
+
|
|
|
+ //连续移动的距离超过3则更新一次视图位置
|
|
|
+ if (Math.abs(mXInScreen - mXDownInScreen) > mFloatLogo.getWidth() / 4 || Math.abs(mYInScreen - mYDownInScreen) > mFloatLogo.getWidth() / 4) {
|
|
|
+ wmParams.x = (int) (mXInScreen - mXInView);
|
|
|
+ wmParams.y = (int) (mYInScreen - mYinview) - mFloatLogo.getHeight() / 2;
|
|
|
+ updateViewPosition(); // 手指移动的时候更新小悬浮窗的位置
|
|
|
+ double a = mScreenWidth / 2;
|
|
|
+ float offset = (float) ((a - (Math.abs(wmParams.x - a))) / a);
|
|
|
+ mFloatLogo.setDraging(isDraging, offset, false);
|
|
|
+ } else {
|
|
|
+ isDraging = false;
|
|
|
+ mFloatLogo.setDraging(false, 0, true);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ float x = wmParams.x;
|
|
|
+ float y = wmParams.y;
|
|
|
+ float XMinPoint = mScreenWidth / 2 - hideView.getWidth();
|
|
|
+ float XMaxPoint = mScreenWidth / 2 + hideView.getWidth();
|
|
|
+ float YMinPoint = screenHeigth / 4 - hideView.getHeight() - mStatusBarHeight;
|
|
|
+
|
|
|
+ float YMaxPoint = screenHeigth / 4 - mStatusBarHeight;
|
|
|
+
|
|
|
+ if (x >= XMinPoint && x <= XMaxPoint && y >=YMinPoint && y <= YMaxPoint){
|
|
|
+
|
|
|
+ ll_hide.setBackgroundResource(ResourceUtil.getDrawableId(mActivity, "qj_hide_float_view_bg"));
|
|
|
+
|
|
|
+ }else {
|
|
|
+ ll_hide.setBackgroundResource(ResourceUtil.getDrawableId(mActivity, "qj_text_black"));
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ private void floatEventUp() {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ float x = wmParams.x;
|
|
|
+ float y = wmParams.y;
|
|
|
+ float RawX = mScreenWidth / 2 - hideView.getWidth();
|
|
|
+ float RawX1 = mScreenWidth / 2 + hideView.getWidth();
|
|
|
+ float RawY = screenHeigth / 4 - hideView.getHeight() - mStatusBarHeight;
|
|
|
+
|
|
|
+ float RawY1 = screenHeigth / 4 - mStatusBarHeight;
|
|
|
+
|
|
|
+ hideView.setVisibility(View.GONE);
|
|
|
+
|
|
|
+ if (x >= RawX && x <= RawX1 && y >=RawY && y <= RawY1){
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ CustomExitGameDialog.Builder builder = new CustomExitGameDialog.Builder(mActivity);
|
|
|
+ builder.Create().show();
|
|
|
+ builder.setPositiveButton(new DialogInterface.OnClickListener() {
|
|
|
+ @Override
|
|
|
+ public void onClick(DialogInterface dialog, int which) {
|
|
|
+
|
|
|
+ hideFloat();
|
|
|
+
|
|
|
+
|
|
|
+ dialog.dismiss();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ builder.setnegativeButton(new DialogInterface.OnClickListener() {
|
|
|
+ @Override
|
|
|
+ public void onClick(DialogInterface dialog, int which) {
|
|
|
+
|
|
|
+ dialog.dismiss();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ builder.setTitle(mActivity.getString(ResourceUtil.getStringId(mActivity, "qj_close_float_view_tip")));
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 悬浮窗touch事件的 up 事件
|
|
|
+ */
|
|
|
+ private void floatEventCancel() {
|
|
|
+ if (mXInScreen < mScreenWidth / 2) { //在左边
|
|
|
+ mHintLocation = LEFT;
|
|
|
+ } else { //在右边
|
|
|
+ mHintLocation = RIGHT;
|
|
|
+ }
|
|
|
+ if (valueAnimator == null) {
|
|
|
+ valueAnimator = ValueAnimator.ofInt(64);
|
|
|
+ valueAnimator.setInterpolator(mLinearInterpolator);
|
|
|
+ valueAnimator.setDuration(1000);
|
|
|
+ valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
|
|
+ @Override
|
|
|
+ public void onAnimationUpdate(ValueAnimator animation) {
|
|
|
+ mResetLocationValue = (int) animation.getAnimatedValue();
|
|
|
+ mHandler.post(updatePositionRunnable);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ valueAnimator.addListener(new Animator.AnimatorListener() {
|
|
|
+ @Override
|
|
|
+ public void onAnimationStart(Animator animation) {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onAnimationEnd(Animator animation) {
|
|
|
+ if (Math.abs(wmParams.x) < 0) {
|
|
|
+ wmParams.x = 0;
|
|
|
+ } else if (Math.abs(wmParams.x) > mScreenWidth) {
|
|
|
+ wmParams.x = mScreenWidth;
|
|
|
+ }
|
|
|
+ updateViewPosition();
|
|
|
+ isDraging = false;
|
|
|
+ mFloatLogo.setDraging(false, 0, true);
|
|
|
+ mHideTimer.start();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onAnimationCancel(Animator animation) {
|
|
|
+ if (Math.abs(wmParams.x) < 0) {
|
|
|
+ wmParams.x = 0;
|
|
|
+ } else if (Math.abs(wmParams.x) > mScreenWidth) {
|
|
|
+ wmParams.x = mScreenWidth;
|
|
|
+ }
|
|
|
+
|
|
|
+ updateViewPosition();
|
|
|
+ isDraging = false;
|
|
|
+ mFloatLogo.setDraging(false, 0, true);
|
|
|
+ mHideTimer.start();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onAnimationRepeat(Animator animation) {
|
|
|
+
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (!valueAnimator.isRunning()) {
|
|
|
+ valueAnimator.start();
|
|
|
+ }
|
|
|
+
|
|
|
+// //这里需要判断如果如果手指所在位置和logo所在位置在一个宽度内则不移动,
|
|
|
+ if (Math.abs(mXInScreen - mXDownInScreen) > mFloatLogo.getWidth() / 5 || Math.abs(mYInScreen - mYDownInScreen) > mFloatLogo.getHeight() / 5) {
|
|
|
+ isDraging = false;
|
|
|
+
|
|
|
+ }else {
|
|
|
+ if (isDraging)
|
|
|
+ return;
|
|
|
+ if (mListener != null) mListener.onClick();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用于检查并更新悬浮球的位置
|
|
|
+ */
|
|
|
+ private void checkPosition() {
|
|
|
+ if (wmParams.x > 0 && wmParams.x < mScreenWidth) {
|
|
|
+ if (mHintLocation == LEFT) {
|
|
|
+ wmParams.x = wmParams.x - mResetLocationValue;
|
|
|
+ } else {
|
|
|
+ wmParams.x = wmParams.x + mResetLocationValue;
|
|
|
+ }
|
|
|
+ updateViewPosition();
|
|
|
+ double a = mScreenWidth / 2;
|
|
|
+ float offset = (float) ((a - (Math.abs(wmParams.x - a))) / a);
|
|
|
+ mFloatLogo.setDraging(isDraging, offset, true);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (Math.abs(wmParams.x) < 0) {
|
|
|
+ wmParams.x = 0;
|
|
|
+ } else if (Math.abs(wmParams.x) > mScreenWidth) {
|
|
|
+ wmParams.x = mScreenWidth;
|
|
|
+ }
|
|
|
+ if (valueAnimator.isRunning()) {
|
|
|
+ valueAnimator.cancel();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ updateViewPosition();
|
|
|
+ isDraging = false;
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新悬浮窗在屏幕中的位置。
|
|
|
+ */
|
|
|
+ private void updateViewPosition() {
|
|
|
+ isDraging = true;
|
|
|
+ try {
|
|
|
+ wManager.updateViewLayout(mFloatLogo, wmParams);
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void show() {
|
|
|
+
|
|
|
+ if (isShowing) return;;
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (wManager != null && wmParams != null && mFloatLogo != null) {
|
|
|
+
|
|
|
+
|
|
|
+ wManager.addView(hideView, wmParams2);
|
|
|
+ wManager.addView(mFloatLogo, wmParams);
|
|
|
+
|
|
|
+ mFloatLogo.setVisibility(View.VISIBLE);
|
|
|
+ isShowing = true;
|
|
|
+
|
|
|
+ }
|
|
|
+ if (mHideTimer != null) {
|
|
|
+ mHideTimer.start();
|
|
|
+ } else {
|
|
|
+ initTimer();
|
|
|
+ mHideTimer.start();
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 隐藏悬浮窗
|
|
|
+ */
|
|
|
+ public void hideFloat() {
|
|
|
+ if (!isShowing) return;
|
|
|
+ //记录上次的位置logo的停放位置,以备下次恢复
|
|
|
+ saveSetting(LOCATION_X, mHintLocation);
|
|
|
+ saveSetting(LOCATION_Y, wmParams.y);
|
|
|
+ mFloatLogo.clearAnimation();
|
|
|
+ try {
|
|
|
+ mHideTimer.cancel();
|
|
|
+
|
|
|
+ wManager.removeViewImmediate(mFloatLogo);
|
|
|
+ wManager.removeViewImmediate(hideView);
|
|
|
+
|
|
|
+ isDraging = false;
|
|
|
+
|
|
|
+ isShowing = false;
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 移除所有悬浮窗 释放资源
|
|
|
+ */
|
|
|
+ public void destoryFloat() {
|
|
|
+ //记录上次的位置logo的停放位置,以备下次恢复
|
|
|
+ saveSetting(LOCATION_X, mHintLocation);
|
|
|
+ saveSetting(LOCATION_Y, wmParams.y);
|
|
|
+ mFloatLogo.clearAnimation();
|
|
|
+ try {
|
|
|
+ mHideTimer.cancel();
|
|
|
+
|
|
|
+ wManager.removeViewImmediate(mFloatLogo);
|
|
|
+ wManager.removeViewImmediate(hideView);
|
|
|
+
|
|
|
+
|
|
|
+ isDraging = false;
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用于保存悬浮球的位置记录
|
|
|
+ *
|
|
|
+ * @param key String
|
|
|
+ * @param defaultValue int
|
|
|
+ * @return int
|
|
|
+ */
|
|
|
+ private int getSetting(String key, int defaultValue) {
|
|
|
+ try {
|
|
|
+ SharedPreferences sharedata = mActivity.getSharedPreferences("floatLogo", 0);
|
|
|
+ return sharedata.getInt(key, defaultValue);
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return defaultValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用于保存悬浮球的位置记录
|
|
|
+ *
|
|
|
+ * @param key String
|
|
|
+ * @param value int
|
|
|
+ */
|
|
|
+ public void saveSetting(String key, int value) {
|
|
|
+ try {
|
|
|
+ SharedPreferences.Editor sharedata = mActivity.getSharedPreferences("floatLogo", 0).edit();
|
|
|
+ sharedata.putInt(key, value);
|
|
|
+ sharedata.apply();
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static int dp2Px(float dp, Context mContext) {
|
|
|
+ return (int) TypedValue.applyDimension(
|
|
|
+ TypedValue.COMPLEX_UNIT_DIP,
|
|
|
+ dp,
|
|
|
+ mContext.getResources().getDisplayMetrics());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public static final class Builder {
|
|
|
+
|
|
|
+
|
|
|
+ private Bitmap mLogoRes;
|
|
|
+ private int mDefaultLocation;
|
|
|
+
|
|
|
+ private Context mActivity;
|
|
|
+
|
|
|
+ private OnFloatClickListener listener;
|
|
|
+
|
|
|
+ private FloatLogoMenu floatLogoMenu;
|
|
|
+
|
|
|
+ public Builder setBgDrawable(Drawable drawable) {
|
|
|
+
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Builder() {
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public Builder logo(Bitmap val) {
|
|
|
+ mLogoRes = val;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Builder withActivity(Activity val) {
|
|
|
+ mActivity = val;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public Builder defaultLocation(int val) {
|
|
|
+ mDefaultLocation = val;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public Builder setOnFloatClickListener(OnFloatClickListener onClickFloatListener) {
|
|
|
+ listener = onClickFloatListener;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public void show() {
|
|
|
+ if (floatLogoMenu == null){
|
|
|
+ floatLogoMenu = new FloatLogoMenu(this);
|
|
|
+ }
|
|
|
+ floatLogoMenu.show();
|
|
|
+ }
|
|
|
+ public void hide(){
|
|
|
+ if (floatLogoMenu != null){
|
|
|
+
|
|
|
+ floatLogoMenu.hideFloat();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ public void release(){
|
|
|
+ if (floatLogoMenu != null){
|
|
|
+ floatLogoMenu.destoryFloat();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ public interface OnFloatClickListener {
|
|
|
+ void onClick();
|
|
|
+ }
|
|
|
+
|
|
|
+}
|