仿iPhone辅助球实现
过年在家时,翻出来了很多去年写的代码。就挑出来了一些自我感觉良好的讲解了。
效果如图,demo中我并没有做完全和iphone那样展开的菜单(因为懒)。
那么接下来就讲讲这个能漂浮在任意Activity、包括手机桌面上的手势菜单是如何实现的。
正文
首先从宏观原理上来讲,它就是一个跑在Service中的View。 从细节上来讲,主要有两个问题需要我们注意:
如何让一个Service中能够显示出View来。
如何让这个View做到相应手势事件(包括点击和拖动)
第一个问题:这要从Activity开始讲起了。
我们都知道,Android中的View并不包括Activity、ActionBar、Dialog、Notification等。Activity能够显示出一个View,是因为我们调用了setContentView()这个方法。再往深层看,Activity之所以能显示View是因为Activity本身使用has…a…关系包含了一个Window对象,而这个Window对象可以理解为一个屏幕,如果你有做过IOS开发,你一定能理解这个Window的含义,其实就是代表了当前屏幕。
通过这个Window对象,我们可以获得一个DecorView对象,这个DecorView对象是一个ViewGroup就是当前屏幕的根View,一切Activity的ContentView都会被add进这个DecorView容器中。
再回到上一个问题,View是如何显示出来的。
在Window对象中,有一个内部类叫做LocalWindowManager,这个内部类实现了一个接口叫WindowManager,而就是这个LocalWindowManager在实现WindowManager.addView()接口方法的时候,实现了在屏幕上绘制View的功能。
原理说了这么多,你也许已经糊涂了,这到底和Service显示View有什么关系。
其实很简单,因为Window对象负责显示我们的View,那么在Service中得到当前的Window对象,一切问题就解决了。
在Context中有一个获取当前系统管理者的方法叫getSystemService(String name);我们就通过这个方法来获取到Window对象中的WindowManager而这里得到的WindowManager对象就是上文提到的LocalWindowManager对象。通过给这个对象的addView()再添加一个我们的手势球View,就解决了View显示问题。
第二个问题:让一个View随着手指移动而移动。
有两种方法实现这种需求。
在《疯狂Android讲义》中有一章是介绍通过自定义控件的形式,重写ondraw()方法来实现,这是在View内部封装,这里我不讲了,希望了解的可以自己去看看电子书。我实现的方法是通过外部给View设置触摸事件监听setOnTouchListener(),然后通过当前手指所在屏幕的坐标,来动态修改View的margin边距来达到View改变位置的效果。
代码
具体实现,我们来这个核心Service类
public class TopFloatService extends Service implements OnClickListener { WindowManager wm = null; WindowManager.LayoutParams ballWmParams = null; private float mTouchStartX; private float mTouchStartY; private float x; private float y; private View ballView; // 球状态View private View menuView; // 菜单状态View private PopupWindow pop; private Button floatImage; private RelativeLayout menuLayout; // 菜单的布局 private RelativeLayout menuTop; private boolean ismoving = false; @Override public void onCreate() { super.onCreate(); // 加载辅助球布局 ballView = View.inflate(this, R.layout.floatball, null); floatImage = (Button) ballView.findViewById(R.id.float_image); setUpFloatMenuView(); createView(); } /** * 窗口菜单初始化 */ private void setUpFloatMenuView() { menuView = View.inflate(this, R.layout.floatmenu, null); menuLayout = (RelativeLayout) menuView.findViewById(R.id.menu); menuTop = (RelativeLayout) menuView.findViewById(R.id.lay_main); menuLayout.setOnClickListener(this); menuTop.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.lay_main: Toast.makeText(getApplicationContext(), "111", Toast.LENGTH_SHORT) .show(); break; default: if (pop != null && pop.isShowing()) { pop.dismiss(); } break; } } private void createView() { wm = (WindowManager) getSystemService("window"); ballWmParams = new WindowManager.LayoutParams(); ballWmParams.type = WindowManager.LayoutParams.TYPE_PHONE; ballWmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; ballWmParams.gravity = Gravity.LEFT | Gravity.TOP; ballWmParams.x = 0; ballWmParams.y = 0; ballWmParams.width = WindowManager.LayoutParams.WRAP_CONTENT; ballWmParams.height = WindowManager.LayoutParams.WRAP_CONTENT; ballWmParams.format = PixelFormat.RGBA_8888; // 添加显示 wm.addView(ballView, ballWmParams); // 注册触摸事件监听 floatImage.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { x = event.getRawX(); y = event.getRawY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: ismoving = false; mTouchStartX = (int) event.getX(); mTouchStartY = (int) event.getY(); break; case MotionEvent.ACTION_MOVE: ismoving = true; updateViewPosition(); break; case MotionEvent.ACTION_UP: mTouchStartX = mTouchStartY = 0; break; } // 如果拖动则返回false,否则返回true if (ismoving == false) { return false; } else { return true; } } }); // 注册点击事件监听 floatImage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 获取在xml中定义的大小 DisplayMetrics dm = getResources().getDisplayMetrics(); pop = new PopupWindow(menuView, dm.widthPixels, dm.heightPixels); pop.showAtLocation(ballView, Gravity.CENTER, 0, 0); pop.update(); } }); } /** * 更新view的显示位置 */ private void updateViewPosition() { ballWmParams.x = (int) (x - mTouchStartX); ballWmParams.y = (int) (y - mTouchStartY); wm.updateViewLayout(ballView, ballWmParams); } @Override public IBinder onBind(Intent intent) { return null; } }
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:开源实验室 来源: 开源实验室
- 标签: 辅助球
- 发布时间:2016-04-02 23:10:40
- [55] Oracle MTS模式下 进程地址与会话信
- [55] IOS安全–浅谈关于IOS加固的几种方法
- [54] 如何拿下简短的域名
- [53] android 开发入门
- [52] Go Reflect 性能
- [51] 图书馆的世界纪录
- [49] 读书笔记-壹百度:百度十年千倍的29条法则
- [47] 【社会化设计】自我(self)部分――欢迎区
- [38] 程序员技术练级攻略
- [33] 视觉调整-设计师 vs. 逻辑