android 的概念对象,无穷无尽,被搞昏了头,在framework中添加一点自己的需求变得头大。为了少让别人走弯路,将软按键的实现代码分享下,不要让人再做无谓的牺牲。fuck android framework!
在网络上有人把软案件做到StatusBar上面,目前我们有个需求就是按下案件弹出一个浮在最上面的窗口,窗口里面有各种案件的按钮,home,back,menu,search,...再按下,窗口消失。
目前控制软按键窗口弹出隐藏的我们用KEYRECODE_STAR,你也可以根据你的实际情况自己定制。
因为android APP的窗口不能满足需求,所以加在了framework上面,想让把软按键做成一个独立AP的同学可以不用往下看了。
选了几种模型,如输入法,statusBar,能满足需求,那么还是决定仿造statusBar来增加软按键。(因为他足够简单,输入法框架没看懂)
看完了 /home/hd/codings/mipsandroid-2.1/frameworks/base/services/java/com/android/server/status 中的两个文件StatusBarService.java,StatusBarView.java两个文件,那么就是我们的实现思路。并结合SystemServer.java中statusBar的创建。
首先系统启动的时候由Zygote启动SystemServer里面的服务,初始化 StatusBarService,然后并调用它的systemReady函数,StatusBarService systemReady创建了status Bar的窗口:1:create View;2 add to winMan,那么StatusBarView.java就显示出来了。
(android View概念很麻烦,什么ViewGroup 主view,至今我没搞懂)
然后StatusBarView.java 中就是可以对你想要的时间进行响应和重载了。
大致了解了后我们就来仿StatusBar来实现我们的touchkey:
mkdir touchkey 和status在同一个路径下,然后创建两个java文件TouchkeyService.java,TouchkeyView.java,其中TouchkeyService继承了IStatusBar.Stub,因为我要在其他的进程来获取它的句柄来控制他,而我有没看懂IStatusBar.Stub这类东西怎么写,(概念多而杂fuck android framework)当你继承这个IStatusBar.Stub对象的时候一定要重载removeIcon updateIcon addIcon disable toggle deactivate activate 方法,这个过程编译起会辅导你完成,缺拿个就补哪个,函数体什么也不做。嘿嘿,看到deactivate,activate我们就可以通过重写这两个方法来控制我的软按键的显示和隐藏。
最后别完了systemReady 的方法把TouchkeyView.java中的TouchkeyView 加入win manager,在TouchkeyService的构造函数中创建
TouchkeyView,并配置其相应的xml文件来做TouchkeyView的布局,我们的具体按键都布局在这个touch_key.xml里面。
public TouchkeyService(Context context) { mContext = context; mDisplay = ((WindowManager)context.getSystemService( Context.WINDOW_SERVICE)).getDefaultDisplay(); TouchkeyView sb = (TouchkeyView)View.inflate(context,com.android.internal.R.layout.touch_key, null); sb.mService=this; mTouchkeyView=sb; mPixelFormat = PixelFormat.TRANSLUCENT; Drawable bg = sb.getBackground(); if (bg != null) { mPixelFormat = bg.getOpacity(); } } public void systemReady() { int w,h; final TouchkeyView view = mTouchkeyView; final View viewBase=(View)mTouchkeyView; WindowManager.LayoutParams lp = new WindowManager.LayoutParams( WIDTH, HEIGHT, WindowManager.LayoutParams.TYPE_PRIORITY_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,mPixelFormat); // lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; lp.setTitle("touchkey"); w=mDisplay.getWidth(); h=mDisplay.getHeight(); lp.x=((w-WIDTH)/2); lp.y=0; lp.windowAnimations = R.style.Animation_Translucent; WindowManagerImpl.getDefault().addView(view, lp); mTouchkeyView.setVisibility(4); }
有了这两个函数和刚才的touch_key.xml文件,你就有了一个你特定大小的窗口浮动在你的右边。
在开始的时候这个窗口是隐藏的(mTouchkeyView.setVisibility(4);),那么我们去到 framework policy 下面的PhoneWindowManager.java,因为那里有按键的时间,我们在那边用类似于获取StatusBar句柄(暂前叫句柄android概念ZTMD麻烦,fuck)的方法来获取我们的TouchkeyService,然后调用 activate deactivate 方法,那么就可以控制到这个窗口的显示。
在 PhoneWindowManager.java代码中(如果你是MID,自己知道是拿个吧) 修改方法interceptKeyTi(别问我是怎么掉它的,反正我知道是WM掉它,然后再掉回去的),在这个函数任意个你看上去顺眼的地方加入
if(code==KeyEvent.KEYCODE_STAR) { if(down) { IStatusBar touchkey = IStatusBar.Stub.asInterface(ServiceManager.getService("touchkey")); if(touchkey!=null) { if(mTouchKeyHiding) { try { touchkey.activate(); } catch (RemoteException e) { // we're screwed anyway, since it's in this process throw new RuntimeException(e); } } else { try { touchkey.deactivate(); } catch(RemoteException e) { throw new RuntimeException(e); } } mTouchKeyHiding=!mTouchKeyHiding; } } return true; }
这个时候体会到为什么要继承IStatusBar.Stub它了吧,(搞懂android 的概念怎么就那么烦呢?fuck!!)
直到这里你就可以完成按下按钮发送了个STAR消息,显示和隐藏你的touchkey 窗口。
工作完成了一大半,剩下的只是在这个模型上去加功能了。
还有一个问题要主义,所有的控制界面显示的不能由调用者的线程去完成,而必须发送消息,给创建它的线程,我们这里借助Handle,所以还要Handler mHandler = new Handler() { public void handleMessage(Message msg) { int down; down=msg.arg1; switch(msg.what) { case TOUCHKEY_HIDE: mTouchkeyView.setVisibility(4); break; case TOUCHKEY_SHOW: mTouchkeyView.setVisibility(0); break; case TOUCHKEY_HOME: sendKeyEventToAndroid(KeyEvent.KEYCODE_HOME,down); break; case TOUCHKEY_BACK: sendKeyEventToAndroid(KeyEvent.KEYCODE_BACK,down); break; case TOUCHKEY_SEACH: sendKeyEventToAndroid(KeyEvent.KEYCODE_SEARCH,down); break; case TOUCHKEY_MENU: sendKeyEventToAndroid(KeyEvent.KEYCODE_MENU,down); break; case TOUCHKEY_VADD: sendKeyEventToAndroid(KeyEvent.KEYCODE_VOLUME_UP,down); break; case TOUCHKEY_VSUB: sendKeyEventToAndroid(KeyEvent.KEYCODE_VOLUME_DOWN,down); break; case TOUCHKEY_RCENT: sendKeyEventToAndroid(KeyEvent.KEYCODE_RECENTAPP,down); break; } } };
这样,调用着发送消息给view所在的线程,然后由他来处理,并响应消息,别问我是为什么,我也不知道,也没看并白为什么一定要这样做。
你想要几个按钮那么清在view对应的xml文件里添加ImageButton,然后在TouchkeyView.java中来获取他们,并重载他们的onClick消息:
然后调用TouchkeyService sendKeyEvent来发送响应的消息:
TouchkeyView.java
homeKey.setOnClickListener(new OnClickListener(){ public void onClick(View arg0) { mService.sendKeyEvent(KeyEvent.KEYCODE_HOME,1); } });
TouchkeyService.java
public void sendKeyEvent(int keyCode,int down) { Message m = new Message(); m.arg1=down; switch(keyCode) { case KeyEvent.KEYCODE_HOME: m.what = TOUCHKEY_HOME; // mHandler.sendMessageDelayed(m,10); mHandler.sendMessage(m); break; case KeyEvent.KEYCODE_BACK: m.what = TOUCHKEY_BACK; // mHandler.sendMessageDelayed(m,10); mHandler.sendMessage(m); break; case KeyEvent.KEYCODE_SEARCH: m.what = TOUCHKEY_SEACH; // mHandler.sendMessageDelayed(m,10); mHandler.sendMessage(m); break; case KeyEvent.KEYCODE_MENU: m.what = TOUCHKEY_MENU; // mHandler.sendMessageDelayed(m,10); mHandler.sendMessage(m); break; case KeyEvent.KEYCODE_VOLUME_DOWN: m.what = TOUCHKEY_VSUB; // mHandler.sendMessageDelayed(m,10); mHandler.sendMessage(m); break; case KeyEvent.KEYCODE_VOLUME_UP: m.what = TOUCHKEY_VADD; // mHandler.sendMessageDelayed(m,10); mHandler.sendMessage(m); break; case KeyEvent.KEYCODE_RECENTAPP: m.what = TOUCHKEY_RCENT; // mHandler.sendMessageDelayed(m,10); mHandler.sendMessage(m); break; } }
然后同样借助handle来更新UI,并发送消息
private void sendKeyEventToAndroid(int eventCode,int downPress) { long now = SystemClock.uptimeMillis(); Log.d("[hd debug]","sendKeyEventToAndroid begin/n"); try { /*if(downPress==1) */{ KeyEvent down = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, eventCode, 0); (IWindowManager.Stub .asInterface(ServiceManager.getService("window"))) .injectKeyEvent(down, true); } /*else */{ KeyEvent up = new KeyEvent(now, now, KeyEvent.ACTION_UP, eventCode, 0); (IWindowManager.Stub .asInterface(ServiceManager.getService("window"))) .injectKeyEvent(up, true); } } catch (RemoteException e) { Log.i("Input", "DeadOjbectException"); } Log.d("[hd debug]","sendKeyEventToAndroid end/n"); }
代码有是抄写了input.java的,别问我是为什么。
这样就基本完成了你要的软按键的功能。
代码和xml除图片外我会以资源的形式给出来,如果你喜欢独立来做,那么你可以参考思路自己实现,不想的话就去下载来用吧,不过不好意思你的贡献2点资源分。
我建议你舍去2点资源分吧,因为android 的设计太过于复杂,概念太过于繁多,对于那些大吹android设计好的家伙,我不认同,违背了kiss原则,如果能在不造概念活沿用通俗概念的情况下完成设计,为什么不呢?
fuck android framework!!!
源码 http://download.csdn.net/source/2599645