ButterKnife可以方便的获取到xml中定义的view的实例,比findViewById方便多了,使用ButterKnife非常简单,可以总结为3步吧:
compile 'com.jakewharton:butterknife:7.0.0'
@Bind(R.id.start) Button start;
@Bind(R.id.stop) Button stop;
@Bind(R.id.bind) Button bind;
@Bind(R.id.unbind) Button unbind;
ButterKnife.bind(this);
在onCreate中调用它就可以。通过这句调用,start,stop,bind,unbind几个Button都被实例化了。public class FlowWindowService extends Service {
private final String TAG = "FlowWindowService";
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"onCreate");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind");
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG,"onStartCommand");
showFlowWindow();
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy");
}
public void showFlowWindow(){
Log.v(TAG,"showFlowWindow");
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
Button button = new Button(getApplicationContext());
button.setText("flow");
button.setBackgroundColor(Color.RED);
button.setWidth(100);
button.setHeight(100);
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_PHONE;
params.format = PixelFormat.RGBA_8888;
params.gravity = Gravity.LEFT | Gravity.TOP;
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.width = 100;
params.height = 100;
params.x = 300;
params.y = 300;
windowManager.addView(button, params);
}
}
MainActivity中使用ButterKnife实例化四个Button,并设置触摸事件监听器:
public class MainActivity extends AppCompatActivity {
@Bind(R.id.start) Button start;
@Bind(R.id.stop) Button stop;
@Bind(R.id.bind) Button bind;
@Bind(R.id.unbind) Button unbind;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startService(new Intent(MainActivity.this,FlowWindowService.class));
}
});
stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopService(new Intent(MainActivity.this,FlowWindowService.class));
}
});
bind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bindService(new Intent(MainActivity.this,FlowWindowService.class),connectionService, Context.BIND_AUTO_CREATE);
}
});
unbind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(connectionService);
}
});
}
ServiceConnection connectionService = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy");
windowManager.removeView(button);
}
这意味着之前的代码需要略作修改,button和windowManager都必须是类中定义的,而不是方法中定义的。
private static final int REQUEST_PERMISSION_CODE = 1;
private void requestCameraPermission() {
requestPermissions(new String[]{Manifest.permission.SYSTEM_ALERT_WINDOW}, REQUEST_PERMISSION_CAMERA_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSION_CODE) {
int grantResult = grantResults[0];
boolean granted = grantResult == PackageManager.PERMISSION_GRANTED;
Log.i(TAG, "onRequestPermissionsResult granted=" + granted);
}
}
使用requestPermisson方法申请SYSTEM_ALERT_WINDOW权限,REQUEST_PERMISSON_CODE是一个整数,用来表示这次请求,它的值随意。requestPermissions会导致onRequestPermissionsResult方法被回调,在这个方法中我们就可以知道我们是不是申请到了权限。最后,
在MainActivity的onCreate方法中申请权限即可。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
requestPermission();
button.setOnClickListener(new View.OnClickListener() {
int count = 0;
@Override
public void onClick(View v) {
if((count++)%2==0){
button.setBackgroundColor(Color.GREEN);
}else {
button.setBackgroundColor(Color.RED);
}
}
});
Handler myHandler = new Handler();
Runnable runnable = new Runnable() {
boolean direct = true;
@Override
public void run() {
Log.v(TAG,"params.x: "+params.x);
if(direct){
params.x+=10;
if(params.x>800){
direct = false;
}
}else {
params.x-=10;
if(params.x<100){
direct = true;
}
}
windowManager.updateViewLayout(button,params);
if(!button.isAttachedToWindow()){
Log.v(TAG,"not attach to window");
}else{
myHandler.postDelayed(this,50);
}
}
};
public void moveFlowButton(){
Log.v(TAG,"moveFlowButton");
myHandler.postDelayed(runnable,500);
}
Process: com.konka.flowwindowtest, PID: 19478
java.lang.IllegalArgumentException: View=android.widget.Button{2553221 VFED..C.. ......I. 0,0-200,200} not attached to window manager
at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:456)
at android.view.WindowManagerGlobal.updateViewLayout(WindowManagerGlobal.java:368)
at android.view.WindowManagerImpl.updateViewLayout(WindowManagerImpl.java:99)
at com.konka.flowwindowtest.FlowWindowService$1.run(FlowWindowService.java:89)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:5969)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:801)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:691)
这个问题应该是Service已经销毁了,但是button还想要更新位置造成的,所以应该在销毁Service前先取消handler更新button的动画。
Boolean destoryStatus = false;
在onDestory中是它为true即可:
synchronized (this){
destoryStatus = true;
}
run方法如下:
Runnable runnable = new Runnable() {
boolean direct = true;
@Override
public void run() {
if(!destoryStatus){
Log.v(TAG,"params.x: "+params.x);
if(direct){
params.x+=10;
if(params.x>800){
direct = false;
}
}else {
params.x-=10;
if(params.x<100){
direct = true;
}
}
windowManager.updateViewLayout(button,params);
myHandler.postDelayed(this,50);
}
}
};
这样就不会有这个问题了。