最近公司在做一個支付組件。持續做了一個月,我做了三個版本,現在項目突然終止,我也無事可做,就想把這個項目中用到的浮動框效果拿出來,曬一曬,鞏固知識的同時
也順便幫幫有需要的人,也不失為一件樂事。
該浮動框其實挺簡單,就是一個開機廣播啟動一個後台Service,不斷的從記憶體中查看商城用戶端啟動,如果啟動,就發送一個廣播,運行支付組件;當商城退出時,支付組件
也退出。
先看來:
(注意:該圖為私人收藏圖,請勿隨意轉載。)
下面說說過程與核心代碼。
一、服務隨著開機完成自動啟動。
(1)、開機廣播接收者代碼。BootCompleteReceiver.java
public class BootCompleteReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Log.i("BootCompleteReceiver", "接到開機廣播");Intent iAppServiceIntent = new Intent(context, AppService.class);context.startService(iAppServiceIntent);}}
上面代碼中的AppService.class是廣播接收者啟動的服務。
資訊清單檔內的許可權和註冊。
許可權
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
註冊
<receiver android:name=".receiver.BootCompleteReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
(2)、開機後啟動的服務AppService.java
public class AppService extends Service {private String TAG = "AppService";private RunningTaskInfo info;private static WindowManager wm;private ActivityManager am;private PackageManager pm;private String actionStart = "com.handaer.receiver.FLOAT_VIEW_START_ACTION"; //自訂action名字,private String actionStop = "com.handaer.receiver.FLOAT_VIEW_STOP_ACTION"; //自訂action名字Handler handler = new Handler();Timer timer = null;TimerTask task = new TimerTask() {public void run() {handler.post(new Runnable() {@Overridepublic void run() {getAppInfo();}});};};public int onStartCommand(Intent intent, int flags, int startId) {if (timer == null) {timer = new Timer();timer.scheduleAtFixedRate(task, 0, 500);}return super.onStartCommand(intent, flags, startId);};@Overridepublic IBinder onBind(Intent intent) {return null;}private void getAppInfo() {am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);pm = this.getPackageManager();ApplicationInfo actInfo;List<RunningTaskInfo> list = am.getRunningTasks(1);info = list.get(0);ComponentName cn = info.topActivity;String name = cn.getPackageName();try {actInfo = pm.getApplicationInfo(name, PackageManager.GET_META_DATA);Bundle metaData = actInfo.metaData;Log.i(TAG, "AppService正在運行");if (metaData != null) {String metaValue = metaData.getString("com.handaer.newhbddemo");if ("com.handaer.around".equals(metaValue)|| "com.handaer.pay".equals(metaValue)) {// 發送自訂廣播 com.handaer.receiver.FLOAT_VIEW_START_ACTIONIntent intent = new Intent();intent.setAction(actionStart);sendBroadcast(intent);}} else {// 發送自訂廣播 com.handaer.receiver.FLOAT_VIEW_STOP_ACTIONIntent intent = new Intent();intent.setAction(actionStop);Bundle bundle = new Bundle();sendBroadcast(intent);} } catch (NameNotFoundException e) {e.printStackTrace(); }}}
getAppInfo()中的代碼是從記憶體中擷取任務棧第一個任務,然後通過ActivityManager擷取到其詳細資料中的name,然後判斷是否為商城應用,然後再做相應的處理。
(3)、下面是兩個自訂廣播FloatViewStartReceiver.java 和 FloatViewStopReceiver.java
FloatViewStartReceiver比較簡單,就是啟動 FloatWeigtService服務
public class FloatViewStartReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Log.i("FloatViewStartReceiver", "收到啟動廣播");Intent intentStart = new Intent(context, FloatWeigtService.class);try {context.startService(intentStart);} catch (Exception e) {e.printStackTrace();}}}
資訊清單檔中註冊:
<receiver android:name=".receiver.FloatViewStartReceiver" > <intent-filter> <action android:name="com.handaer.receiver.FLOAT_VIEW_START_ACTION" /> </intent-filter> </receiver>
自訂廣播接收者FloatViewStopReceiver.java ,接收到廣播後會停止 FloatWeigtService
public class FloatViewStopReceiver extends BroadcastReceiver {private RunningServiceInfo runningServiceInfo;private String serviceName = "com.handaer.hbdpay.service.FloatWeigtService";@Overridepublic void onReceive(Context context, Intent intent) {ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);PackageManager pm = context.getPackageManager();List<ActivityManager.RunningServiceInfo> serviceList = am.getRunningServices(Integer.MAX_VALUE);if (serviceList.size() == 0) {return;}for (int i = 0; i < serviceList.size(); i++) {runningServiceInfo = serviceList.get(i);if (runningServiceInfo.service.getClassName().equals(serviceName)) {ComponentName name = runningServiceInfo.service;Intent i2 = new Intent();i2.setComponent(name);try {context.stopService(i2);} catch (Exception e) {e.printStackTrace();}break;}}}}
在資訊清單檔註冊 FloatViewStopReceiver
<receiver android:name=".receiver.FloatViewStopReceiver" > <intent-filter> <action android:name="com.handaer.receiver.FLOAT_VIEW_STOP_ACTION" /> </intent-filter> </receiver>
(4)、下面是 負責建立浮動框的 FloatWeigtService.java ,該服務會建立和取消浮動框。FloatView
public class FloatView extends LinearLayout {private WindowManager wm;public static int viewWidth;public static int viewHeight;private int screenWidth;private int width;private int yDown;private int xValueInView;private int yValueInView;private int xInScreen;private int yInScreen;private WindowManager.LayoutParams params;private ImageView iv_short;public FloatView(Context context) {super(context);wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);screenWidth = wm.getDefaultDisplay().getWidth();LayoutInflater inflater = LayoutInflater.from(context);View view = inflater.inflate(R.layout.float_view, this);iv_short = (ImageView) view.findViewById(R.id.iv_short);width = view.getWidth();LinearLayout deskView = (LinearLayout) view.findViewById(R.id.ll_base_title_view);viewWidth = deskView.getLayoutParams().width;viewHeight = deskView.getLayoutParams().height;}public void updataLocation() {params.x = (int) (xInScreen - xValueInView);params.y = (int) (yInScreen - yValueInView);wm.updateViewLayout(this, params);}public void setParams(WindowManager.LayoutParams params) {this.params = params;}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN://按下浮動框後記錄浮動框的位置xValueInView = (int) event.getX();yValueInView = (int) event.getY();yDown = (int) event.getRawY();xInScreen = (int) event.getRawX();yInScreen = (int) (event.getRawY());break;case MotionEvent.ACTION_MOVE://移動浮動框時,不斷的更新浮動框updataLocation()xInScreen = (int) event.getRawX();yInScreen = (int) (event.getRawY());//只有浮動框移動位置才會更新。不加這段代碼,當按住浮動框時,浮動框會自己彈開一段距離。可能和我案頭有關係。int abs = Math.abs(yInScreen - yDown);if (abs != 0) {updataLocation();}break;case MotionEvent.ACTION_UP://抬起時,將浮動框靠螢幕右邊顯示。params.x = screenWidth;params.y = (int) (yInScreen - yValueInView);wm.updateViewLayout(this, params);break;default:break;}return true;}}
完。