如何在Android中实现悬浮activity透明悬浮窗口

Android_WindowManager_实现类似360流量悬浮框
2.过程实现
这里主要用到WindowManager类,通过WindowManager的addView(View view, WindowManager.LayoutParams params)方法可以在窗体中添加view对象,如果希望浮动窗体随手指一块移动,应该修改params参数并通过windowManager对象的updateViewLayout()方法完成更新,并配合service!对于WindowManager.LayoutParams这里不做介绍,不了解的朋友可以网上查~
* @author zimo2013
* @see http://blog.csdn.net/zimo2013
public class MyService extends Service {
private MyWindowManager windowM
* 这里使用线程池,比如访问网络数据的实时更新,主要完成于悬浮框的通信,可根据需要实现,这里并没有实现
private ScheduledExecutorService threadP
public void onCreate() {
super.onCreate();
public IBinder onBind(Intent intent) {
public int onStartCommand(Intent intent, int flags, int startId) {
if (windowManager == null) {
windowManager = MyWindowManager
.getInstance(getApplicationContext());
if (threadPool == null) {
threadPool = Executors.newScheduledThreadPool(1);
threadPool.scheduleAtFixedRate(command, 0, 1, TimeUnit.SECONDS);
//开启悬浮框
windowManager.show();
return super.onStartCommand(intent, flags, startId);
private Runnable command = new Runnable() {
public void run() {
// 在该方法中,定时更新ui,比如访问网络数据的实时更新,主要完成于悬浮框的通信,这里暂不实现
System.out.println(run());
public void onDestroy() {
super.onDestroy();
if (threadPool != null) {
threadPool.shutdown();
threadPool =
* 单例设计
* @author zimo2013
* @see http://blog.csdn.net/zimo2013
public class MyWindowManager {
private static MyWindowM
private MyWindowManager() {
private static WindowManager winM
private static C
private LayoutP
private LayoutParams paramsC
* 显示的浮动窗体
private FloatView floatV
* 点击浮动窗体后的详细信息
private FloatContentView floatContentV
// 屏幕的尺寸
private static int displayW
private static int displayH
* @param context
ApplicationContext
public static synchronized MyWindowManager getInstance(Context context) {
if (manager == null) {
MyWindowManager.context =
winManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
displayWidth = winManager.getDefaultDisplay().getWidth();
displayHeight = winManager.getDefaultDisplay().getHeight();
manager = new MyWindowManager();
* 显示悬浮框
public void show() {
floatView = getView();
if (floatView.getParent() == null) {
winManager.addView(floatView, params);
* 点击悬浮框后的显示的实时详细流量信息
public void showContent() {
winManager.addView(getContentView(), paramsContent);
// 先移除悬浮框
winManager.removeView(floatView);
floatView =
// 移动悬浮框
public void move(View view, int delatX, int deltaY) {
if (view == floatView) {
params.x += delatX;
params.y += deltaY;
// 更新floatView
winManager.updateViewLayout(view, params);
* 移除悬浮框
public void dismiss() {
winManager.removeView(floatContentView);
floatContentView =
public void back() {
winManager.addView(getView(), params);
winManager.removeView(floatContentView);
floatContentView =
* 是否实时更新流量数据,主要用于流量悬浮框是否实时更新流量数据
public boolean isUpdate() {
if (floatContentView == null) {
* 得到悬浮框view
public FloatView getView() {
if (floatView == null) {
floatView = new FloatView(context);
if (params == null) {
params = new LayoutParams();
params.type = LayoutParams.TYPE_PHONE;
params.format = PixelFormat.RGBA_8888;
params.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
| LayoutParams.FLAG_NOT_FOCUSABLE;
params.gravity = Gravity.LEFT | Gravity.TOP;
// 须指定宽度高度信息
params.width = floatView.mW
params.height = floatView.mH
params.x = displayWidth - floatView.mW
params.y = displayHeight / 2;
return floatV
* 得到实时流量悬浮框view
public View getContentView() {
if (floatContentView == null) {
floatContentView = new FloatContentView(context);
if (paramsContent == null) {
paramsContent = new LayoutParams();
paramsContent.type = LayoutParams.TYPE_PHONE;
paramsContent.format = PixelFormat.RGBA_8888;
paramsContent.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
| LayoutParams.FLAG_NOT_FOCUSABLE;
paramsContent.gravity = Gravity.LEFT | Gravity.TOP;
// 须指定宽度高度信息
paramsContent.width = floatContentView.mW
paramsContent.height = floatContentView.mH
paramsContent.x = (displayWidth - floatContentView.mWidth) / 2;
paramsContent.y = (displayHeight - floatContentView.mHeight) / 2;
return floatContentV
public class FloatView extends LinearLayout {
private TextView tvI
private MyWindowM
public int mW
public int mH
private int preX;
private int preY;
private boolean isM
public FloatView(Context context) {
this(context, null);
public FloatView(Context context, AttributeSet attrs) {
super(context, attrs);
// 填充布局,并添加至
LayoutInflater.from(context).inflate(R.layout.float_view, this);
tvInfo = (TextView) findViewById(R.id.tv_info);
mWidth = tvInfo.getLayoutParams().
mHeight = tvInfo.getLayoutParams().
manager = MyWindowManager.getInstance(context);
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
preX = (int) event.getRawX();
preY = (int) event.getRawY();
case MotionEvent.ACTION_MOVE:
x = (int) event.getRawX();
y = (int) event.getRawY();
manager.move(this, x - preX, y - preY);
case MotionEvent.ACTION_UP:
if (!isMove) {//是否是移动,主要是区分click
//显示悬浮框详细流量信息
manager.showContent();
return super.onTouchEvent(event);
public class FloatContentView extends LinearLayout implements OnClickListener {
public int mW
public int mH
private MyWindowM
private PackageManager pkgM
private LayoutI
private TrafficA
private Button btCL
private Button btB
private ListView listV
public FloatContentView(Context context) {
this(context, null);
public FloatContentView(Context context, AttributeSet attrs) {
super(context, attrs);
pkgManager = context.getPackageManager();
// 填充布局,并添加至
inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.float_content_view, this);
manager = MyWindowManager.getInstance(context);
ViewGroup.LayoutParams params = findViewById(R.id.content)
.getLayoutParams();
mWidth = params.
mHeight = params.
btCLose = (Button) findViewById(R.id.bt_close);
btBack = (Button) findViewById(R.id.bt_back);
listView = (ListView) findViewById(R.id.list);
btCLose.setOnClickListener(this);
btBack.setOnClickListener(this);
adapter = new TrafficAdapter();
listView.setAdapter(adapter);
//消息,主要完成流量的实时更新
handler.sendEmptyMessage(0);
// 主要用于实时更新
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
// 更新流信息
if (manager.isUpdate()) {
adapter.notifyDataSetChanged();
handler.sendEmptyMessageDelayed(0, 2 * 1000);
public void onClick(View v) {
if (v == btCLose) {
manager.dismiss();
} else if (v == btBack) {
manager.back();
private class TrafficAdapter extends BaseAdapter {
private List
TrafficAdapter() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
//根据意图,查询得到与之对应的list信息
infos = pkgManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
public int getCount() {
return infos.size();
public Object getItem(int position) {
return infos.get(position);
public long getItemId(int position) {
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.item, null);
convertView.setTag(new ViewHolder(convertView));
ViewHolder holder = (ViewHolder) convertView.getTag();
ResolveInfo info = infos.get(position);
holder.tvPkgName.setText(info.loadLabel(pkgManager).toString());
int uid = pkgManager.getPackageInfo(info.activityInfo.packageName,
0).applicationInfo.
//TrafficStats查询具体流量数据
holder.tvRcvSize.setText(SizeFormaterUtil.getDataSize(TrafficStats
.getUidRxBytes(uid)));
holder.tvSndSize.setText(SizeFormaterUtil.getDataSize(TrafficStats
.getUidTxBytes(uid)));
} catch (NameNotFoundException e) {
e.printStackTrace();
return convertV
private class ViewHolder {
TextView tvPkgN
TextView tvRcvS
TextView tvSndS
public ViewHolder(View view) {
tvPkgName = (TextView) view.findViewById(R.id.tv_name);
tvRcvSize = (TextView) view.findViewById(R.id.tv_rcv);
tvSndSize = (TextView) view.findViewById(R.id.tv_snd);
(window.slotbydup=window.slotbydup || []).push({
id: '2467140',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'Android 超炫的悬浮窗设计与实现
第2页_Linux编程_Linux公社-Linux系统门户网站
你好,游客
Android 超炫的悬浮窗设计与实现
来源:Linux社区&
作者:zz7zz7zz
三、具体代码
1.& Manifest.xml中的配置
权限:&uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/&&uses-permission android:name="android.permission.GET_TASKS"/&
其他配置:&!-- 悬浮框 开始 --&&meta-data android:name="tooltipdata" android:value="com.open.MainActivity" /&&service android:name="com.open.tooltip.TooltipService"& android:exported="false"&&/service&&!-- 悬浮框 接收 --&
2.ChatBall,悬浮的小球
package com.open.
import android.content.Cimport android.graphics.Rimport android.os.Bimport android.util.AttributeSimport android.view.MotionEimport android.view.Vimport android.widget.ImageV
/**&* 小球&* @author DexYang&*&*/public class ChatBall extends ImageView{&&private final String TAG="ChatBall";
&public ChatBall(Context context, AttributeSet attrs, int defStyle) {& super(context, attrs, defStyle);& init(context);&}
&public ChatBall(Context context, AttributeSet attrs) {& super(context, attrs);& init(context);&}
&public ChatBall(Context context) {& super(context);& init(context);& &}&&private void init(Context context)&{& this.setImageResource(R.drawable.tooltip_icon_f);& this.setOnTouchListener(onTouchListener);&}&&private OnTouchListener onTouchListener=new OnTouchListener() {
& private boolean isMove=& private Rect mViewRect=new Rect();& & @Override& public boolean onTouch(View v, MotionEvent event) {
& &switch(event.getAction())& &{& & case MotionEvent.ACTION_DOWN:& & &isMove=& & && & &//有些机子上调用下面代码无效//& & &int[] location = new int[2];//& & &v.getLocationOnScreen(location);//& & &mViewRect.set(location[0], location[1], location[0]+TooltipMgr.getInstance().getBallWidth(), location[1]+TooltipMgr.getInstance().getBallHeight());& & && & &int left=TooltipMgr.getInstance().src[0]-TooltipMgr.getInstance().getBallWidth()/2;& & &int top=TooltipMgr.getInstance().src[1]-TooltipMgr.getInstance().getBallHeight()/2;& & &mViewRect.set(left, top, left+TooltipMgr.getInstance().getBallWidth(), top+TooltipMgr.getInstance().getBallHeight());& & && & && & && & case MotionEvent.ACTION_MOVE:& & &if(!isMove)& & &{& & & int _lastX = (int) event.getRawX();& & & int _lastY = (int) event.getRawY()-TooltipMgr.getInstance().getStatusBarHeight(getContext());& & & if(!mViewRect.contains(_lastX, _lastY))& & & {& & & &isMove=& & & && & & &if(ToolTipConfig.iSurfaceView)& & & &{& & & & Bundle mBundle=new Bundle();& & & & mBundle.putIntArray("data", new int[]{_lastX,_lastY});& & & & TooltipMgr.getInstance().updateUI(getContext(), TooltipMgr.STATUS_BALL_DRAG, mBundle);& & & &}& & & &TooltipMgr.getInstance().updataChatBall(event);& & & }& & &}& & &else& & &{& & & &TooltipMgr.getInstance().updataChatBall(event);& & &}& & && & && & case MotionEvent.ACTION_UP:& & case MotionEvent.ACTION_CANCEL:& & && & &if(isMove)& & &{& & & TooltipMgr.getInstance().updataChatBall(event);& & &}& & &else& & &{& & & TooltipMgr.getInstance().updateUI(getContext(), TooltipMgr.STATUS_CHATWINDOW_OPEN, null);& & &}& & && &}& && }&};}
3. TooltipService& 控制悬浮框在哪些界面显示,哪些界面不显示(通过循环查询的方式)
package com.open.
import java.util.ArrayLimport java.util.L
import android.app.ActivityMimport android.app.ActivityManager.RunningTaskIimport android.app.Simport android.content.Cimport android.content.Iimport android.content.pm.ApplicationIimport android.content.pm.PackageMimport android.content.res.Cimport android.os.Himport android.os.IBimport android.text.TextUimport android.util.L
/**&* 控制悬浮框&* @author DexYang&*&*/public class TooltipService extends Service {&&private final String TAG="TooltipService";
&private ArrayList&String& mActivityList=new ArrayList&String&();&private Handler mHandler=new Handler();&&@Override&public IBinder onBind(Intent arg0) {&&}
&@Override&public void onCreate() {& Log.v(TAG, "onCreate");& super.onCreate();& TooltipMgr.getInstance().init(getApplicationContext());& mHandler.post(heartRunnable);& & try {& & ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPackageName(),PackageManager.GET_META_DATA);& & if(null!=appInfo.metaData)& & {& & &String mActivityNames=appInfo.metaData.getString("tooltipdata");& & &if(!TextUtils.isEmpty(mActivityNames))& & &{& & & String[] names=mActivityNames.split("\\|");& & & for(int i=0;i&names.i++)& & & {& & & &if(!TextUtils.isEmpty(names[i])&&!mActivityList.contains(names[i]))& & & &{& & & & mActivityList.add(names[i]);& & & & Log.v(TAG, " onCreate Activity:"+names[i]);& & & &}& & & }& & &}& & }& &} & catch (Exception e) & {& &e.printStackTrace();& }&}
&@Override&public void onDestroy() {& Log.v(TAG, "onDestroy");& mHandler.removeCallbacks(heartRunnable);& super.onDestroy();&}
&@Override&public int onStartCommand(Intent intent, int flags, int startId) {& & if(null!=intent)& {& &int command=intent.getIntExtra("command", TooltipMgr.STATUS_OPEN);& &if(command==TooltipMgr.STATUS_MESSAGE_ADD)& &{& & int size=TooltipMgr.getInstance().getMessageList().size();& & if(size==0)& & {& & &command=TooltipMgr.STATUS_MESSAGE_NULL2FULL;& & }& & else & & {& & &command=TooltipMgr.STATUS_MESSAGE_ADD;& & }& & & & TooltipMgr.getInstance().updateData(intent.getExtras());& & if(isContain())& & {& & &TooltipMgr.getInstance().updateUI(getApplicationContext(),command,intent.getExtras());& & }& &}& &else if(command==TooltipMgr.REGISTER_TOOLTIP)& &{& & String mActivity=intent.getExtras().getString("data");& & if(!TextUtils.isEmpty(mActivity)&&!mActivityList.contains(mActivity))& & {& & &mActivityList.add(mActivity);& & }& &}& &else if(command==TooltipMgr.STATUS_CLOSE)& &{& & TooltipMgr.getInstance().closeWindows();& & mHandler.post(heartRunnable);& & this.stopSelf();& &}& }& return super.onStartCommand(intent, flags, startId);&}&&/**& * 检测当前界面是不是要显示的界面& */&private Runnable heartRunnable=new Runnable() {& & @Override& public void run() {//& &Log.v(TAG, "heartRunnable");& &if(isContain())& &{& & if(TooltipMgr.getInstance().isChatBallAddWindow||TooltipMgr.getInstance().isChatUIAddWindow||TooltipMgr.getInstance().isAnimViewAddWindow)& & {& & && & }& & else& & {& & &TooltipMgr.getInstance().onResume();& & }& &}& &else& &{& & TooltipMgr.getInstance().onPause();& &}& &mHandler.postDelayed(this, 1000L);& }&};&&private boolean isContain()&{& ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);& List&RunningTaskInfo& mRunningTaskInfo = mActivityManager.getRunningTasks(1);& String topActivityName=mRunningTaskInfo.get(0).topActivity.getClassName();& boolean isContain=& for(int i=0;i&mActivityList.size();i++)& {& &isContain=mActivityList.contains(topActivityName);& &if(isContain)& &{& && &}& }& return isC&}&&@Override&public void onConfigurationChanged(Configuration newConfig) {& Log.v(TAG, "onConfigurationChanged()");& if(TooltipMgr.getInstance().getScreenWidth()!=TooltipMgr.getInstance().getWindowManager().getDefaultDisplay().getWidth())& {& &TooltipMgr.getInstance().onConfigurationChanged();& }& super.onConfigurationChanged(newConfig);&}&}
4.TooltipMgr , 控制悬浮框的具体行为
package com.open.
import java.lang.ref.SoftRimport java.lang.reflect.Fimport java.util.ArrayLimport java.util.HashM
import android.annotation.SuppressLimport android.content.Cimport android.graphics.Bimport android.graphics.BitmapFimport android.graphics.Cimport android.graphics.Cimport android.graphics.Pimport android.graphics.PixelFimport android.graphics.RectF;import android.graphics.drawable.Dimport android.os.Bimport android.os.Himport android.util.Limport android.view.Gimport android.view.MotionEimport android.view.Vimport android.view.WindowM
import com.open.data.ChatMimport com.open.tooltip.anim.BallDragAimport com.open.tooltip.anim.BallDragAnimTimport com.open.tooltip.anim.IAimport com.open.tooltip.anim.MessageDragAimport com.open.tooltip.anim.TransAimport com.open.tooltip.anim.TransAnimS
/**&* 管理布局&* @author DexYang&*&*/public class TooltipMgr {&&private final String TAG="TooltipMgr";&&public static final int STATUS_OPEN=1;//开始悬浮框&public static final int STATUS_CLOSE=2;//关闭所有悬浮窗&&public static final int STATUS_MESSAGE_NULL2FULL=3;//无消息&有消息&public static final int STATUS_MESSAGE_ADD=4;//有消息&添加了新消息&public static final int STATUS_MESSAGE_FULL2NULL=5;//有消息&无消息&&public static final int STATUS_CHATWINDOW_OPEN=6;//点击小球,动画开始&public static final int STATUS_CHATWINDOW_EXPAND=7;//有消息,展开聊天窗口&public static final int STATUS_CHATWINDOW_DRAWBACK=8;//点击对话框,收缩&public static final int STATUS_CHATWINDOW_CLOSE=9;//有消息,收缩&public static final int STATUS_CHATWINDOW_DRAWBACK_MESSAGE_NULL=10;//点击对话框,收缩&&public static final int STATUS_BALL_DRAG=11;//拖拽小球&public static final int STATUS_BALL_DRAG_END=12;//拖拽小球,松手&public static final int STATUS_BALL_DRAG_END_BALL=13;//松手后,从新显示小球&&public static final int STATUS_RECRODING_START=14;//录音&public static final int STATUS_RECRODING_STOP=15;//录音&&public static final int STATUS_MESSAGE_DRAG=16;//拖拽消息头像&public static final int STATUS_MESSAGE_DRAG_DELETE_SUCCESS=17;//拖拽消息头像结束,并且删除成功&public static final int STATUS_MESSAGE_DRAG_DELETE_FAILED=18;//拖拽消息头像结束,并且删除失败&public static final int STATUS_MESSAGE_DRAG_FLASHVIEW_END=19;//拖拽消息头像结束&&public static final int REGISTER_TOOLTIP=100;//注册悬浮框,注册过才可以显示
&private WindowManager mWindowM&&private ChatBall mChatB//小球&private ChatUI mChatUI;//聊天框&private AnimSurfaceView mAnimSurfaceV//小球动画&private AudioRecordMicView mMicImageV//麦克风&&private WindowManager.LayoutParams mChatBallP&private WindowManager.LayoutParams mChatUIP&private WindowManager.LayoutParams mAnimSurfaceViewP&private WindowManager.LayoutParams mMicP&&public boolean isChatBallAddWindow=//小球是否添加了&public boolean isChatUIAddWindow=//对话框是否添加了&public boolean isAnimViewAddWindow=//动画View是否添加了&& & private int statusBarH//系统状态栏的高度 & & private int screenW& & & private int screenH& & & private int mBallW& & & private int mBallH& & public int []src=new int[]{0,0};//初始点&private final int []des=new int[]{0,0};//左上角
&private BallDragAnim mBallDragAnim=&private MessageDragAnim mMessageDragAnim=&private ArrayList&DrawMessage& msgArrayList=new ArrayList&DrawMessage&(5);&private Context mC&private Handler mH&private static TooltipM&&private TooltipMgr(){}&&public static TooltipMgr getInstance()&{& if(null==instance)& {& &instance=new TooltipMgr();& }&&}&&public void init(Context context)&{& if(null==mContext)& {& &mContext=context.getApplicationContext();& &mWindowManager=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);& &mHandler=new Handler(context.getMainLooper());& && &Drawable ballIcon=context.getResources().getDrawable(R.drawable.tooltip_icon_nf);& &mBallWidth=ballIcon.getIntrinsicWidth();& &mBallHeight=ballIcon.getIntrinsicHeight();& && &des[0]=mBallWidth/2;& &des[1]=mBallHeight/2;& && &src[0]=getScreenWidth()-mBallWidth/2;& &src[1]=(getScreenHeight()-getStatusBarHeight(context))/2;& && &getChatBallParam(context);& &getChatUIParam(context);& }&}&&public WindowManager getWindowManager()&{& return mWindowM&}&& & public int getStatusBarHeight(Context context) & & {& & & & & if (statusBarHeight == 0) & & & & {& & & & & & & try {& & & & & & & & & Class&?& c = Class.forName("com.android.internal.R$dimen");& & & & & & & & & Object o = c.newInstance();& & & & & & & & & Field field = c.getField("status_bar_height");& & & & & & & & & int x = (Integer) field.get(o);& & & & & & & & & statusBarHeight = context.getResources().getDimensionPixelSize(x);& & & & & & & } catch (Exception e) {& & & & & & & & & e.printStackTrace();& & & & & & & }& & & & & }& & & & & return statusBarH& & & } & & & & public int getScreenWidth()& & {& if(screenWidth==0)& {& &screenWidth =mWindowManager.getDefaultDisplay().getWidth();& &screenHeight=mWindowManager.getDefaultDisplay().getHeight();& }& & &return screenW& & }& & & & public int getScreenHeight()& & {& & &if(screenHeight==0)& & &{& & & screenWidth =mWindowManager.getDefaultDisplay().getWidth();& & & screenHeight=mWindowManager.getDefaultDisplay().getHeight();& & &}& & &return screenH& & }& & & & public int getBallWidth()& & {& & &return mBallW& & }& & & & public int getBallHeight()& & {& & &return mBallH& & }& & & & public ArrayList&DrawMessage& getMessageList()& & {& & &return msgArrayL& & }& & &public AnimSurfaceView getWindowFlashBall()&{& return mAnimSurfaceV&}
&private int _lastX;&private int _lastY;&public void updataChatBall(MotionEvent event) &{& if(ToolTipConfig.isUseSurfaceView)& {& &if(null!=mBallDragAnim)& &{& & mBallDragAnim.reflesh(event);& &}& }& else& {& &int _newlastX=(int) event.getRawX();& &int _newlastY=(int) event.getRawY()-getStatusBarHeight(mContext);& &boolean isDragEnd=(event.getAction()==MotionEvent.ACTION_UP)||(event.getAction()==MotionEvent.ACTION_CANCEL);& &if(Math.abs(_lastX-_newlastX)&8||Math.abs(_lastY-_newlastY)&8)& &{& & _lastX=_newlastX;& & _lastY=_newlastY;& & & & mChatBallParams.x = _lastX-des[0];& & mChatBallParams.y = _lastY-des[1];& & mWindowManager.updateViewLayout(mChatBall,mChatBallParams);& &}& && &if(isDragEnd)& &{& & _lastX=_newlastX;& & _lastY=_newlastY;& & & & int []src=new int[]{_lastX,_lastY};& & final int []des=new int[]{0,0};& & & & _lastX=(_lastX&=(TooltipMgr.getInstance().getScreenWidth())/2)?TooltipMgr.getInstance().getBallWidth()/2:TooltipMgr.getInstance().getScreenWidth()-TooltipMgr.getInstance().getBallWidth()/2;& & if(_lastY&TooltipMgr.getInstance().getBallHeight()/2)& & {& & &_lastY=TooltipMgr.getInstance().getBallHeight()/2;& & }& & else if(_lastY&TooltipMgr.getInstance().getScreenHeight()-TooltipMgr.getInstance().getBallHeight()/2)& & {& & &_lastY=TooltipMgr.getInstance().getScreenHeight()-TooltipMgr.getInstance().getBallHeight()/2;& & }& & & & des[0]=_lastX;& & des[1]=_lastY;& & & & if(null!=trimRunnable)& & {& & &trimRunnable.stopRun();& & }& & trimRunnable=new TransAnimRunnable(src, des,200,new Runnable() {& & && & &@Override& & &public void run() {& & & & & & mChatBallParams.x = des[0]-TooltipMgr.this.des[0];& & & mChatBallParams.y = des[1]-TooltipMgr.this.des[1];& & & & & & mWindowManager.updateViewLayout(mChatBall,mChatBallParams);& & & TooltipMgr.this.src=& & &}& & });& & mHandler.post(trimRunnable);& &}& }&}
&public void updateDraggedMessage(MotionEvent event) &{& if(null!=mMessageDragAnim)& {& &mMessageDragAnim.reflesh(event);& }&}
&public boolean isDraggedMessageDelete(MotionEvent event)&{& if(null!=mMessageDragAnim)& {& &return mMessageDragAnim.isDelete(event);& }&&}&&private HashMap&String, SoftReference&Bitmap&& cache=new HashMap&String, SoftReference&Bitmap&&();&public Bitmap[] getHeadBitmaps()&{& int size=Math.min(3, msgArrayList.size());& Bitmap[] ret=new Bitmap[size];& B& for(int i=0;i&ret.i++)& {& &String url=msgArrayList.get(i).msgList.get(0).getAvatar();& && &SoftReference&Bitmap& ref = cache.get(url);& & & & & & if ( ref != null ) & & & & & & {& & & & & & &bmp =ref.get();& & & & & & & & if (bmp == null)& & & & & & & & {& & & & & & & & &cache.remove(url);& & & & & & & & }& & & & & & & & else& & & & & & & & {& & & & & & & & &cache.put(url, new SoftReference&Bitmap&(bmp));& & & & & & & & &ret[i]=& & & & & & & & && & & & & & & & }& & & & & & }& & ret[i]=getFromAssetBitmap(msgArrayList.get(i).msgList.get(0).getAvatar());& &if(null!=ret[i])& &{& & cache.put(url, new SoftReference&Bitmap&(ret[i]));& &}& }&&}&&public Bitmap getBitmap(String url)&{& Bitmap bmp=& SoftReference&Bitmap& ref = cache.get(url);& & & & if ( ref != null ) & & & & {& & & & &bmp =ref.get();& & & & & & if (bmp == null)& & & & & & {& & & & & & &cache.remove(url);& & & & & & }& & & & & & else& & & & & & {& & & & & & &cache.put(url, new SoftReference&Bitmap&(bmp));& & & & & & && & & & & & }& & & & }& & & & bmp=getFromAssetBitmap(url);& & & & & if(null!=bmp)& {& &cache.put(url, new SoftReference&Bitmap&(bmp));& }&&}&&public Bitmap getFromAssetBitmap(String fileName){ & & & &
try { & & & & & & & return BitmapFactory.decodeStream(mContext.getResources().getAssets().open(fileName));& & & &
} catch (Exception e) { & & & & & &
e.printStackTrace(); & & & &
}& & & && } &&@SuppressLint("DefaultLocale")&public Bitmap getCircleBitmap(int width,int height)&{& String url=String.format("image:width:%d_height:%d",width,height);& Bitmap bmp=& SoftReference&Bitmap& ref = cache.get(url);& & & & if ( ref != null ) & & & & {& & & & &bmp =ref.get();& & & & & & if (bmp == null)& & & & & & {& & & & & & &cache.remove(url);& & & & & & }& & & & & & else& & & & & & {& & & & & & &cache.put(url, new SoftReference&Bitmap&(bmp));& & & & & & && & & & & & }& & & & }& & & & bmp=makeDst(width, height);& if(null!=bmp)& {& &cache.put(url, new SoftReference&Bitmap&(bmp));& }&&}&&private Bitmap makeDst(int w, int h) & & {& & & & Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);& & & & Canvas c = new Canvas(bm);& & & & Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);& & & & p.setColor(Color.parseColor("#ffffffff"));&
& & & & c.drawOval(new RectF(0, 0, w, h), p);& & & && & }&&public void updateData(Bundle mBundle)&{& ChatMsg msg=(ChatMsg)mBundle.getSerializable("data");& & boolean isFind=& for(int i=0;i&msgArrayList.size();i++)& {& &if(msgArrayList.get(i).uid==msg.getFriendId())& &{& & msgArrayList.get(i).msgList.add(msg);& & msgArrayList.get(i).unReadCount++;& & isFind=& && &}& }& & if(!isFind)& {& &DrawMessage dm=new DrawMessage();& &dm.msgList.add(msg);& &dm.uid=msg.getFriendId();& &dm.unReadCount++;& &msgArrayList.add(0,dm);& }&}&&public void updateUI(final Context context,final int status,final Bundle mBundle)&{Log.v(TAG,"updateUI status:"+status);
& mHandler.post(new Runnable(){
& &@Override& &public void run() {& & & & if(status==STATUS_OPEN)& & {& & &hideChatUI();& & &hideAnimView();& & &showChatBall(context);& & &mChatBall.setImageResource(R.drawable.tooltip_icon_f);& & }& & else if(status==STATUS_MESSAGE_NULL2FULL)& & {& & &hideChatUI();& & &hideAnimView();& & &showChatBall(context);& & &mChatBall.setImageResource(R.drawable.tooltip_icon_f);& & }& & else if(status==STATUS_MESSAGE_ADD)& & {& & &if(isChatUIAddWindow)& & &{& & & showChatUI(context);& & &}& & &else if(!isChatBallAddWindow)& & &{& & & showChatBall(context);& & &}& & &else if(isChatBallAddWindow)& & &{& & & mChatBall.setImageResource(R.drawable.tooltip_icon_f);& & & if(null!=mVirRunnable)& & & {& & & &mVirRunnable.stopRun();& & & }& & & mVirRunnable=new VirRunnable();& & & mHandler.post(mVirRunnable);& & &}& & }& & else if(status==STATUS_MESSAGE_FULL2NULL)& & {& & && & }& & else if(status==STATUS_CHATWINDOW_OPEN)//点击小球,开始动画OK& & {& & &if(ToolTipConfig.isUseSurfaceView)& & &{& & & hideChatBall();& & & hideChatUI();& & & & & & IAnimation anim=ToolTipConfig.isAnimWithShadow?new TransAnimShadow(context, src, des, status, 250, getHeadBitmaps()):new TransAnim(context, src, des, status, 300);& & & showAnimView(context, anim);& & &}& & &else& & &{& & & hideChatUI();& & & if(null!=trimRunnable)& & & {& & & &trimRunnable.stopRun();& & & }& & & trimRunnable=new TransAnimRunnable(src, des,showChatUIRunnable);& & & mHandler.post(trimRunnable);& & &}& & }& & else if(status==STATUS_CHATWINDOW_EXPAND)//小球动画完毕,显示聊天窗口 OK& & {& & &hideChatBall();& & &hideAnimView();& & && & &showChatUI(context);& & }& & else if(status==STATUS_CHATWINDOW_DRAWBACK||status==STATUS_CHATWINDOW_DRAWBACK_MESSAGE_NULL)//点击聊天窗口下半部分,收缩 OK& & {& & &if(ToolTipConfig.isUseSurfaceView)& & &{& & & hideAnimView();& & & hideChatBall();& & & hideChatUI();& & & & & & mChatBall.setImageResource(R.drawable.tooltip_icon_nf);& & & IAnimation anim=ToolTipConfig.isAnimWithShadow?new TransAnimShadow(context, des, src, status, 250, getHeadBitmaps()):new TransAnim(context, des, src, status, 300);& & & showAnimView(context, anim);& & &}& & &else& & &{& & & hideAnimView();& & & hideChatUI();& & & showChatBall(mContext);& & & mChatBall.setImageResource(R.drawable.tooltip_icon_nf);& & & & & & if(null!=trimRunnable)& & & {& & & &trimRunnable.stopRun();& & & }& & & trimRunnable=new TransAnimRunnable(des, src,showChatBallRunnable);& & & mHandler.post(trimRunnable);& & &}& & }& & else if(status==STATUS_CHATWINDOW_CLOSE)//收缩动画完成,显示原来位置OK& & {& & &hideChatUI();& & &hideAnimView();& & && & &showChatBall(context);& & }& & else if(status==STATUS_BALL_DRAG)//拖动小球OK& & {& & &hideChatBallInvisible();& & &hideChatUI();& & && & &mBallDragAnim=ToolTipConfig.isAnimWithShadow?new BallDragAnimTail(context, getHeadBitmaps()):new BallDragAnim(context);& & &showAnimView(context, mBallDragAnim);& & }& & else if(status==STATUS_BALL_DRAG_END_BALL)//拖动小球结束OK& & {& & &hideAnimView();& & && & &int []_src=mBundle.getIntArray("data");//重置位置& & &retSettingBall(context, _src);& & &showChatBall(context);& & }& & else if(status==STATUS_RECRODING_START)& & {& & &showMicView(context);& & }& & else if(status==STATUS_RECRODING_STOP)& & {& & &hideMicView();& & }& & else if(status==STATUS_MESSAGE_DRAG)//OK& & {& & &int []viewLocation=mBundle.getIntArray("data");& & &String headPath=mBundle.getString("data1");& & &mMessageDragAnim=new MessageDragAnim(context, getScreenWidth(), getScreenHeight()-getStatusBarHeight(context),viewLocation, getBitmap(headPath));& & &showAnimView(context, mMessageDragAnim);& & }& & else if(status==STATUS_MESSAGE_DRAG_FLASHVIEW_END)//OK& & {& & &hideAnimView();& & }& & else if(status==STATUS_CLOSE)& & {& & &closeWindows();& & }& &}& });
&}&&private WindowManager.LayoutParams getChatBallParam(Context context)&{& if(null==mChatBallParams)& {& && & & &
/* & & & & & *& params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;& 通知栏处也可见& & & & & * 如果设置为params.type = WindowManager.LayoutParams.TYPE_PHONE; & & & & & * 那么优先级会降低一些, 即拉下通知栏不可见 & & & & & */& & & & &
& &mChatBallParams=new WindowManager.LayoutParams();& &mChatBallParams.type=WindowManager.LayoutParams.TYPE_PHONE;& &mChatBallParams.format = PixelFormat.RGBA_8888; // 设置图片格式,效果为背景透明& &mChatBallParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
& &mChatBallParams.width=getBallWidth();& &mChatBallParams.height=getBallHeight();& &mChatBallParams.gravity=Gravity.LEFT| Gravity.TOP; // 调整悬浮窗口至右侧中间& &mChatBallParams.x=src[0]-des[0];& & &mChatBallParams.y=src[1]-des[1];& && &mChatBallParams.softInputMode=WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;& }& return mChatBallP&}&&private WindowManager.LayoutParams getChatUIParam(Context context)&{& if(null==mChatUIParams)& {& &mChatUIParams=new WindowManager.LayoutParams();& &mChatUIParams.type=WindowManager.LayoutParams.TYPE_PHONE;& &mChatUIParams.format = PixelFormat.RGBA_8888; // 设置图片格式,效果为背景透明& &mChatUIParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;& &mChatUIParams.width=WindowManager.LayoutParams.MATCH_PARENT;& &mChatUIParams.height=WindowManager.LayoutParams.MATCH_PARENT;& &mChatUIParams.gravity=Gravity.LEFT| Gravity.TOP; // 调整悬浮窗口至右侧中间& &mChatUIParams.x=0;& & &mChatUIParams.y=0;& & && &mChatUIParams.softInputMode=WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;& }& return mChatUIP&}&&private WindowManager.LayoutParams getAnimSurfaceViewParam(Context context)&{& if(null==mAnimSurfaceViewParams)& {& &mAnimSurfaceViewParams=new WindowManager.LayoutParams();& &mAnimSurfaceViewParams.type=WindowManager.LayoutParams.TYPE_PHONE;& &mAnimSurfaceViewParams.format = PixelFormat.RGBA_8888; // 设置图片格式,效果为背景透明& &mAnimSurfaceViewParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;& &mAnimSurfaceViewParams.width=WindowManager.LayoutParams.MATCH_PARENT;& &mAnimSurfaceViewParams.height=WindowManager.LayoutParams.MATCH_PARENT;& &mAnimSurfaceViewParams.gravity=Gravity.LEFT| Gravity.TOP; // 调整悬浮窗口至右侧中间& &mAnimSurfaceViewParams.x=0;& & &mAnimSurfaceViewParams.y=0; & && &mAnimSurfaceViewParams.softInputMode=WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;& }& return mAnimSurfaceViewP&}&&private WindowManager.LayoutParams getMicParam(Context context)&{& if(null==mMicParams)& {& &mMicParams=new WindowManager.LayoutParams();& &mMicParams.type=WindowManager.LayoutParams.TYPE_PHONE;& &mMicParams.format = PixelFormat.RGBA_8888; // 设置图片格式,效果为背景透明& &mMicParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;& &mMicParams.width=context.getResources().getDrawable(R.drawable.tooltip_mic_bottom).getIntrinsicWidth();& &mMicParams.height=context.getResources().getDrawable(R.drawable.tooltip_mic_bottom).getIntrinsicHeight();& &mMicParams.x = 0;& &mMicParams.y = 0;& && &mMicParams.softInputMode=WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;& }& return mMicP&}&&private void showChatBall(Context context)&{& if(null==mChatBall)& {& &mChatBall=new ChatBall(context);& }& & if(null==mChatUI)& {& &mChatUI=new ChatUI(context);& }& && if(mChatBall.getVisibility()!=View.VISIBLE)& {& &mChatBall.setVisibility(View.VISIBLE);& }& & if(!isChatBallAddWindow)& {& &mChatBallParams=getChatBallParam(context);& &mWindowManager.addView(mChatBall, mChatBallParams);& &isChatBallAddWindow=& }&}&&private void showChatUI(Context context)&{& if(null==mChatUI)& {& &mChatUI=new ChatUI(context);& }& mChatUI.onUIupdate();& & if(!isChatUIAddWindow)& {& &mChatUIParams=getChatUIParam(context);& &mWindowManager.addView(mChatUI, mChatUIParams);& &isChatUIAddWindow=& }&}&&private void showAnimView(Context context,IAnimation anim)&{& if(null==mAnimSurfaceView)& {& &mAnimSurfaceView=new AnimSurfaceView(context);& }& & if(!isAnimViewAddWindow)& {& &mAnimSurfaceViewParams=getAnimSurfaceViewParam(context);& &mWindowManager.addView(mAnimSurfaceView, mAnimSurfaceViewParams);& &isAnimViewAddWindow=& }& & mAnimSurfaceView.postAnimation(anim);&}&&private void showMicView(Context context)&{& if(null==mMicImageView)& {& &mMicImageView=new AudioRecordMicView(context);& }& mWindowManager.addView(mMicImageView, getMicParam(context));&}&&private void retSettingBall(Context context,int [] loc)&{& & loc[0]=(loc[0]&=(TooltipMgr.getInstance().getScreenWidth())/2)?TooltipMgr.getInstance().getBallWidth()/2:TooltipMgr.getInstance().getScreenWidth()-TooltipMgr.getInstance().getBallWidth()/2;& if(loc[1]&TooltipMgr.getInstance().getBallHeight()/2)& {& &loc[1]=TooltipMgr.getInstance().getBallHeight()/2;& }& else if(loc[1]&TooltipMgr.getInstance().getScreenHeight()-TooltipMgr.getInstance().getBallHeight()/2)& {& &loc[1]=TooltipMgr.getInstance().getScreenHeight()-TooltipMgr.getInstance().getBallHeight()/2;& }& & mChatBallParams.x=loc[0]-des[0];& mChatBallParams.y= (loc[1]-des[1]);& & mChatBall.setVisibility(View.VISIBLE);& mWindowManager.updateViewLayout(mChatBall,mChatBallParams);& isChatBallAddWindow=& & src=&}&&private void hideChatBallInvisible()&{& mChatBall.setVisibility(View.INVISIBLE);&}&&private void hideChatBall()&{& if(isChatBallAddWindow)& {& &if(null!=mChatBall)& &{& & mWindowManager.removeView(mChatBall);& &}& &isChatBallAddWindow=& }&}&&private void hideChatUI()&{& if(isChatUIAddWindow)& {& &if(null!=mChatUI)& &{& & mWindowManager.removeView(mChatUI);& &}& &isChatUIAddWindow=& }&}&&private void hideAnimView()&{& if(isAnimViewAddWindow)& {& &if(null!=mAnimSurfaceView)& &{& & mWindowManager.removeView(mAnimSurfaceView);& &}& &isAnimViewAddWindow=& }&}&&private void hideMicView()&{& if(null!=mMicImageView)& {& &mWindowManager.removeView(mMicImageView);& &mMicImageView=& }&}&&public void closeWindows()&{& hideChatBall();& hideChatUI();& hideAnimView();& hideMicView();& & mChatBall=& mChatUI=& mAnimSurfaceView=& mMicImageView=& & msgArrayList.clear();&}&&public void onResume()&{& if(status.isChatBallAddWindow)& {& &hideChatUI();& &hideAnimView();& && &showChatBall(mContext);& }& else if(status.isChatUIAddWindow)& {& &hideChatBall();& &hideAnimView();& && &showChatUI(mContext);& }& else& {& &hideChatUI();& &hideAnimView();& && &if(getMessageList().size()&0)& &{& & showChatBall(mContext);& &}& }&}&&public void onPause()&{& if(isChatBallAddWindow||isChatUIAddWindow||isAnimViewAddWindow)& {& &status.isChatBallAddWindow=isChatBallAddW& &status.isChatUIAddWindow=isChatUIAddW& &status.isAnimViewAddWindow=isAnimViewAddW& & &hideChatBall();& &hideChatUI();& &hideAnimView();& && &isChatBallAddWindow=& &isChatUIAddWindow=& &isAnimViewAddWindow=& }
&}&& & public void onConfigurationChanged()& & {& & &float oldHPrecent=(float)src[1]/(float)(screenHeight-getStatusBarHeight(mContext));& & && & &int tmp=screenW& & &screenWidth=screenH& & &screenHeight=& & && & &if(src[0]&getBallWidth()/2)//在右边& & &{& & & src[0]=screenWidth-getBallWidth()/2;& & & getChatBallParam(mContext).x=src[0]-des[0];& & & &}& & &src[1]=(int)(oldHPrecent*(screenHeight-getStatusBarHeight(mContext)));& & &getChatBallParam(mContext).y=src[1]-des[1];& & & && & &if(isChatBallAddWindow)& & &{& & & mWindowManager.updateViewLayout(mChatBall,getChatBallParam(mContext));& & &}& & && & &Log.v(TAG,"onConfigurationChanged sWidth:"+screenWidth+" sHeight:"+screenHeight);& & && & &//重新设置Src的值,与parms的值& & && & &if(null!=mChatUI)& & &mChatUI.onUIupdate();& & }&& & private VirRunnable mVirR&private TransAnimRunnable trimR&private Runnable showChatUIRunnable=new Runnable() {& & @Override& public void run() {& &hideChatBall();& &showChatUI(mContext);& }&};&private Runnable showChatBallRunnable=new Runnable() {& & @Override& public void run() {& && &if(getMessageList().size()==0)& &{& & hideChatBall();& &}& }&};&private TooltipState status=new TooltipState();&&public class DrawMessage&{&& public ArrayList&ChatMsg& msgList=new ArrayList&ChatMsg&(1);& public int unReadC&}&&private class TooltipState&{& public boolean isChatBallAddWindow=//小球是否添加了& public boolean isChatUIAddWindow=//对话框是否添加了& public boolean isAnimViewAddWindow=//动画View是否添加了&}&&private class VirRunnable implements Runnable&{&& final int oldX;&& p& & public VirRunnable()& {& &end=System.currentTimeMillis()+500;& &oldX=(mChatBallParams.x=(src[0]&=(getScreenWidth())/2)?0:getScreenWidth()-getBallWidth());& &amplitude=DensityUtil.dip2px(mContext, 25);& }& & public void stopRun()& {& &mHandler.removeCallbacks(this);& &getChatBallParam(mContext).x=oldX;& &mWindowManager.updateViewLayout(mChatBall,mChatBallParams);& }& & @Override& public void run() {& && &if((run=(end-System.currentTimeMillis()))&0)& &{& & int value=(int) (amplitude*Math.sin((float)4*Math.PI*(500-run)/(float)500));& & if(oldX&0&&value&0||oldX&=0&&value&0)& & {& & &value=(-value);& & }& & & & getChatBallParam(mContext).x=oldX-& & mWindowManager.updateViewLayout(mChatBall,mChatBallParams);& & mHandler.postDelayed(this, 20);& &}& &else& &{& & mHandler.removeCallbacks(this);& & getChatBallParam(mContext).x=oldX;& & mWindowManager.updateViewLayout(mChatBall,mChatBallParams);& &}& }&}&&private class TransAnimRunnable implements Runnable&{&&& private int duration=300;&& private long runMills=0;& private int[] srcL& private int[] desL& private R& & public TransAnimRunnable(int[] srcLoc, int[] desLoc, Runnable run) {& &this(srcLoc, desLoc, 300, run);& }& & public TransAnimRunnable(int[] srcLoc, int[] desLoc, int duration ,Runnable run) {& &this.srcLoc=srcL& &this.desLoc=desL& &dx=desLoc[0]-srcLoc[0];& &dy=desLoc[1]-srcLoc[1];& &start=System.currentTimeMillis();& &this.duration=& &this.run =& }
& public void stopRun()& {& &mHandler.removeCallbacks(this);& }& & @Override& public void run() {& && &if((runMills=(System.currentTimeMillis()-start))&duration)& &{& & int _dx=(int)((float)runMills*dx/(float)duration);& & int _dy=(int)((float)runMills*dy/(float)duration);& & & & mChatBallParams.x = (srcLoc[0]+_dx)-des[0];& & mChatBallParams.y = (srcLoc[1]+_dy)-des[1];& & mWindowManager.updateViewLayout(mChatBall,mChatBallParams);& & & & mHandler.postDelayed(this, 20);& &}& &else& &{& & mChatBallParams.x = desLoc[0]-des[0];& & mChatBallParams.y = desLoc[1]-des[1];& & mWindowManager.updateViewLayout(mChatBall,mChatBallParams);& & & & if(null!=run)& & {& & &run.run();& & }& & mHandler.removeCallbacks(this);& &}& }&}}
5 、动画特效,请看Demo中的代码
更多Android相关信息见 专题页面 2
相关资讯 & & &
& (04/12/:22)
图片资讯 & & &
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款}

我要回帖

更多关于 android实现悬浮窗口 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信