DotImageView.java
package com.yw.game.floatmenu;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
/**
* Created by wengyiming on 2017/7/21.
*/
/**
* 00%=FF(不透明) 5%=F2 10%=E5 15%=D8 20%=CC 25%=BF 30%=B2 35%=A5 40%=99 45%=8c 50%=7F
* 55%=72 60%=66 65%=59 70%=4c 75%=3F 80%=33 85%=21 90%=19 95%=0c 100%=00(全透明)
*/
public class DotImageView extends View {
private static final String TAG = DotImageView.class.getSimpleName();
public static final int NORMAL = 0;
public static final int HIDE_LEFT = 1;
public static final int HIDE_RIGHT = 2;
private Paint mPaint;
private Paint mPaintBg;
private String dotNum = null;
private float mAlphValue;
private float mRolateValue = 1f;
private boolean inited = false;
private Bitmap mBitmap;
private final int mLogoBackgroundRadius = dip2px(25);
private final int mLogoWhiteRadius = dip2px(20);
private final int mRedPointRadiusWithNum = dip2px(6);
private final int mRedPointRadius = dip2px(3);
private final int mRedPointOffset = dip2px(10);
private boolean isDraging = false;
private float scaleOffset;
private ValueAnimator mDragingValueAnimator;
private LinearInterpolator mLinearInterpolator = new LinearInterpolator();
public boolean mDrawDarkBg = true;
private static final float hideOffset = 0.4f;
private Camera mCamera;
private boolean mDrawNum = false;
private int mStatus = NORMAL;
private int mLastStatus = mStatus;
private Matrix mMatrix;
private boolean mIsResetPosition;
private int mBgColor = 0x99000000;
public void setBgColor(int bgColor) {
mBgColor = bgColor;
}
public void setDrawNum(boolean drawNum) {
this.mDrawNum = drawNum;
}
public void setDrawDarkBg(boolean drawDarkBg) {
mDrawDarkBg = drawDarkBg;
invalidate();
}
public int getStatus() {
return mStatus;
}
public void setStatus(int status) {
this.mStatus = status;
isDraging = false;
if (this.mStatus != NORMAL) {
setDrawNum(mDrawNum);
this.mDrawDarkBg = true;
}
invalidate();
}
public void setBitmap(Bitmap bitmap) {
mBitmap = bitmap;
}
public DotImageView(Context context, Bitmap bitmap) {
super(context);
this.mBitmap = bitmap;
init();
}
public DotImageView(Context context) {
super(context);
init();
}
public DotImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public DotImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(sp2px(10));
mPaint.setStyle(Paint.Style.FILL);
mPaintBg = new Paint();
mPaintBg.setAntiAlias(true);
mPaintBg.setStyle(Paint.Style.FILL);
mPaintBg.setColor(mBgColor);
mCamera = new Camera();
mMatrix = new Matrix();
}
/**
* 这个方法是否有优化空间
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int wh = mLogoBackgroundRadius * 2;
setMeasuredDimension(wh, wh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float centryX = getWidth() / 2;
float centryY = getHeight() / 2;
canvas.save();
mCamera.save();
if (mStatus == NORMAL) {
if (mLastStatus != NORMAL) {
canvas.restore();
mCamera.restore();
}
if (isDraging) {
canvas.scale((scaleOffset + 1f), (scaleOffset + 1f), getWidth() / 2, getHeight() / 2);
if (mIsResetPosition) {
mCamera.save();
mCamera.rotateX(720 * scaleOffset);
mCamera.getMatrix(mMatrix);
mMatrix.preTranslate(-getWidth() / 2, -getHeight() / 2);
mMatrix.postTranslate(getWidth() / 2, getHeight() / 2);
canvas.concat(mMatrix);
mCamera.restore();
} else {
canvas.rotate(60 * mRolateValue, getWidth() / 2, getHeight() / 2);
}
}
} else if (mStatus == HIDE_LEFT) {
canvas.translate(-getWidth() * hideOffset, 0);
canvas.rotate(-45, getWidth() / 2, getHeight() / 2);
} else if (mStatus == HIDE_RIGHT) {
canvas.translate(getWidth() * hideOffset, 0);
canvas.rotate(45, getWidth() / 2, getHeight() / 2);
}
canvas.save();
if (!isDraging) {
if (mDrawDarkBg) {
mPaintBg.setColor(mBgColor);
canvas.drawCircle(centryX, centryY, mLogoBackgroundRadius, mPaintBg);
mPaint.setColor(0x99ffffff);
} else {
mPaint.setColor(0xFFFFFFFF);
}
if (mAlphValue != 0) {
mPaint.setAlpha((int) (mAlphValue * 255));
}
canvas.drawCircle(centryX, centryY, mLogoWhiteRadius, mPaint);
}
canvas.restore();
mPaint.setColor(0xFFFFFFFF);
int left = (int) (centryX - mBitmap.getWidth() / 2);
int top = (int) (centryY - mBitmap.getHeight() / 2);
canvas.drawBitmap(mBitmap, left, top, mPaint);
if (!TextUtils.isEmpty(dotNum)) {
int readPointRadus = (mDrawNum ? mRedPointRadiusWithNum : mRedPointRadius);
mPaint.setColor(Color.RED);
if (mStatus == HIDE_LEFT) {
canvas.drawCircle(centryX + mRedPointOffset, centryY - mRedPointOffset, readPointRadus, mPaint);
if (mDrawNum) {
mPaint.setColor(Color.WHITE);
canvas.drawText(dotNum, centryX + mRedPointOffset - getTextWidth(dotNum, mPaint) / 2, centryY - mRedPointOffset + getTextHeight(dotNum, mPaint) / 2, mPaint);
}
} else if (mStatus == HIDE_RIGHT) {
canvas.drawCircle(centryX - mRedPointOffset, centryY - mRedPointOffset, readPointRadus, mPaint);
if (mDrawNum) {
mPaint.setColor(Color.WHITE);
canvas.drawText(dotNum, centryX - mRedPointOffset - getTextWidth(dotNum, mPaint) / 2, centryY - mRedPointOffset + getTextHeight(dotNum, mPaint) / 2, mPaint);
}
} else {
if (mLastStatus == HIDE_LEFT) {
canvas.drawCircle(centryX + mRedPointOffset, centryY - mRedPointOffset, readPointRadus, mPaint);
if (mDrawNum) {
mPaint.setColor(Color.WHITE);
canvas.drawText(dotNum, centryX + mRedPointOffset - getTextWidth(dotNum, mPaint) / 2, centryY - mRedPointOffset + getTextHeight(dotNum, mPaint) / 2, mPaint);
}
} else if (mLastStatus == HIDE_RIGHT) {
canvas.drawCircle(centryX - mRedPointOffset, centryY - mRedPointOffset, readPointRadus, mPaint);
if (mDrawNum) {
mPaint.setColor(Color.WHITE);
canvas.drawText(dotNum, centryX - mRedPointOffset - getTextWidth(dotNum, mPaint) / 2, centryY - mRedPointOffset + getTextHeight(dotNum, mPaint) / 2, mPaint);
}
}
}
}
mLastStatus = mStatus;
}
public void setDotNum(int num, Animator.AnimatorListener l) {
if (!inited) {
startAnim(num, l);
} else {
refreshDot(num);
}
}
private void refreshDot(int num) {
if (num > 0) {
String dotNumTmp = String.valueOf(num);
if (!TextUtils.equals(dotNum, dotNumTmp)) {
dotNum = dotNumTmp;
invalidate();
}
} else {
dotNum = null;
}
}
public void startAnim(final int num, Animator.AnimatorListener l) {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(1.f, 0.6f, 1f, 0.6f);
valueAnimator.setInterpolator(mLinearInterpolator);
valueAnimator.setDuration(3000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mAlphValue = (float) animation.getAnimatedValue();
invalidate();
}
});
valueAnimator.addListener(l);
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
inited = true;
refreshDot(num);
mAlphValue = 0;
}
@Override
public void onAnimationCancel(Animator animation) {
mAlphValue = 0;
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
valueAnimator.start();
}
public void setDraging(boolean draging, float offset, boolean isResetPosition) {
isDraging = draging;
this.mIsResetPosition = isResetPosition;
if (offset > 0 && offset != this.scaleOffset) {
this.scaleOffset = offset;
}
if (isDraging && mStatus == NORMAL) {
if (mDragingValueAnimator != null) {
if (mDragingValueAnimator.isRunning()) return;
}
mDragingValueAnimator = ValueAnimator.ofFloat(0, 6f, 12f, 0f);
mDragingValueAnimator.setInterpolator(mLinearInterpolator);
mDragingValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mRolateValue = (float) animation.getAnimatedValue();
invalidate();
}
});
mDragingValueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
isDraging = false;
mIsResetPosition = false;
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
mDragingValueAnimator.setDuration(1000);
mDragingValueAnimator.start();
}
}
private int dip2px(float dipValue) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
private int sp2px(float spValue) {
final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
private float getTextHeight(String text, Paint paint) {
Rect rect = new Rect();
paint.getTextBounds(text, 0, text.length(), rect);
return rect.height() / 1.1f;
}
private float getTextWidth(String text, Paint paint) {
return paint.measureText(text);
}
}
FloatItem.java
package com.yw.game.floatmenu;
import android.graphics.Bitmap;
import android.graphics.Color;
/**
* Created by wengyiming on 2017/7/21.
*/
public class FloatItem {
public String title;
public int titleColor = Color.BLACK;
public int bgColor = Color.WHITE;
public Bitmap icon;
public String dotNum = null;
public FloatItem(String title, int titleColor, int bgColor, Bitmap icon, String dotNum) {
this.title = title;
this.titleColor = titleColor;
this.bgColor = bgColor;
this.icon = icon;
this.dotNum = dotNum;
}
public String getDotNum() {
return dotNum;
}
public FloatItem(String title, int titleColor, int bgColor, Bitmap bitmap) {
this.title = title;
this.titleColor = titleColor;
this.bgColor = bgColor;
this.icon = bitmap;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getTitleColor() {
return titleColor;
}
public void setTitleColor(int titleColor) {
this.titleColor = titleColor;
}
public int getBgColor() {
return bgColor;
}
public void setBgColor(int bgColor) {
this.bgColor = bgColor;
}
public Bitmap getIcon() {
return icon;
}
public void setIcon(Bitmap icon) {
this.icon = icon;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) return true;
if (obj instanceof FloatItem) {
FloatItem floatItem = (FloatItem) obj;
return floatItem.title.equals(this.title);
} else {
return false;
}
}
@Override
public int hashCode() {
return title.hashCode();
}
@Override
public String toString() {
return "FloatItem{" +
"title='" + title + '\'' +
", titleColor=" + titleColor +
", bgColor=" + bgColor +
", icon=" + icon +
", dotNum='" + dotNum + '\'' +
'}';
}
}
package com.yw.game.floatmenu;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
import java.util.ArrayList;
import java.util.List;
/**
* 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;
/**
* 来自 activity 的 wManager
*/
private WindowManager wManager;
/**
* 为 wManager 设置 LayoutParams
*/
private WindowManager.LayoutParams wmParams;
/**
* 带透明度动画、旋转、放大的悬浮球
*/
private DotImageView mFloatLogo;
/**
* 用于 定时 隐藏 logo的定时器
*/
private CountDownTimer mHideTimer;
/**
* float menu的高度
*/
private Handler mHandler = new Handler(Looper.getMainLooper());
/**
* 悬浮窗左右移动到默认位置 动画的 加速器
*/
private Interpolator mLinearInterpolator = new LinearInterpolator();
/**
* 用于记录上次菜单打开的时间,判断时间间隔
*/
private static double DOUBLE_CLICK_TIME = 0L;
/**
* 标记是否拖动中
*/
private boolean isDraging = false;
/**
* 用于恢复悬浮球的location的属性动画值
*/
private int mResetLocationValue;
/**
* 手指离开屏幕后 用于恢复 悬浮球的 logo 的左右位置
*/
private Runnable updatePositionRunnable = new Runnable() {
@Override
public void run() {
isDraging = true;
checkPosition();
}
};
/**
* 这个事件不做任何事情、直接return false则 onclick 事件生效
*/
private OnTouchListener mDefaultOnTouchListerner = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
isDraging = false;
return false;
}
};
/**
* 这个事件用于处理移动、自定义点击或者其它事情,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:
case MotionEvent.ACTION_CANCEL:
floatEventUp();
break;
}
return true;
}
};
/**
* 菜单背景颜色
*/
private int mBackMenuColor = 0xffe4e3e1;
/**
* 是否绘制红点数字
*/
private boolean mDrawRedPointNum;
/**
* 是否绘制圆形菜单项,false绘制方形
*/
private boolean mCicleMenuBg;
/**
* R.drawable.yw_game_logo
*
* @param floatItems
*/
private Bitmap mLogoRes;
/**
* 用于显示在 mActivity 上的 mActivity
*/
private Context mActivity;
/**
* 菜单 点击、关闭 监听
*/
private FloatMenuView.OnMenuClickListener mOnMenuClickListener;
/**
* 停靠默认位置
*/
private int mDefaultLocation = RIGHT;
/**
* 悬浮窗 坐落 位置
*/
private int mHintLocation = mDefaultLocation;
/**
* 用于记录菜单项内容
*/
private List mFloatItems = new ArrayList<>();
private LinearLayout rootViewRight;
private LinearLayout rootView;
private ValueAnimator valueAnimator;
private boolean isExpaned = false;
private Drawable mBackground;
private FloatLogoMenu(Builder builder) {
mBackMenuColor = builder.mBackMenuColor;
mDrawRedPointNum = builder.mDrawRedPointNum;
mCicleMenuBg = builder.mCicleMenuBg;
mLogoRes = builder.mLogoRes;
mActivity = builder.mActivity;
mOnMenuClickListener = builder.mOnMenuClickListener;
mDefaultLocation = builder.mDefaultLocation;
mFloatItems = builder.mFloatItems;
mBackground = builder.mDrawable;
if (mLogoRes == null) {
throw new IllegalArgumentException("No logo found,you can setLogo/showWithLogo to set a FloatLogo ");
}
if (mFloatItems.isEmpty()) {
throw new IllegalArgumentException("At least one menu item!");
}
initFloatWindow();
initTimer();
initFloat();
}
public void setFloatItemList(List floatItems) {
this.mFloatItems = floatItems;
caculateDotNum();
}
/**
* 初始化悬浮球 window
*/
private void initFloatWindow() {
wmParams = new WindowManager.LayoutParams();
if (mActivity instanceof Activity) {
Activity activity = (Activity) mActivity;
wManager = activity.getWindowManager();
wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION;
} else {
wManager = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (Build.VERSION.SDK_INT > 23) {
wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
} else {
wmParams.type = WindowManager.LayoutParams.TYPE_TOAST;
}
} else {
wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
}
mScreenWidth = wManager.getDefaultDisplay().getWidth();
int screenHeigth = wManager.getDefaultDisplay().getHeight();
mStatusBarHeight = dp2Px(25, 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;
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;
}
/**
* 初始化悬浮球
*/
private void initFloat() {
genarateLeftLineLayout();
genarateRightLineLayout();
mFloatLogo = new DotImageView(mActivity, mLogoRes);
mFloatLogo.setLayoutParams(new WindowManager.LayoutParams(dp2Px(50, mActivity), dp2Px(50, mActivity)));
mFloatLogo.setDrawNum(mDrawRedPointNum);
mFloatLogo.setBgColor(mBackMenuColor);
mFloatLogo.setDrawDarkBg(true);
caculateDotNum();
floatBtnEvent();
try {
wManager.addView(mFloatLogo, wmParams);
} catch (Exception e) {
e.printStackTrace();
}
}
private void genarateLeftLineLayout() {
DotImageView floatLogo = new DotImageView(mActivity, mLogoRes);
floatLogo.setLayoutParams(new WindowManager.LayoutParams(dp2Px(50, mActivity), dp2Px(50, mActivity)));
floatLogo.setDrawNum(mDrawRedPointNum);
floatLogo.setDrawDarkBg(false);
rootView = new LinearLayout(mActivity);
rootView.setOrientation(LinearLayout.HORIZONTAL);
rootView.setGravity(Gravity.CENTER);
rootView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, dp2Px(50, mActivity)));
rootView.setBackgroundDrawable(mBackground);
FloatMenuView mFloatMenuView = new FloatMenuView.Builder(mActivity)
.setFloatItems(mFloatItems)
.setBackgroundColor(Color.TRANSPARENT)
.setCicleBg(mCicleMenuBg)
.setStatus(FloatMenuView.STATUS_LEFT)
.setMenuBackgroundColor(Color.TRANSPARENT)
.drawNum(mDrawRedPointNum)
.create();
setMenuClickListener(mFloatMenuView);
rootView.addView(floatLogo);
rootView.addView(mFloatMenuView);
floatLogo.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isExpaned) {
try {
wManager.removeViewImmediate(rootView);
wManager.addView(FloatLogoMenu.this.mFloatLogo, wmParams);
} catch (Exception e) {
e.printStackTrace();
}
isExpaned = false;
}
}
});
}
private void genarateRightLineLayout() {
final DotImageView floatLogo = new DotImageView(mActivity, mLogoRes);
floatLogo.setLayoutParams(new WindowManager.LayoutParams(dp2Px(50, mActivity), dp2Px(50, mActivity)));
floatLogo.setDrawNum(mDrawRedPointNum);
floatLogo.setDrawDarkBg(false);
floatLogo.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isExpaned) {
try {
wManager.removeViewImmediate(rootViewRight);
wManager.addView(FloatLogoMenu.this.mFloatLogo, wmParams);
} catch (Exception e) {
e.printStackTrace();
}
isExpaned = false;
}
}
});
rootViewRight = new LinearLayout(mActivity);
rootViewRight.setOrientation(LinearLayout.HORIZONTAL);
rootViewRight.setGravity(Gravity.CENTER);
rootViewRight.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, dp2Px(50, mActivity)));
rootViewRight.setBackgroundDrawable(mBackground);
FloatMenuView mFloatMenuView = new FloatMenuView.Builder(mActivity)
.setFloatItems(mFloatItems)
.setBackgroundColor(Color.TRANSPARENT)
.setCicleBg(mCicleMenuBg)
.setStatus(FloatMenuView.STATUS_RIGHT)
.setMenuBackgroundColor(Color.TRANSPARENT)
.drawNum(mDrawRedPointNum)
.create();
setMenuClickListener(mFloatMenuView);
rootViewRight.addView(mFloatMenuView);
rootViewRight.addView(floatLogo);
}
/**
* 初始化 隐藏悬浮球的定时器
*/
private void initTimer() {
mHideTimer = new CountDownTimer(2000, 10) {
@Override
public void onTick(long millisUntilFinished) {
if (isExpaned) {
mHideTimer.cancel();
}
}
@Override
public void onFinish() {
if (isExpaned) {
mHideTimer.cancel();
return;
}
if (!isDraging) {
if (mHintLocation == LEFT) {
mFloatLogo.setStatus(DotImageView.HIDE_LEFT);
mFloatLogo.setDrawDarkBg(true);
} else {
mFloatLogo.setStatus(DotImageView.HIDE_RIGHT);
mFloatLogo.setDrawDarkBg(true);
}
}
}
};
}
/**
* 用于 拦截 菜单项的 关闭事件,以方便开始 隐藏定时器
*
* @param mFloatMenuView
*/
private void setMenuClickListener(FloatMenuView mFloatMenuView) {
mFloatMenuView.setOnMenuClickListener(new FloatMenuView.OnMenuClickListener() {
@Override
public void onItemClick(int position, String title) {
mOnMenuClickListener.onItemClick(position, title);
}
@Override
public void dismiss() {
mFloatLogo.setDrawDarkBg(true);
mOnMenuClickListener.dismiss();
mHideTimer.start();
}
});
}
/**
* 悬浮窗的点击事件和touch事件的切换
*/
private void floatBtnEvent() {
mFloatLogo.setOnTouchListener(touchListener);
}
/**
* 悬浮窗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();
}
/**
* 悬浮窗touch事件的 move 事件
*/
private void floatEventMove(MotionEvent event) {
mXInScreen = event.getRawX();
mYInScreen = event.getRawY();
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);
}
}
/**
* 悬浮窗touch事件的 up 事件
*/
private void floatEventUp() {
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();
}
if (Math.abs(mXInScreen - mXDownInScreen) > mFloatLogo.getWidth() / 5 || Math.abs(mYInScreen - mYDownInScreen) > mFloatLogo.getHeight() / 5) {
isDraging = false;
} else {
openMenu();
}
}
/**
* 用于检查并更新悬浮球的位置
*/
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 openMenu() {
if (isDraging) return;
if (!isExpaned) {
mFloatLogo.setDrawDarkBg(false);
try {
wManager.removeViewImmediate(mFloatLogo);
if (mHintLocation == RIGHT) {
wManager.addView(rootViewRight, wmParams);
} else {
wManager.addView(rootView, wmParams);
}
} catch (Exception e) {
e.printStackTrace();
}
isExpaned = true;
mHideTimer.cancel();
} else {
mFloatLogo.setDrawDarkBg(true);
if (isExpaned) {
try {
wManager.removeViewImmediate(mHintLocation == LEFT ? rootView : rootViewRight);
wManager.addView(mFloatLogo, wmParams);
} catch (Exception e) {
e.printStackTrace();
}
isExpaned = false;
}
mHideTimer.start();
}
}
/**
* 更新悬浮窗在屏幕中的位置。
*/
private void updateViewPosition() {
isDraging = true;
try {
if (!isExpaned) {
if (wmParams.y - mFloatLogo.getHeight() / 2 <= 0) {
wmParams.y = mStatusBarHeight;
isDraging = true;
}
wManager.updateViewLayout(mFloatLogo, wmParams);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void show() {
try {
if (wManager != null && wmParams != null && mFloatLogo != null) {
wManager.addView(mFloatLogo, wmParams);
}
if (mHideTimer != null) {
mHideTimer.start();
} else {
initTimer();
mHideTimer.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 关闭菜单
*/
public void hide() {
destoryFloat();
}
/**
* 移除所有悬浮窗 释放资源
*/
public void destoryFloat() {
saveSetting(LOCATION_X, mHintLocation);
saveSetting(LOCATION_Y, wmParams.y);
mFloatLogo.clearAnimation();
try {
mHideTimer.cancel();
if (isExpaned) {
wManager.removeViewImmediate(mHintLocation == LEFT ? rootView : rootViewRight);
} else {
wManager.removeViewImmediate(mFloatLogo);
}
isExpaned = false;
isDraging = false;
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 计算总红点数
*/
private void caculateDotNum() {
int dotNum = 0;
for (FloatItem floatItem : mFloatItems) {
if (!TextUtils.isEmpty(floatItem.getDotNum())) {
int num = Integer.parseInt(floatItem.getDotNum());
dotNum = dotNum + num;
}
}
mFloatLogo.setDrawNum(mDrawRedPointNum);
setDotNum(dotNum);
}
/**
* 绘制悬浮球的红点
*
* @param dotNum d
*/
private void setDotNum(int dotNum) {
mFloatLogo.setDotNum(dotNum, new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
if (!isDraging) {
mHideTimer.start();
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
/**
* 用于暴露给外部判断是否包含某个菜单项
*
* @param menuLabel string
* @return boolean
*/
public boolean hasMenu(String menuLabel) {
for (FloatItem menuItem : mFloatItems) {
if (TextUtils.equals(menuItem.getTitle(), menuLabel)) {
return true;
}
}
return false;
}
/**
* 用于保存悬浮球的位置记录
*
* @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 interface OnMenuClickListener {
void onMenuExpended(boolean isExpened);
}
public void setValueAnimator() {
}
public static final class Builder {
private int mBackMenuColor;
private boolean mDrawRedPointNum;
private boolean mCicleMenuBg;
private Bitmap mLogoRes;
private int mDefaultLocation;
private List mFloatItems = new ArrayList<>();
private Context mActivity;
private FloatMenuView.OnMenuClickListener mOnMenuClickListener;
private Drawable mDrawable;
public Builder setBgDrawable(Drawable drawable) {
mDrawable = drawable;
return this;
}
public Builder() {
}
public Builder setFloatItems(List mFloatItems) {
this.mFloatItems = mFloatItems;
return this;
}
public Builder addFloatItem(FloatItem floatItem) {
this.mFloatItems.add(floatItem);
return this;
}
public Builder backMenuColor(int val) {
mBackMenuColor = val;
return this;
}
public Builder drawRedPointNum(boolean val) {
mDrawRedPointNum = val;
return this;
}
public Builder drawCicleMenuBg(boolean val) {
mCicleMenuBg = val;
return this;
}
public Builder logo(Bitmap val) {
mLogoRes = val;
return this;
}
public Builder withActivity(Activity val) {
mActivity = val;
return this;
}
public Builder withContext(Context val) {
mActivity = val;
return this;
}
public Builder setOnMenuItemClickListener(FloatMenuView.OnMenuClickListener val) {
mOnMenuClickListener = val;
return this;
}
public Builder defaultLocation(int val) {
mDefaultLocation = val;
return this;
}
public FloatLogoMenu showWithListener(FloatMenuView.OnMenuClickListener val) {
mOnMenuClickListener = val;
return new FloatLogoMenu(this);
}
public FloatLogoMenu showWithLogo(Bitmap val) {
mLogoRes = val;
return new FloatLogoMenu(this);
}
public FloatLogoMenu show() {
return new FloatLogoMenu(this);
}
}
}
package com.yw.game.floatmenu;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* Created by wengyiming on 2017/7/21.
*/
public class FloatMenuView extends View {
public static final int STATUS_LEFT = 3;
public static final int STATUS_RIGHT = 4;
private int mStatus = STATUS_RIGHT;
private Paint mPaint;
private int mBackgroundColor = 0x00FFFFFF;
private int mMenuBackgroundColor = -1;
private RectF mBgRect;
private int mItemWidth = dip2px(50);
private int mItemHeight = dip2px(50);
private int mItemLeft = 0;
private int mCorner = dip2px(2);
private int mRadius = dip2px(4);
private final int mRedPointRadiuWithNoNum = dip2px(3);
private int mFontSizePointNum = sp2px(10);
private int mFontSizeTitle = sp2px(12);
private float mFirstItemTop;
private boolean mDrawNum = false;
private boolean cicleBg = false;
private List mItemList = new ArrayList<>();
private List mItemRectList = new ArrayList<>();
private OnMenuClickListener mOnMenuClickListener;
private ObjectAnimator mAlphaAnim;
public void setItemList(List itemList) {
mItemList = itemList;
}
public void drawNum(boolean drawNum) {
mDrawNum = drawNum;
}
public void setCicleBg(boolean cicleBg) {
this.cicleBg = cicleBg;
}
public void setMenuBackgroundColor(int mMenuBackgroundColor) {
this.mMenuBackgroundColor = mMenuBackgroundColor;
}
public void setBackgroundColor(int BackgroundColor) {
this.mBackgroundColor = BackgroundColor;
}
public FloatMenuView(Context context) {
super(context);
}
public FloatMenuView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FloatMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public FloatMenuView(Context baseContext, int status) {
super(baseContext);
mStatus = status;
int screenWidth = getResources().getDisplayMetrics().widthPixels;
int screenHeight = getResources().getDisplayMetrics().heightPixels;
mBgRect = new RectF(0, 0, screenWidth, screenHeight);
initView();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(mItemWidth * mItemList.size(), mItemHeight);
}
private void initView( ) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextSize(sp2px(12));
mAlphaAnim = ObjectAnimator.ofFloat(this, "alpha", 1.0f, 0f);
mAlphaAnim.setDuration(50);
mAlphaAnim.addListener(new MyAnimListener() {
@Override
public void onAnimationEnd(Animator animation) {
if (mOnMenuClickListener != null) {
removeView();
mOnMenuClickListener.dismiss();
}
}
});
mFirstItemTop = 0;
if (mStatus == STATUS_LEFT) {
mItemLeft = 0;
} else {
mItemLeft = 0;
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (mStatus) {
case STATUS_LEFT:
drawBackground(canvas);
drawFloatLeftItem(canvas);
break;
case STATUS_RIGHT:
drawBackground(canvas);
drawFloatLeftItem(canvas);
break;
}
}
private void drawBackground(Canvas canvas) {
mPaint.setColor(mBackgroundColor);
canvas.drawRect(mBgRect, mPaint);
}
private void drawFloatLeftItem(Canvas canvas) {
mItemRectList.clear();
for (int i = 0; i < mItemList.size(); i++) {
canvas.save();
mPaint.setColor(mMenuBackgroundColor);
if (cicleBg) {
float cx = (mItemLeft + i * mItemWidth) + mItemWidth / 2;
float cy = mFirstItemTop + mItemHeight / 2;
float radius = mItemWidth / 2;
canvas.drawCircle(cx, cy, radius, mPaint);
} else {
mPaint.setColor(mItemList.get(i).bgColor);
canvas.drawRect(mItemLeft + i * mItemWidth, mFirstItemTop, mItemLeft + mItemWidth + i * mItemWidth, mFirstItemTop + mItemHeight, mPaint);
}
mItemRectList.add(new RectF(mItemLeft + i * mItemWidth, mFirstItemTop, mItemLeft + mItemWidth + i * mItemWidth, mFirstItemTop + mItemHeight));
mPaint.setColor(mItemList.get(i).bgColor);
drawIconTitleDot(canvas, i);
}
canvas.restore();
}
private void drawIconTitleDot(Canvas canvas, int position) {
FloatItem floatItem = mItemList.get(position);
if (floatItem.icon != null) {
float centryX = mItemLeft + mItemWidth / 2 + (mItemWidth) * position;
float centryY = mFirstItemTop + mItemHeight / 2;
float left = centryX - mItemWidth / 4;
float right = centryX + mItemWidth / 4;
float iconH = mItemHeight * 0.5f;
float textH = getTextHeight(floatItem.getTitle(), mPaint);
float paddH = (mItemHeight - iconH - textH - mRadius) / 2;
float top = centryY - mItemHeight / 2 + paddH;
float bottom = top + iconH;
mPaint.setColor(floatItem.titleColor);
canvas.drawBitmap(floatItem.icon, null, new RectF(left, top, right, bottom), mPaint);
if (!TextUtils.isEmpty(floatItem.dotNum) && !floatItem.dotNum.equals("0")) {
float dotLeft = centryX + mItemWidth / 5;
float cx = dotLeft + mCorner;
float cy = top + mCorner;
int radiu = mDrawNum ? mRadius : mRedPointRadiuWithNoNum;
mPaint.setColor(Color.RED);
canvas.drawCircle(cx, cy, radiu, mPaint);
if (mDrawNum) {
mPaint.setColor(Color.WHITE);
mPaint.setTextSize(mFontSizePointNum);
canvas.drawText(floatItem.dotNum, cx - getTextWidth(floatItem.getDotNum(), mPaint) / 2, cy + getTextHeight(floatItem.getDotNum(), mPaint) / 2, mPaint);
}
}
mPaint.setColor(floatItem.titleColor);
mPaint.setTextSize(mFontSizeTitle);
canvas.drawText(floatItem.title, centryX - getTextWidth(floatItem.getTitle(), mPaint) / 2, centryY + iconH / 2 + getTextHeight(floatItem.getTitle(), mPaint) / 2, mPaint);
}
}
public void startAnim() {
if (mItemList.size() == 0) {
return;
}
invalidate();
}
public void dismiss() {
if (!mAlphaAnim.isRunning()) {
mAlphaAnim.start();
}
}
private void removeView() {
ViewGroup vg = (ViewGroup) this.getParent();
if (vg != null) {
vg.removeView(this);
}
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
if (visibility == GONE) {
if (mOnMenuClickListener != null) {
mOnMenuClickListener.dismiss();
}
}
super.onWindowVisibilityChanged(visibility);
}
public void setOnMenuClickListener(OnMenuClickListener onMenuClickListener) {
this.mOnMenuClickListener = onMenuClickListener;
}
public interface OnMenuClickListener {
void onItemClick(int position, String title);
void dismiss();
}
private abstract class MyAnimListener implements Animator.AnimatorListener {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
for (int i = 0; i < mItemRectList.size(); i++) {
if (mOnMenuClickListener != null && isPointInRect(new PointF(event.getX(), event.getY()), mItemRectList.get(i))) {
mOnMenuClickListener.onItemClick(i, mItemList.get(i).title);
return true;
}
}
dismiss();
}
return false;
}
private boolean isPointInRect(PointF pointF, RectF targetRect) {
return pointF.x >= targetRect.left && pointF.x <= targetRect.right && pointF.y >= targetRect.top && pointF.y <= targetRect.bottom;
}
public static class Builder {
private Context mActivity;
private List mFloatItems = new ArrayList<>();
private int mBgColor = Color.TRANSPARENT;
private int mStatus = STATUS_LEFT;
private boolean cicleBg = false;
private int mMenuBackgroundColor = -1;
private boolean mDrawNum = false;
public Builder drawNum(boolean drawNum) {
mDrawNum = drawNum;
return this;
}
public Builder setMenuBackgroundColor(int mMenuBackgroundColor) {
this.mMenuBackgroundColor = mMenuBackgroundColor;
return this;
}
public Builder setCicleBg(boolean cicleBg) {
this.cicleBg = cicleBg;
return this;
}
public Builder setStatus(int status) {
mStatus = status;
return this;
}
public Builder setFloatItems(List floatItems) {
this.mFloatItems = floatItems;
return this;
}
public Builder(Context activity ) {
mActivity = activity;
}
public Builder addItem(FloatItem floatItem) {
mFloatItems.add(floatItem);
return this;
}
public Builder addItems(List list) {
mFloatItems.addAll(list);
return this;
}
public Builder setBackgroundColor(int color) {
mBgColor = color;
return this;
}
public FloatMenuView create() {
FloatMenuView floatMenuView = new FloatMenuView(mActivity, mStatus);
floatMenuView.setItemList(mFloatItems);
floatMenuView.setBackgroundColor(mBgColor);
floatMenuView.setCicleBg(cicleBg);
floatMenuView.startAnim();
floatMenuView.drawNum(mDrawNum);
floatMenuView.setMenuBackgroundColor(mMenuBackgroundColor);
return floatMenuView;
}
}
private int dip2px(float dipValue) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
private int sp2px(float spValue) {
final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
private float getTextHeight(String text, Paint paint) {
Rect rect = new Rect();
paint.getTextBounds(text, 0, text.length(), rect);
return rect.height() / 1.1f;
}
private float getTextWidth(String text, Paint paint) {
return paint.measureText(text);
}
}
Utils.java
package com.yw.game.floatmenu
import android.content.Context
import android.util.TypedValue
import android.view.ViewGroup
import android.view.WindowManager
import android.view.animation.AccelerateInterpolator
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.view.animation.AnimationSet
import android.view.animation.LinearInterpolator
import android.view.animation.ScaleAnimation
import android.widget.FrameLayout
import java.lang.reflect.Field
public class Utils {
private static int statusBarHeight
public static int getStatusBarHeight(Context context) {
if (statusBarHeight == 0) {
try {
Class> c = Class.forName("com.android.internal.R$dimen")
Object o = c.newInstance()
Field field = c.getField("status_bar_height")
int x = (Integer) field.get(o)
statusBarHeight = context.getResources().getDimensionPixelSize(x)
} catch (Exception e) {
e.printStackTrace()
}
}
return statusBarHeight
}
}