自定义控件(23)---FloatView悬浮窗(2)

原创 2015年11月18日 17:17:11

点击打开链接,下载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>

看主页面的代码memFloat

/**
 * 代码的主要作用是:
 * 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");

	}

}

看上面代码中的FloatService1

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;
	}
}

上段代码中floatingActionMenu = (FloatingActionMenu) LayoutInflater.from(this)
                .inflate(R.layout.floating_action_menu, 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>

先看那2个绿色的imageView代码

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" />


相关文章推荐

自定义控件(22)---FloatView悬浮窗(1)

点击打开链接,现在demo。。。。 activity_main.xml看主界面的布局文件

Android手机悬浮窗口小例子

–主页面——–//布局中就一个Button public class MainActivity extends Activity { @Override protected void o...
  • true100
  • true100
  • 2016年08月17日 15:43
  • 2966

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

android悬浮窗口的实现

http://blog.csdn.net/stevenhu_223/article/details/8504058 当我们在手机上使用360安全卫士时,手机屏幕上时刻都会出现一个小浮动窗口,点击该浮...
  • an__qi
  • an__qi
  • 2014年09月05日 11:18
  • 887

AndroidDemo - FloatWindowDemo

安卓悬浮窗Demo 在桌面上创建一个小的悬浮窗。点击小悬浮窗后会弹出一个大的窗口。大窗口上有2个按键,分别为返回与关闭。点击大窗口外的部分能返回小窗口。 Demo包含以下几个主要文件: -------...

android悬浮窗口的实现

当我们在手机上使用360安全卫士时,手机屏幕上时刻都会出现一个小浮动窗口,点击该浮动窗口可跳转到安全卫士的操作界面,而且该浮动窗口不受其他activity的覆盖影响仍然可见(多米音乐也有相关的和主界面...

Android 自定义PopupWindow实现悬浮窗效果

有时候我们需要在界面上弹出一个窗口,而Android中弹出窗体有两种方式:一种是AlertDialog,另一种就是PopupWindow,AlertDialog的位置是固定的,而PopupWindow...

Android常用控件之悬浮窗(Service实现)

悬浮窗可以显示在所有应用程序之上,不管在PC机还是Android设备上都有这个,最常见的是360的“加速球” 来看下在Android设备上的效果 程序的目录结构如下图 创...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Android UI开发第十二篇——动画效果Animation

Android框架本身就使用了大量的动画效果,比如Activity切换的动画效果,Dialog弹出和关闭时的渐变动画效果以及Toast显示信息时的淡入淡出效果等等。Android系统框架为我们提供了一...
  • xyz_lmn
  • xyz_lmn
  • 2011年10月26日 13:22
  • 14971
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:自定义控件(23)---FloatView悬浮窗(2)
举报原因:
原因补充:

(最多只允许输入30个字)