点击打开链接,下载demo...
点击打开链接,下载自定义控件(22)---FloatView悬浮窗(1)
点击打开链接,最原始的悬浮窗代码。。。
先看效果图
先看主页面布局main.xml--这里面有2个button,操作是打开浮动窗口,关闭浮动窗口
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_gravity="center" android:orientation="vertical" android:background="#ffffff" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:text="内存窗" android:textSize="40dp" android:textStyle="bold" /> <Button android:id="@+id/btnstart" android:layout_width="160dip" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" android:text="悬浮开" android:textSize="20dp" android:textStyle="bold" /> <Button android:id="@+id/btnstop" android:layout_width="160dip" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" android:text="悬浮关" android:textSize="20dp" android:textStyle="bold" /> </LinearLayout>
/**
* 代码的主要作用是:
* 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--是一个自定义控件
<?xml version="1.0" encoding="utf-8"?> <hq.memFloat.floatMenu.FloatingActionMenu xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/floatMenu" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:background="@android:color/transparent" > <hq.memFloat.floatMenu.FloatingActionButton android:id="@+id/btnEncryption" android:layout_width="60.33dp" android:layout_height="60.33dp" android:src="@drawable/bendi" /> <hq.memFloat.floatMenu.FloatingActionButton android:id="@+id/btnTransmission" android:layout_width="60.33dp" android:layout_height="60.33dp" android:src="@drawable/chuanshu2" /> </hq.memFloat.floatMenu.FloatingActionMenu>
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); } }权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />