点击打开链接,下载demo...
点击打开链接,下载自定义控件(22)---FloatView悬浮窗(1)
点击打开链接,最原始的悬浮窗代码。。。
先看效果图
先看主页面布局main.xml--这里面有2个button,操作是打开浮动窗口,关闭浮动窗口
/**
* 代码的主要作用是:
* 1、加载悬浮窗的布局文件
* 2、未布局设置WindowManager.LayoutParams
* 3、接口是进行拖动悬浮窗的设置操作
* @author Administrator
*
*/
package hq.memFloat.main;
import hq.memFloat.R;
import hq.memFloat.service.FloatService1;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class memFloat extends Activity {
Button btnstart;
Button btnstop;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnstart = (Button) findViewById(R.id.btnstart);
btnstart.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
Intent service = new Intent();
service.setClass(memFloat.this, FloatService1.class);
startService(service);
}
});
btnstop = (Button) findViewById(R.id.btnstop);
btnstop.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
Intent serviceStop = new Intent();
serviceStop.setClass(memFloat.this, FloatService1.class);
stopService(serviceStop);
}
});
}
@Override
protected void onStop() {
super.onStop();
Log.v("stop", "stop");
}
@Override
protected void onRestart() {
super.onRestart();
Log.v("restart", "restart");
}
}
package hq.memFloat.service;
import hq.memFloat.R;
import hq.memFloat.floatMenu.FloatingActionMenu;
import hq.memFloat.floatMenu.FloatingActionMenu.FloatViewUpdateListener;
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.WindowManager;
public class FloatService1 extends Service {
private WindowManager windowManager = null;
private WindowManager.LayoutParams params = null;
private FloatingActionMenu floatingActionMenu;
@SuppressLint("InflateParams")
@Override
public void onCreate() {
super.onCreate();
floatingActionMenu = (FloatingActionMenu) LayoutInflater.from(this)
.inflate(R.layout.floating_action_menu, null);
initWindowManger();
floatingActionMenu.setFloatViewUpdateListener(new FloatViewUpdateListener() {
@Override
public void onUpdateViewPosition(float dx, float dy) {
params.x += (int) (dx);
params.y += (int) (dy);
windowManager.updateViewLayout(floatingActionMenu,
params);
}
});
}
private void initWindowManger() {
// 获取WindowManager
windowManager = (WindowManager) getApplicationContext()
.getSystemService("window");
// 设置LayoutParams(全局变量)相关参数
params = getWmParams();
windowManager.addView(floatingActionMenu, params);
}
private WindowManager.LayoutParams getWmParams() {
WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();
wmParams.type = 2002;
wmParams.flags |= 8;
wmParams.gravity = Gravity.CENTER; // 调整悬浮窗口至左上角
// 以屏幕左上角为原点,设置x、y初始值
wmParams.x = 0;
wmParams.y = 25; // 25是系统状态栏的高度
// 设置悬浮窗口长宽数据
wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.format = 1;
return wmParams;
}
@Override
public void onDestroy() {
try {
windowManager.removeView(floatingActionMenu);
} catch (Exception e) {
e.printStackTrace();
}
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
加载的布局文件如下:
floating_action_menu.xml--是一个自定义控件
FloatingActionButton 这个代码里面没有进行测绘,测绘是在父类中进行的,主要是进行一些动画设置。。
package hq.memFloat.floatMenu;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.OvershootInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
public class FloatingActionButton extends ImageView {
private AnimationSet hideAnimSet, showAnimSet;
private final int DURAIION = 500;
private int deltaX = 0;
private AttributeSet attrs;
/**
* 构造函数
*/
public FloatingActionButton(Context context) {
this(context, null);
}
public FloatingActionButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatingActionButton(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.attrs = attrs;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public FloatingActionButton(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
this.attrs = attrs;
}
private AnimationSet initHideAnimation() {
if (hideAnimSet != null) {
hideAnimSet = null;
}
hideAnimSet = new AnimationSet(getContext(), attrs);
AlphaAnimation alpha = new AlphaAnimation(1f, 0f);
hideAnimSet.addAnimation(alpha);
hideAnimSet.setFillAfter(false);
hideAnimSet.setDuration(DURAIION);
// AnticipateOvershootInterpolator开始的时候向后然后向前甩一定值后返回最后的值
hideAnimSet.setInterpolator(new OvershootInterpolator(getContext(),
attrs));
TranslateAnimation hideTranslate = new TranslateAnimation(0, deltaX, 0,
0);
hideAnimSet.addAnimation(hideTranslate);
hideAnimSet.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
FloatingActionButton.this.setVisibility(INVISIBLE);
}
});
return hideAnimSet;
}
private AnimationSet initShowAnimation() {
if (showAnimSet != null) {
showAnimSet = null;
}
showAnimSet = new AnimationSet(getContext(), attrs);
AlphaAnimation alpha = new AlphaAnimation(0f, 1f);
showAnimSet.addAnimation(alpha);
showAnimSet.setDuration(DURAIION);
showAnimSet.setFillAfter(false);
TranslateAnimation showTranslate = new TranslateAnimation(deltaX, 0, 0,
0);
showAnimSet.addAnimation(showTranslate);
showAnimSet.setInterpolator(new OvershootInterpolator(getContext(),
attrs));
return showAnimSet;
}
public void setTranslateX(int deltaX) {
this.deltaX = deltaX;
}
public boolean isHidden() {
return getVisibility() == INVISIBLE;
}
public void show() {
if (isHidden()) {
this.setVisibility(VISIBLE);
startAnimation(initShowAnimation());
}
}
public void hide() {
if (!isHidden()) {
startAnimation(initHideAnimation());
}
}
public int getAnimDuration() {
return DURAIION;
}
}
/**
* 1、对2个绿色的imageview和那个灰色的Menu分别进行测绘
* 2、分别进行layout布局
* 3、设置监听时间,在touch事件里进行操作
* @author Administrator
*
*/
FloatingActionMenu
package hq.memFloat.floatMenu;
import hq.memFloat.R;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
@SuppressLint("NewApi")
public class FloatingActionMenu extends ViewGroup {
private ImageView mMenuImageToggle;
private int mChildViewCounts;
private int mMaxButtonHeight;
private boolean mMenuOpened = true;
private Handler mUiHandler = new Handler();
private float lastX = 0f;
private float lastY = 0f;
private float downX = 0f;
private float downY = 0f;
private final float CRITICAL_VALUE = 0.5f;
/**
* 构造函数
*
* @param context
*/
public FloatingActionMenu(Context context) {
this(context, null);
}
public FloatingActionMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatingActionMenu(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
createMenuButton();
}
/**
* 初始化将Menu图片添加进来,另外2个在通过测绘的形式,添加进来
*/
protected void createMenuButton() {
mMenuImageToggle = new ImageView(getContext());
mMenuImageToggle.setImageResource(R.drawable.zhuicon);
// 宽高参数
addView(mMenuImageToggle,
new LayoutParams(Util.dpToPx(getContext(), 50f), Util.dpToPx(
getContext(), 50f)));
mMenuImageToggle.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isOpened()) {
close();
} else {
open();
}
}
});
}
public void open() {
if (!isOpened()) {
int delay = 0;
for (int i = mChildViewCounts - 1; i >= 0; i--) {
View child = getChildAt(i);
if (child.getVisibility() == View.GONE
|| child == mMenuImageToggle
|| !(child instanceof FloatingActionButton)) {
continue;
}
final FloatingActionButton fab = (FloatingActionButton) child;
delay = fab.getAnimDuration();
mUiHandler.post(new Runnable() {
@Override
public void run() {
if (!isOpened()) {
fab.show();
}
}
});
}
mUiHandler.postDelayed(new Runnable() {
@Override
public void run() {
mMenuOpened = true;
}
}, delay);
}
}
public void close() {
if (isOpened()) {
int delay = 0;
for (int i = mChildViewCounts - 1; i >= 0; i--) {
View child = getChildAt(i);
if (child.getVisibility() == View.GONE
|| child == mMenuImageToggle
|| !(child instanceof FloatingActionButton)) {
continue;
}
final FloatingActionButton fab = (FloatingActionButton) child;
delay = fab.getAnimDuration();
mUiHandler.post(new Runnable() {
@Override
public void run() {
if (isOpened()) {
fab.hide();
}
}
});
}
mUiHandler.postDelayed(new Runnable() {
@Override
public void run() {
mMenuOpened = false;
}
}, delay);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = 0;// 控件需要用到的实际宽度
int height = 0;// 控件需要用到的实际高度
mMaxButtonHeight = 0;// 控件的最大宽度
mChildViewCounts = getChildCount();
/**
* 测量主菜单按钮
*/
width += mMenuImageToggle.getMeasuredWidth();
/**
* 循环测量子view
*/
for (int i = 0; i < mChildViewCounts; i++) {
View child = getChildAt(i);
// 不测量主菜单按钮了
if (child.getVisibility() == View.GONE || child == mMenuImageToggle) {
continue;
}
measureChildren(widthMeasureSpec, heightMeasureSpec);
mMaxButtonHeight = Math.max(mMaxButtonHeight,
child.getMeasuredHeight());
width += child.getMeasuredHeight();
}
width += getPaddingLeft() + getPaddingRight();
height = mMaxButtonHeight + getPaddingBottom() + getPaddingTop();
width = adjustForOvershoot(width);
if (getLayoutParams().width == LayoutParams.MATCH_PARENT) {
width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
}
if (getLayoutParams().height == LayoutParams.MATCH_PARENT) {
height = getDefaultSize(getSuggestedMinimumHeight(),
heightMeasureSpec);
}
// 设置控件的大小
setMeasuredDimension(width, height);
}
/**
* 对我提供的方法
*
* @param dx
* @param dy
*/
protected int adjustForOvershoot(int dimension) {
return (int) (dimension * 0.1 + dimension);
}
/**
* onLayout传下来的l,t,r,b分别是放置父控件的矩形可用空间的左上角的left、 top以及右下角right、bottom值。
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.i("TAG", "r:" + r + " l:" + l + " t:" + t + " b:" + b);
/**
* 本例中,ViewGroup没有margin值, (父控件-上下padding值)/2
*/
int buttonsVerticalCenterY = (b - t - getPaddingBottom() - getPaddingTop()) / 2;
/**
* 计算 mMenuImageToggle显示的X轴坐标
*/
int mMenuImageToggleLeft = r - l
- (mMenuImageToggle.getMeasuredWidth() + getPaddingRight());
/**
* 计算mMenuImageToggle显示Y轴坐标
*/
int mMenuImageToggleTop = buttonsVerticalCenterY
- mMenuImageToggle.getMeasuredHeight() / 2;
/**
* 该方法是View的放置方法,在View类实现。调用该方法需要传入放置View的矩形空间左上角left、top值和右下角right、
* bottom值。这四个值是相对于父控件而言的
*/
mMenuImageToggle.layout(mMenuImageToggleLeft, mMenuImageToggleTop,
mMenuImageToggleLeft + mMenuImageToggle.getMeasuredWidth()
+ getPaddingRight(), mMenuImageToggleTop
+ mMenuImageToggle.getMeasuredHeight()
+ getPaddingBottom());
/**
* 计算下一个View显示的X轴坐标
*/
int nextX = mMenuImageToggleLeft;
for (int i = 0; i < mChildViewCounts; i++) {
View child = getChildAt(i);
if (child.getVisibility() == View.GONE || child == mMenuImageToggle) {
continue;
}
int childY = buttonsVerticalCenterY - child.getMeasuredHeight() / 2;
/**
* 计算子View实际的X轴坐标
*/
int childX = nextX - child.getMeasuredWidth();
child.layout(childX, childY, childX + child.getMeasuredWidth(),
childY + child.getMeasuredHeight() + getPaddingBottom());
// 设置动画效果
if (child instanceof FloatingActionButton) {
FloatingActionButton fab = (FloatingActionButton) child;
fab.setTranslateX(mMenuImageToggle.getLeft() - fab.getLeft());
}
nextX = childX;
}
}
public boolean isOpened() {
return mMenuOpened;
}
/**
* 接口
*/
private FloatViewUpdateListener mListener;
public interface FloatViewUpdateListener {
public void onUpdateViewPosition(float dx, float dy);
}
public void setFloatViewUpdateListener(FloatViewUpdateListener listener) {
this.mListener = listener;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getRawX();
float y = event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE:
if (lastX != 0 || lastY != 0) {
if (mListener != null)
mListener.onUpdateViewPosition(x - lastX, y - lastY);
}
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
lastX = 0;
lastY = 0;
break;
}
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getRawX();
downY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(event.getRawX() - downX);
float dy = Math.abs(event.getRawY() - downY);
if (dx < CRITICAL_VALUE && dy < CRITICAL_VALUE) {
//下传
return false;
} else {
//拦截,交给自己的onTouch事件处理
return true;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
downX = 0;
downY = 0;
break;
}
return super.onInterceptTouchEvent(event);
}
}
Util
package hq.memFloat.floatMenu;
import android.content.Context;
import android.os.Build;
final class Util {
private Util() {
}
static int dpToPx(Context context, float dp) {
final float scale = context.getResources().getDisplayMetrics().density;
return Math.round(dp * scale);
}
}
权限