关闭

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

1631人阅读 评论(0) 收藏 举报
分类:

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


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:344930次
    • 积分:7366
    • 等级:
    • 排名:第2961名
    • 原创:413篇
    • 转载:5篇
    • 译文:1篇
    • 评论:65条
    友情链接
    最新评论