android自动申请悬浮窗权限,Android 悬浮窗--无需权限
image.png
无需一切权限,不受各种国产ROM限制,默认可以显示的应用内悬浮窗。
应用内显示,无需申请任何权限
应用内显示,所有机型都可以默认显示悬浮窗,无需引导用户做更多设置
支持拖拽
超出屏幕限制移动
可自动吸附到屏幕边缘
可向左右边缘隐藏一半
核心类共三个:
CFloatingManager.java
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.view.ViewCompat;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import com.imuxuan.floatingview.utils.EnContext;
/**
* @ClassName CFloatingManager
* @Description 悬浮窗管理器, 建造者模式
*/
public class CFloatingManager {
public static FloatingImp build() {
return new FloatingImp();
}
public static class FloatingImp {
private Handler mHandler;
private CFloatingView mCFloatingView;
private FrameLayout mContainer;
int layoutId = 0; //布局id
ViewGroup.LayoutParams params; //布局初始参数(位置,大小等)
CFloatingView.MagnetViewListener magnetViewListener;//监听
CFloatingView.IFloatingViews iFloatingViews;//监听
boolean isMovable = true;//是否可移动
boolean isHideEdge = true;//是否隐藏边缘
public FloatingImp create() {
mHandler = new Handler(Looper.getMainLooper());
synchronized (this) {
if (mCFloatingView != null) {
return this;
}
mCFloatingView = new CFloatingView(EnContext.get().getApplicationContext());
mCFloatingView.setLayout(EnContext.get().getApplicationContext(), layoutId);
if (null != iFloatingViews) {
iFloatingViews.onInitViews(mCFloatingView);
}
mCFloatingView.setLayoutParams(null == params ? defaultParams() : params);
mCFloatingView.setIsMovable(isMovable);
mCFloatingView.setIsHideEdge(isHideEdge);
if (mCFloatingView != null) {
mCFloatingView.setMagnetViewListener(magnetViewListener);
}
addViewToWindow(mCFloatingView);
}
return this;
}
/**
* 设置布局控件
*/
public FloatingImp setLayout(int layoutId) {
this.layoutId = layoutId;
return this;
}
/**
* 设置布局初始参数
*/
public FloatingImp setLayoutParams(ViewGroup.LayoutParams params) {
this.params = params;
return this;
}
/**
* 监听事件
*/
public FloatingImp setListener(CFloatingView.MagnetViewListener magnetViewListener) {
this.magnetViewListener = magnetViewListener;
return this;
}
public FloatingImp setIsMovable(boolean isMovable) {
this.isMovable = isMovable;
return this;
}
public FloatingImp setIsHideEdge(boolean isHideEdge) {
this.isHideEdge = isHideEdge;
return this;
}
public FloatingImp setInitViews(CFloatingView.IFloatingViews iFloatingViews) {
this.iFloatingViews = iFloatingViews;
return this;
}
public FloatingImp remove() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if (mCFloatingView == null) {
return;
}
if (ViewCompat.isAttachedToWindow(mCFloatingView) && mContainer != null) {
mContainer.removeView(mCFloatingView);
}
mCFloatingView = null;
}
});
return this;
}
/**
* 将view绑定到activity的布局中
*/
public FloatingImp attach(Activity activity) {
attach(getActivityRoot(activity));
return this;
}
/**
* 将view绑定到布局中
*/
public FloatingImp attach(FrameLayout container) {
if (container == null || mCFloatingView == null) {
mContainer = container;
return this;
}
if (mCFloatingView.getParent() == container) {
return this;
}
if (mContainer != null && mCFloatingView.getParent() == mContainer) {
mContainer.removeView(mCFloatingView);
}
mContainer = container;
container.addView(mCFloatingView);
mCFloatingView.onAttach();
return this;
}
/**
* 将view从activity的布局中解绑
*/
public FloatingImp detach(Activity activity) {
detach(getActivityRoot(activity));
return this;
}
/**
* 将view从布局中解绑
*/
public FloatingImp detach(FrameLayout container) {
if (mCFloatingView != null && container != null && ViewCompat.isAttachedToWindow(mCFloatingView)) {
container.removeView(mCFloatingView);
}
if (mContainer == container) {
mContainer = null;
}
mCFloatingView.onDetach();
return this;
}
/**
* 将view添加到当前窗口中
*/
private void addViewToWindow(final CFloatingView view) {
if (mContainer == null) {
return;
}
mContainer.addView(view);
}
/**
* 默認Params,用于设置最开始位置
*/
private FrameLayout.LayoutParams defaultParams() {
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.BOTTOM | Gravity.START;
params.setMargins(13, params.topMargin, params.rightMargin, 56);
return params;
}
/**
* 获取activity所绑定的布局
*/
private FrameLayout getActivityRoot(Activity activity) {
if (activity == null) {
return null;
}
try {
return (FrameLayout) activity.getWindow().getDecorView().findViewById(android.R.id.content);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
}
CFloatingView.java
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.MotionEvent;
import android.view.View;
/**
* @ClassName CFloatingView
* @Description 悬浮窗,继承磁力吸附悬浮窗,封装点击事件
*/
public class CFloatingView extends FloatingMagnetView {
private MagnetViewListener mMagnetViewListener;
private long mLastTouchDownTime;//用于判断点击
private static final int TOUCH_TIME_THRESHOLD = 150;
public View view;
/**
* 悬浮窗点击接口
*/
public interface MagnetViewListener {
void onRemove(CFloatingView cFloatingView);
void onClick(CFloatingView cFloatingView);
void onEndAppear(CFloatingView cFloatingView);
void onEndHide(CFloatingView cFloatingView);
}
public interface IFloatingViews {
void onInitViews(CFloatingView cFloatingView);
}
public CFloatingView(@NonNull Context context) {
super(context, null);
}
public View setLayout(@NonNull Context context, int en_floating_view_id) {
return view = inflate(context, en_floating_view_id, this);
}
public void setMagnetViewListener(MagnetViewListener magnetViewListener) {
this.mMagnetViewListener = magnetViewListener;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (event != null) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastTouchDownTime = System.currentTimeMillis();
clearAnimation();
break;
case MotionEvent.ACTION_UP:
if (isOnClickEvent()) {
dealClickEvent();
}
break;
}
}
return true;
}
protected void dealClickEvent() {
if (mMagnetViewListener != null) {
mMagnetViewListener.onClick(this);
}
}
public void onRemove() {
if (mMagnetViewListener != null) {
mMagnetViewListener.onRemove(this);
}
}
protected boolean isOnClickEvent() {
return System.currentTimeMillis() - mLastTouchDownTime < TOUCH_TIME_THRESHOLD;
}
/**
* 隐藏,点击又出现之后,由子类进行具体实现
*/
@Override
protected void onEndAppear() {
super.onEndAppear();
if (mMagnetViewListener != null) {
mMagnetViewListener.onEndAppear(this);
}
}
/**
* 隐藏半边之后
*/
@Override
protected void onEndHide() {
super.onEndHide();
if (mMagnetViewListener != null) {
mMagnetViewListener.onEndHide(this);
}
}
@Override
public void onAttach() {
super.onAttach();
handler.removeCallbacksAndMessages(null);
handler.postDelayed(new Runnable() {
@Override
public void run() {
hideEdgeAnima();
}
}, 2000);
}
}
FloatingMagnetView.java
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import com.imuxuan.floatingview.utils.SystemUtils;
/**
* @ClassName FloatingMagnetView
* @Description 磁力吸附悬浮窗
* @Author Yunpeng Li
* @Creation 2018/3/15 下午5:02
* @Mender Yunpeng Li
* @Modification 2018/3/15 下午5:02
*/
public class FloatingMagnetView extends FrameLayout {
public static final int MARGIN_EDGE = 13;
private float mOriginalRawX;
private float mOriginalRawY;
private float mOriginalX;
private float mOriginalY;
protected MoveAnimator mMoveAnimator;
protected int mScreenWidth;
private int mScreenHeight;
private int mStatusBarHeight;
boolean isMovable = true;//是否可移动
public FloatingMagnetView(Context context) {
this(context, null);
}
public FloatingMagnetView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatingMagnetView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mMoveAnimator = new MoveAnimator();
mStatusBarHeight = SystemUtils.getStatusBarHeight(getContext());
setClickable(true);
updateSize();
}
/**
* 设置是否可移动
*/
public void setIsMovable(boolean isMovable) {
this.isMovable = isMovable;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event == null) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
changeOriginalTouchParams(event);
updateSize();
mMoveAnimator.stop();
removeRunable();
onEndAppear();
break;
case MotionEvent.ACTION_MOVE:
if (isMovable) {
updateViewPosition(event);
}
break;
case MotionEvent.ACTION_UP:
moveToEdge();
break;
}
return true;
}
/**
* 更新view的坐标位置
*/
private void updateViewPosition(MotionEvent event) {
setX(mOriginalX + event.getRawX() - mOriginalRawX);
// 限制不可超出屏幕高度
float desY = mOriginalY + event.getRawY() - mOriginalRawY;
if (desY < 0) {
desY = 0;
}
if (desY > mScreenHeight - getHeight() * 2) {
desY = mScreenHeight - getHeight() * 2;
}
setY(desY);
}
private void changeOriginalTouchParams(MotionEvent event) {
mOriginalX = getX();
mOriginalY = getY();
mOriginalRawX = event.getRawX();
mOriginalRawY = event.getRawY();
}
protected void updateSize() {
mScreenWidth = (SystemUtils.getScreenWidth(getContext()) - this.getWidth());
mScreenHeight = SystemUtils.getScreenHeight(getContext());
}
public void moveToEdge() {
float moveDistance = isNearestLeft() ? MARGIN_EDGE : mScreenWidth - MARGIN_EDGE;
mMoveAnimator.start(moveDistance, getY());
}
protected boolean isNearestLeft() {
int middle = mScreenWidth / 2;
return getX() < middle;
}
protected class MoveAnimator implements Runnable {
private Handler handler = new Handler(Looper.getMainLooper());
private float destinationX;
private float destinationY;
private long startingTime;
void start(float x, float y) {
this.destinationX = x;
this.destinationY = y;
startingTime = System.currentTimeMillis();
handler.post(this);
}
@Override
public void run() {
if (getRootView() == null || getRootView().getParent() == null) {
return;
}
float progress = Math.min(1, (System.currentTimeMillis() - startingTime) / 400f);
float deltaX = (destinationX - getX()) * progress;
float deltaY = (destinationY - getY()) * progress;
move(deltaX, deltaY);
if (progress < 1) {
handler.post(this);
} else {
hideEdgeAnima();//开始隐藏
}
}
private void stop() {
handler.removeCallbacks(this);
}
}
private void move(float deltaX, float deltaY) {
setX(getX() + deltaX);
setY(getY() + deltaY);
}
public void onAttach() {
}
public void onDetach() {
}
//关于靠边隐藏==============================================================================================
boolean isHideEdge = true;//是否隐藏边缘
protected static final int HEDE_FLOAT_VIEW_TIME = 3000;//靠边隐藏时间
protected static final int MES_ANIMA_LEFT = 0;
protected static final int MES_ANIMA_RIGHT = 1;
protected static final int VIEW_GONE = 2;
protected static final int VIEW_Transparent = 3;
protected AnimationSet animationleft;
protected AnimationSet animationright;
/**
* 设置是否隐藏边缘
*/
public void setIsHideEdge(boolean isHideEdge) {
this.isHideEdge = isHideEdge;
}
/**
* 隐藏边缘动画
*/
protected void hideEdgeAnima() {
if (isHideEdge) {
if (isNearestLeft()) {
handler.postDelayed(myRunnableLeft, HEDE_FLOAT_VIEW_TIME);
} else {
handler.postDelayed(myRunnableRigth, HEDE_FLOAT_VIEW_TIME);
}
}
}
private void removeRunable() {
handler.removeCallbacksAndMessages(null);
}
protected Runnable runnableViewGone = new Runnable() {
public void run() {
Message message = handler.obtainMessage();
message.what = VIEW_GONE;
handler.sendMessage(message);
}
};
protected Runnable runnableTransparent = new Runnable() {
@Override
public void run() {
Message message = handler.obtainMessage();
message.what = VIEW_Transparent;
handler.sendMessage(message);
}
};
protected Runnable myRunnableLeft = new Runnable() {
public void run() {
Message message = handler.obtainMessage();
message.what = MES_ANIMA_LEFT;
handler.sendMessage(message);
}
};
protected Runnable myRunnableRigth = new Runnable() {
public void run() {
Message message = handler.obtainMessage();
message.what = MES_ANIMA_RIGHT;
handler.sendMessage(message);
}
};
protected Handler handler = new Handler(Looper.getMainLooper()) {
public void handleMessage(Message msg) {
switch (msg.what) {
case MES_ANIMA_LEFT:
if (null == animationleft) {
animationleft = new AnimationSet(true);
animationleft.setDuration(250);
animationleft.addAnimation(new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, -0.5f, 0, 0, 0, 0));
animationleft.setFillAfter(true);
}
startAnimation(animationleft);
animationleft.setAnimationListener(ainimaLeft);
break;
case MES_ANIMA_RIGHT:
if (null == animationright) {
animationright = new AnimationSet(true);
animationright.setDuration(250);
animationright.addAnimation(new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0.5f, 0, 0, 0, 0));
animationright.setFillAfter(true);
}
startAnimation(animationright);
animationright.setAnimationListener(ainimaRigth);
break;
// case VIEW_GONE:
//
// imgFloatView.setVisibility(View.GONE);
// Log.d(TAG, "handleMessage: bingo");
// break;
case VIEW_Transparent:
break;
default:
break;
}
}
};
protected Animation.AnimationListener ainimaLeft = new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
onEndHide();
}
};
protected Animation.AnimationListener ainimaRigth = new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
onEndHide();
}
};
/**
* 隐藏,点击又出现之后,由子类进行具体实现
*/
protected void onEndAppear() {
}
/**
* 隐藏半边之后,由子类进行具体实现
*/
protected void onEndHide() {
}
}
两个个辅助工具类:
EnContext.java
import android.app.Application;
/**
* Created by Yunpeng Li on 2018/11/8.
*/
public class EnContext {
private static final Application INSTANCE;
static {
Application app = null;
try {
app = (Application) Class.forName("android.app.AppGlobals").getMethod("getInitialApplication").invoke(null);
if (app == null)
throw new IllegalStateException("Static initialization of Applications must be on main thread.");
} catch (final Exception e) {
e.printStackTrace();
try {
app = (Application) Class.forName("android.app.ActivityThread").getMethod("currentApplication").invoke(null);
} catch (final Exception ex) {
e.printStackTrace();
}
} finally {
INSTANCE = app;
}
}
public static Application get() {
return INSTANCE;
}
}
SystemUtils.java
import android.content.Context;
/**
* Created by Yunpeng Li on 2018/3/15.
*/
public class SystemUtils {
public static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public static int getScreenWidth(Context context) {
int screenWith = -1;
try {
screenWith = context.getResources().getDisplayMetrics().widthPixels;
} catch (Exception e) {
e.printStackTrace();
}
return screenWith;
}
public static int getScreenHeight(Context context) {
int screenHeight = -1;
try {
screenHeight = context.getResources().getDisplayMetrics().heightPixels;
} catch (Exception e) {
e.printStackTrace();
}
return screenHeight;
}
}
应用例子:
在基础类的地方创建一个全局的CFloatingManager.FloatingImp floatingImp对象,目的是为了保持对象唯一,然后在各个子类中进行引用:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (null == floatingImp) {
floatingImp = CFloatingManager.build()
.setLayout(R.layout.en_floating_view)//设置布局
.setInitViews(new CFloatingView.IFloatingViews() { // 可以在此处对布局内的子控件单独进行控制,todo:设置点击事件,则会影响到拖动事件
@Override
public void onInitViews(CFloatingView cFloatingView) {
cFloatingView.view.findViewById(R.id.icon).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(TestActivity.this, "点击了icon", Toast.LENGTH_SHORT).show();
}
});
}
})
.setListener(new CFloatingView.MagnetViewListener() {//设置监听事件
@Override
public void onRemove(CFloatingView cFloatingView) {
Toast.makeText(TestActivity.this, "我没了", Toast.LENGTH_SHORT).show();
}
@Override
public void onClick(CFloatingView cFloatingView) {
Toast.makeText(TestActivity.this, "点到我了", Toast.LENGTH_SHORT).show();
}
@Override
public void onEndAppear(CFloatingView cFloatingView) {
((ImageView)cFloatingView.view.findViewById(R.id.icon)).setImageResource(R.drawable.jy_sdk_float_window_normal);
Toast.makeText(TestActivity.this, "又出现了", Toast.LENGTH_SHORT).show();
}
@Override
public void onEndHide(CFloatingView cFloatingView) {
((ImageView)cFloatingView.view.findViewById(R.id.icon)).setImageResource(R.drawable.jy_sdk_float_window_transparent);
Toast.makeText(TestActivity.this, "隐藏了一半", Toast.LENGTH_SHORT).show();
}
})
.setIsMovable(true)//控制是否可移动
.setIsHideEdge(true)//控制是否需要隐藏
.create();//创建实体
}
}
@Override
protected void onStart() {
super.onStart();
floatingImp.attach(this);//绑定实体至页面
}
@Override
protected void onStop() {
super.onStop();
floatingImp.detach(this);//移除实体离开页面
}
en_floating_view.xml
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:id="@+id/icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/imuxuan" />
android:id="@+id/icon2"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_toRightOf="@+id/icon"
android:src="@drawable/imuxuan" />
其中的小图标我就不上了,更换一下就行
android自动申请悬浮窗权限,Android 悬浮窗--无需权限相关推荐
- android 可编辑的表格框架,smartTable-一款android自动生成表格框架---A Android automatically generated table framework...
一款android自动生成表格框架功能介绍 1. 快速配置自动生成表格: 2. 自动计算表格宽高: 3. 表格列标题组合: 4. 表格固定左序列.顶部序列.第一行.列标题.统计行: 5. 自动统计,排 ...
- android自动接听电话并回复,android自动接听电话各种异常处理
public void autoAnswerPhone() { try { Log.i(TAG,"autoAnswerPhone"); ITelephony itelephony ...
- android 自动打包脚本,Jenkins实现Android自动化打包
1.Tomcat 进入 https://tomcat.apache.org/ 官网,下载最新的 tomcat 安装包并且安装. 安装完成后,启动 tomcat 后,在浏览器中输入 http://loc ...
- android自动测试2:使用android studio实现设备循环自动重启
一.前提: apk可以获得系统签名 二.适用: 需要对android设备进行循环重启测试 三.步骤: 1. AndroidManifest.xml中添加以下权限: <uses-permissio ...
- android 自动更新 服务端,搭建android版本更新服务器使用android系统自带的DownloadManager下载文件...
这几天想自己做一个文件更新的功能,但是由于不知道怎样写服务端,所以一直没有去做,后来发现原来服务端编写简直是太简单了,所以今天就实现了 版本更新的这样一个功能. 一搭建版本更新服务器: 搭建这个一个服 ...
- android自动触发返回,ionic4处理android返回按钮事件
前言 之前在这里介绍了ionic3如何处理android返回按钮 ionic4和ionic3关于android返回按钮的处理是不一样的,而且有点坑,所以本文介绍一下 效果演示 如下gif,所有返回操作 ...
- android自动屏幕点击事件,Android 中屏幕点击事件的实现
在android下,事件的发生是在监听器下进行,android系统可以响应按键事件和触摸屏事件,事件说明如下: 常用实现OnClickListener,OnTouchListener,OnFocusC ...
- android 自动上下翻滚,如何让Android TextView自动向下滚动到最后?
我有一个TextView,其内容从文本文件中复制.现在每次将文本文件的内容加载到TextView中时,我都希望它自动向下滚动到最后. 这是我的布局XML文件的部分内容: android:id=&quo ...
- android 自动打开第三方应用程序,Android如何做到应用程序图标隐藏,由第三方程序显示启动...
Android如何做到应用程序图标隐藏,由第三方程序显示启动 发布时间:2020-07-13 03:25:02 来源:51CTO 阅读:11353 作者:ord1nary 在AndroidManife ...
最新文章
- 从零入门 Serverless | 一文详解 Serverless 技术选型
- php如何实现添加到购物车_PHP实现添加购物车功能
- 落在我手里,今天你能嫁出去算我输!
- .NET开源工作流CCFlow-快速入门
- UIPageViewController用法
- ssd raid0 linux 2018,2018-01-28 Linux学习之RAID与LVM硬盘阵列技术
- Camel In Action 读书笔记 (8)
- 理解与学习linux 文件系统的目录结构
- ATIchinapay银联支付模块.zip
- c3p0 服务启动获取连接超时_一次c3p0连接池连接异常错误的排查
- 【国产MCU移植】看看有没有你需要的,一起来查漏补缺吧!(附已报名的硬件)...
- 读取cpu温度的api_获取传感器温度-cpu 温度篇
- 单细胞分析实录(18): 基于CellPhoneDB的细胞通讯分析及可视化 (上篇)
- const的意义及作用
- 天呐!惊人的Springboot测试.Springboot测试类之@RunWith注解
- matlab和saber哪个好用,实例分析 saber与simulink谁更适合仿真
- 语音增强算法的概述[转]
- 软通22年秋季新员工入职考试
- against fate
- 阻塞/非阻塞 同步/异步