Share
Embed
Whoa, Views can do that? WindowManager ideas and tricks!
https://youtu.be/ujfe_IF1O4A
Android WindowManager Ideas and Floating Views
Eric Cochran
August 28, 2015
Transcript
20 slides
-
ERIC COCHRAN WHOA, VIEWS CAN DO THAT? WINDOWMANAGER IDEAS AND
TRICKS! @ERIC_COCHRAN DROIDCON NYC AUGUST 28, 2015 -
BEYOND SCREENS • VIEWS THAT ARE NOT ATTACHED TO AN
ACTIVITY’S WINDOW • REAL, FULL-FLEDGED VIEWS -
PERMISSION <uses-permission android:name=“android.permission.SYSTEM_ALERT_WINDOW"/> Android M: hidden setting if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + activity.getPackageName())); activity.startActivityForResult(intent, REQUEST_CODE_PERMISION_SYSTEM_ALERT_WINDOW); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE_PERMISION_SYSTEM_ALERT_WINDOW) { if (Settings.canDrawOverlays(this)) { // yay! } else { // denied } } } -
ADDING THE VIEWS WindowManager windowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE); View
floatingView = new View(context); // background to see View floatingView.setBackgroundColor(Color.RED); WindowManager.LayoutParams floatingLayoutParams = new WindowManager.LayoutParams(floatingViewWidth, floatingViewHeight, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSPARENT); floatingLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; windowManager.addView(floatingView, floatingLayoutParams); -
FLAG_NOT_FOCUSABLE Views will not receive focus. No EditTexts. WI FLAG_NOT_TOUCHABLE
No touch listeners will work. FLAG_NOT_TOUCH_MODAL Will allow touch events to pass to outside Views FLAG_LAYOUT_NO_LIMITS Views are free to move offscreen. FLAG_LAYOUT_IN_SCREEN Keep Views bounded by the screen’s dimensions. -
TO A SERVICE @Override public int onStartCommand(Intent intent, int flags,
int startId) { // addView code Notification notification = // valid notification startForeground(id, notification); return START_STICKY; } -
GET MOVING floatingView.setOnTouchListener(new View.OnTouchListener() { private int downX; private
int downY; private float touchDownX; private float touchDownY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: downX = floatingLayoutParams.x; downY = floatingLayoutParams.y; touchDownX = event.getRawX(); touchDownY = event.getRawY(); break; case MotionEvent.ACTION_MOVE: floatingLayoutParams.x = (int) (downX + event.getRawX() - touchDownX); floatingLayoutParams.y = (int) (downY + event.getRawY() - touchDownY); windowManager.updateViewLayout(floatingView, floatingLayoutParams); break; default: break; } return true; } }); -
GOTCHAS — MORE ISSUES • ALPHA, SCALE, AND TRANSFORMATIONS DO
NOT WORK PRE-LOLLIPOP -
SPOILER • ADD A PARENT • VIEW PROPERTIES WILL WORK
ON THE PARENTED VIEW -
FIRST IDEA (NOT-SO-GREAT) • MAKE THE ROOT VIEW THAT IS
ATTACHED TO THE WINDOW MANAGER ALWAYS LARGE ENOUGH TO ACCOMMODATE THE MAXIMUM SCALE THAT THE CORE VIEW WILL EVER ACHIEVE. • ADD A MARGIN TO THE CORE VIEW THAT FILLS THE SPACE. • WHEN THE CORE VIEW SCALES, THE MARGIN WILL BE THE ONLY PART CLIPPED. -
• DISADVANTAGE OF A LARGER ROOT VIEW THAT WILL NOW
BLOCK MORE UI FROM THE USER. • WORSE, THIS ADDED MARGIN IS INVISIBLE AND COULD LEAD TO THE USER NOT UNDERSTANDING WHY OUR FLOATING VIEW IS INTERCEPTING TOUCH EVENTS OUTSIDE OF THE VISIBLE UI. FIRST IDEA (NOT-SO-GREAT) -
FIRST IDEA (NOT-SO-GREAT) GRAY: the root view’s invisible padding RED:
visible parented view that should scale -
FIRST IDEA (NOT-SO-GREAT) • ROOT VIEW ACCOUNTS FOR THE CORE
VIEW (THE RED DOT) SCALING TO TWICE AS LARGE • BUT, THE ROOT VIEW IS NOW OBSTRUCTING OTHER TOUCH EVENTS WITHOUT THE USER REALIZING IT. -
• DYNAMICALLY SCALE THE ROOT VIEW BETTER METHOD floatingLayoutParams.width
*= scaleFactorX; floatingLayoutParams.height *= scaleFactorY; windowManager.updateViewLayout(floatingView, floatingLayoutParams); -
BETTER IDEA • WE HAVE JUST SCALED THE WIDTH AND
THE HEIGHT WHILE LEAVING THE X AND Y FIELDS IN PLACE. PROBABLY NOT IDEAL. • TO SCALE RADIALLY: floatingLayoutParams.x -= (int) ((scaleFactorX - 1f) * floatingLayoutParams.width / 2f); floatingLayoutParams.y -= (int) ((scaleFactorY - 1f) * floatingLayoutParams.height / 2f); -
REMOVE WINDOW VIEWS • REMOVE VIEWS WHERE APPROPRIATE TO PREVENT
LEAKS • (PROBABLY IN ONDESTROY() OF A SERVICE) -
• DEFINE YOUR BOUNDS • WITH INSETS, CHECK AGAINST BOUNDS
BEFORE MOVING IN TOUCH LISTENER • VIEW.GETLOCATIONONSCREEN • ADD A VELOCITY TRACKER TIPS