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