Android 技术博客(3):android 悬浮窗菜单,可用于显示在 launcher 或者 activity。
16lz
2021-01-23
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;//用于画anything private Paint mPaintBg;//用于画anything private String dotNum = null;//红点数字 private float mAlphValue;//透明度动画值 private float mRolateValue = 1f;//旋转动画值 private boolean inited = false;//标记透明动画是否执行过,防止因onreseme 切换导致重复执行 private Bitmap mBitmap;//logo private final int mLogoBackgroundRadius = dip2px(25);//logo的灰色背景圆的半径 private final int mLogoWhiteRadius = dip2px(20);//logo的白色背景的圆的半径 private final int mRedPointRadiusWithNum = dip2px(6);//红点圆半径 private final int mRedPointRadius = dip2px(3);//红点圆半径 private final int mRedPointOffset = dip2px(10);//红点对logo的偏移量,比如左红点就是logo中心的 x - mRedPointOffset 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;//往左右隐藏多少宽度的偏移值, 隐藏宽度的0.4 private Camera mCamera;//camera用于执行3D动画 private boolean mDrawNum = false;//只绘制红点还是红点+白色数字 private int mStatus = NORMAL;//0 正常,1 左,2右,3 中间方法旋转 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);//60% 黑色背景 (透明度 40%) 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) { //手指拖动后离开屏幕复位时使用 x轴旋转 3d动画 mCamera.save(); mCamera.rotateX(720 * scaleOffset);//0-720度 最多转两圈 mCamera.getMatrix(mMatrix); mMatrix.preTranslate(-getWidth() / 2, -getHeight() / 2); mMatrix.postTranslate(getWidth() / 2, getHeight() / 2); canvas.concat(mMatrix); mCamera.restore(); } else { //手指拖动且手指未离开屏幕则使用 绕图心2d旋转动画 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); // 60% 白色 (透明度 40%) mPaint.setColor(0x99ffffff); } else { //100% 白色背景 (透明度 0%) mPaint.setColor(0xFFFFFFFF); } if (mAlphValue != 0) { mPaint.setAlpha((int) (mAlphValue * 255)); } canvas.drawCircle(centryX, centryY, mLogoWhiteRadius, mPaint); } canvas.restore(); //100% 白色背景 (透明度 0%) 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 + '\'' + '}'; }}
FloatLogoMenu.java
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 (mActivity == null || mActivity.isFinishing() || mActivity.getWindowManager() == null) {// throw new IllegalArgumentException("Activity = null, or Activity is isFinishing ,or this Activity`s token is bad");// } 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(); //类似dialog,寄托在activity的windows上,activity关闭时需要关闭当前float 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) { //在android7.1以上系统需要使用TYPE_PHONE类型 配合运行时权限 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(); //判断状态栏是否显示 如果不显示则statusBarHeight为0 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) { //悬浮窗超过5秒没有操作的话会自动隐藏 @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); }// mFloatLogo.setOnTouchListener(mDefaultOnTouchListerner);//把onClick事件分发下去,防止onclick无效 } } }; } /** * 用于 拦截 菜单项的 关闭事件,以方便开始 隐藏定时器 * * @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() { //这里的onCick只有 touchListener = mDefaultOnTouchListerner 才会触发// mFloatLogo.setOnClickListener(new OnClickListener() {// @Override// public void onClick(View v) {// if (!isDraging) {// if (mFloatLogo.getStatus() != DotImageView.NORMAL) {// mFloatLogo.setBitmap(mLogoRes);// mFloatLogo.setStatus(DotImageView.NORMAL);// if (!mFloatLogo.mDrawDarkBg) {// mFloatLogo.setDrawDarkBg(true);// }// }// mFloatLogo.setOnTouchListener(touchListener);// mHideTimer.start();// }// }// }); mFloatLogo.setOnTouchListener(touchListener);//恢复touch事件 } /** * 悬浮窗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(); //连续移动的距离超过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); } } /** * 悬浮窗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(); }// //这里需要判断如果如果手指所在位置和logo所在位置在一个宽度内则不移动, 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() { //记录上次的位置logo的停放位置,以备下次恢复 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); } }}
FloatMenuView.java
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;//菜单项左边的默认偏移值,这里是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);//菜单项的title的文字大小 private float mFirstItemTop;//菜单项的最小y值,上面起始那条线 private boolean mDrawNum = false;//是否绘制数字,false则只绘制红点 private boolean cicleBg = false;//菜单项背景是否绘制成圆形,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; } //用于标记所依赖的view的screen的坐标,实际view的坐标是window坐标,所以这里后面会减去状态栏的高度 //设置菜单的背景颜色 public void setMenuBackgroundColor(int mMenuBackgroundColor) { this.mMenuBackgroundColor = mMenuBackgroundColor; } //设置这个view(整个屏幕)的背景,这里默认透明 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;//x中心点 float cy = mFirstItemTop + mItemHeight / 2;//y中心点 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;//计算每一个item的中心点x的坐标值 float centryY = mFirstItemTop + mItemHeight / 2;//计算每一个item的中心点的y坐标值 float left = centryX - mItemWidth / 4;//计算icon的左坐标值 中心点往左移宽度的四分之一 float right = centryX + mItemWidth / 4; float iconH = mItemHeight * 0.5f;//计算出icon的宽度 = icon的高度 float textH = getTextHeight(floatItem.getTitle(), mPaint); float paddH = (mItemHeight - iconH - textH - mRadius) / 2;//总高度减去文字的高度,减去icon高度,再除以2就是上下的间距剩余 float top = centryY - mItemHeight / 2 + paddH;//计算icon的上坐标值 float bottom = top + iconH;//剩下的高度空间用于画文字 //画icon 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;//x中心点 float cy = top + mCorner;//y中心点 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); //画menu title 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; /** * 用于获取状态栏的高度。 * * @return 返回状态栏高度的像素值。 */ 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; }}
更多相关文章
- Android数据存储路径位置
- Android GPS获取地理位置
- Android侧滑菜单
- Android UI控件详解-Button(按钮)点击事件的5种写法
- Android 利用adb命令 使App自动点击屏幕指定位置
- android 模拟滑动事件
- android APP响应H5页面的点击事件(JS交互)