Android: Overlay on Window over Activities of a Task

前端 未结 2 738
南旧
南旧 2021-02-06 06:04

I wanted to create an Overlay, like a HUD, that resides on the screen during my applications activity stack (the task of my app) changes.

I found a couple of examples u

相关标签:
2条回答
  • 2021-02-06 06:45

    You really only have two options.

    1) You can create an activity with the theme of Theme.Dialog. This will display a popup on top of your window. You can create the dialog to be modal-less (can click through). In my quick testing I wasn't able to get overlay to the edges of my screen although maybe modifying the theme would fix that.

    MainActivity.java

    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button test = (Button) this.findViewById(R.id.test);
            test.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    ((Button) v).setBackgroundColor(Color.RED);
                }
            });
    
            Intent i = new Intent(this, SecondActivity.class);
            startActivity(i);
        }
    }
    

    SecondActivity.java

    public class SecondActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.second_main);
    
            Window window = getWindow();
            window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
            window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
            window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
            window.setGravity(Gravity.BOTTOM);
        }
    
        @Override
        public void onBackPressed() {
                //Override to prevent back button from closing the second activity dialog
        }
    }
    

    Manifest

        ....
        <activity
            android:name="com.example.control.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.example.control.SecondActivity"
            android:theme="@android:style/Theme.Dialog"
            android:label="@string/app_name" >
            ....
        </activity>
        ....
    

    2) The second option is to use a SYSTEM_ALERT_WINDOW. I prefer this method significantly more. You are correct that it CAN be visible and on top of every other app, however, you can control when it is visible and when it isn't. I'm not going to post any source code but I will give you a general plan of attack.

    When you create the service, bind to it using an AIDL. This way you'll be able to talk directly with the service to tell it when to 'hide' and 'show' the overlay. Speaking of hiding and showing, onPause and onResume can be used to tell the service to hide and show the overlay. Lastly, if you need to receive click events on your overlay, that will prove to be tricky as the touch events don't always act the way you expect them to.

    Good Luck.

    0 讨论(0)
  • 2021-02-06 06:52

    I think the following will be of interest to you. You won't require android.permission.SYSTEM_ALERT_WINDOW. The code is for the Application class.

    Comments should help you understand the logic. The code should work right out of the box.

    public class YourApplication extends Application {
    
        // Popup to show persistent view
        PopupWindow pw;
    
        // View held by Popup
        LinearLayout ll;
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            // Register for Activity Lifecyle Callbacks 
            registerActivityLifecycleCallbacks(new YourCallBack());
    
            // Initialize the view  
            ll = new LinearLayout(this);        
            ll.setLayoutParams(new LayoutParams(100, 100));     
            ll.setBackgroundColor(Color.BLUE);
    
            // Initialize popup 
            pw = new PopupWindow(ll, 100, 100);     
    
            // Set popup's window layout type to TYPE_TOAST     
            Method[] methods = PopupWindow.class.getMethods();
            for(Method m: methods){
                if(m.getName().equals("setWindowLayoutType")) {
                    try{
                        m.invoke(pw, WindowManager.LayoutParams.TYPE_TOAST);
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                    break;
                }
            }       
        }
    
        @Override
        public void onTerminate() {
            super.onTerminate();
    
            if (pw != null && pw.isShowing()) {
                pw.dismiss();
            }
        };
    
    
    
        private final class YourCallBack implements ActivityLifecycleCallbacks {
    
            int numOfRunning = 0;
    
            @Override
            public void onActivityCreated(Activity arg0, Bundle arg1) { }
    
            @Override
            public void onActivityDestroyed(Activity arg0) { }
    
            @Override
            public void onActivityPaused(Activity arg0) {
    
                // An activity has been paused
                // Decrement count, but wait for a certain
                // period of time, in case another activity
                // from this application is being launched
                numOfRunning--;
    
                // Delay: 100 ms
                // If no activity's onResumed() was called,
                // its safe to assume that the application
                // has been paused, in which case, dismiss
                // the popup            
                new Handler().postDelayed(new Runnable() {
    
                    @Override
                    public void run() {
                        if (numOfRunning == 0) {
                            pw.dismiss();
                        }
                    }
                }, 100L);
            }
    
            @Override
            public void onActivityResumed(Activity arg0) {
    
                // If no activities were running, show the popup
                if (numOfRunning == 0) {
                    pw.showAtLocation(ll, Gravity.BOTTOM, 0, 0);
                }
    
                // Now, one activity is running         
                numOfRunning++;
            }
    
            @Override
            public void onActivitySaveInstanceState(Activity arg0, Bundle arg1) { }
    
            @Override
            public void onActivityStarted(Activity arg0) { }
    
            @Override
            public void onActivityStopped(Activity arg0) { }
    
        };
    }
    
    0 讨论(0)
提交回复
热议问题