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