[转]Android Studio 基础 之 简单的多屏异显的功能实现的详细步骤,及相关简单整理

目录

Android Studio 基础 之 简单的多屏异显的功能实现的步骤,及相关简单整理

一、简单介绍

二、实现原理

三、注意事项

四、开发环境

五、实现步骤

六、关键代码

附录:

七、报错处理:Android: Unable to add window. Permission denied for this window type

八、网上资料参考:Android 双屏异显实现的三种方式

1、双屏异显第一种实现方式(官方提供的Presentation)

2、双屏异显(同显)实现方式

3、双屏异显还有一种方式是通过 投影来实现的,每次投影都会弹提示框,进行确认,有一定的局限性


一、简单介绍

Android 开发中的一些基础操作,使用整理,便于后期使用。

本节介绍,Android 开发中,简单实现多屏幕的不同显示的功能,方法不唯一,欢迎指正。

二、实现原理

1、继承 Presentation 官方提供的类,相当于一个特殊的弹窗窗口

三、注意事项

1、添加权限:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

2、同时应用开启时,获取 ACTION_MANAGE_OVERLAY_PERMISSION 权限

四、开发环境

Android Studio 4.1.1

五、实现步骤

1、打开 Android Studio 新建工程

2、选择 Phone and Tablet - EmptyActivity,点击  Next

3、根据自己需要,进行一些相关设置,然后 Finish 即可

4、这里以双屏幕为例,新建一个脚本,编写逻辑代码

5、在 MainActivity.java 中添加获取相关权限,和获取相关屏幕等的代码逻辑,如下

6、因为开发环境 sdk 版本 为 27(Android 8.1) 所以,设置类型为 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,你可能需要根据自己的 sdk 版本略有不同

不然可能会报错:例如 android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@40ec8528 -- permission denied for this window type

7、根据版本不同,对应的申请权限,不然可以会报错,或者不同多屏异显

8、因为需要根据应用的后台切换和是否关闭,关闭多屏异显功能,所以监控应用状态进行异显的显示与取消

即:show() 或者 dismiss()

9、AndroidManifest.xml 添加对应权限

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

10、对应的 layout.xml 如下图,便于多屏异显使用

11、此时 Build -Make project ,连接手机多屏,就可以看到多屏异显的效果了

六、关键代码

1、DifferentDislay.java

import android.app.Presentation;
import android.content.Context;
import android.os.Bundle;
import android.view.Display;
 
public class DifferentDislay extends Presentation {
 
    public DifferentDislay(Context outerContext, Display display) {
        super(outerContext,display);
 
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mylayout);
    }
}

2、MainActivity.java

 
import android.app.Activity;
import android.app.Application;
import android.app.Presentation;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
 
public class MainActivity extends AppCompatActivity {
 
    private static final String TAG = "XANTest";
    DifferentDislay mPresentation;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        GetPermission();
 
        multiScreenDisplay();
    }
 
    void multiScreenDisplay(){
        //双屏显示
        DisplayManager mDisplayManager;//屏幕管理类
        Display[] displays;//屏幕数组
 
        mDisplayManager =(DisplayManager)MainActivity.this.getSystemService(Context.DISPLAY_SERVICE);
        displays =mDisplayManager.getDisplays(); //得到显示器数组
 
        Log.i(TAG, "multiScreenDisplay: displays count = " + displays.length);
 
        if (displays.length > 1){
 
            mPresentation =new DifferentDislay
                    (getApplicationContext(),displays[1]);//displays[1]是副屏
 
            mPresentation.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
 
 
//            if you use apiLevel >= 19, don't use
//
//            WindowManager.LayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
//
//            which gets the following error:
//
//            android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@40ec8528 -- permission denied for this window type
//
//            Use this instead:
//
//            LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
 
 
            mPresentation.show();
        }
 
 
    }
 
    void GetPermission(){
        if(Build.VERSION.SDK_INT >= 23) {
            if (!Settings.canDrawOverlays(this)) {
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                        Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent, 1234);
            }
        }
        else
        {
            Intent intent = new Intent(this, Service.class);
            startService(intent);
        }
 
    }
 
    @Override
    protected void onStop() {
        super.onStop();
        if (mPresentation != null) {
            mPresentation.dismiss();
        }
    }
 
    @Override
    protected void onResume() {
        super.onResume();
        if (mPresentation != null) {
            mPresentation.show();
        }
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresentation != null) {
            mPresentation.dismiss();
        }
    }
 
 
}

3、AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testmultiscreendisplay0407">
 
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
 
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.TestMultiScreenDisplay0407">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

4、activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
 
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
 
</android.support.constraint.ConstraintLayout>

5、mylayout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="658dp"
        android:layout_weight="1"
        android:text="MyTestScreen"
        android:textSize="100dp"
        />
</LinearLayout>

附录:

七、报错处理:Android: Unable to add window. Permission denied for this window type

参考博文地址:Android: Unable to add window. Permission denied for this window type - Stack Overflow

八、网上资料参考:Android 双屏异显实现的三种方式

参考博文地址:Android 双屏异显实现的三种方式_createdisplaycontext_我居然是个凡人的博客-CSDN博客

在各种产品脑洞大开的时代,需求也是日益新异,笔者最近开发了一套双屏异显app。现在做一些总结

1、双屏异显第一种实现方式(官方提供的Presentation)

Android 提供了一个叫 Presentation 类,来实现第二屏, 继承 Presentation 实现第二屏,相当于一个特殊的弹窗窗口(具体实现)

 public class DifferentDislay extends Presentation {
        public DifferentDislay(Context outerContext, Display display) {
            super(outerContext,display);
           
        }
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.diffrentdisplay);
        }
    }

引用:

 //双屏显示
            DisplayManager mDisplayManager;//屏幕管理类
            Display[] displays;//屏幕数组
            mDisplayManager =(DisplayManager)MainActivity.this.getSystemService(Context.DISPLAY_SERVICE);
            displays =mDisplayManager.getDisplays(); //得到显示器数组
            DifferentDislay mPresentation =new DifferentDislay
            (getApplicationContext(),displays[1]);//displays[1]是副屏
            mPresentation.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
            mPresentation.show();

所需权限:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
        <uses-permission android:name="android.permission.TYPE_APPLICATION_OVERLAY" />
        <uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

注:以上是以 Presentation 实现的双屏异显,这种方式比较适合双屏独立操作没有交际的时候,如果存在双屏同显,或者两者之际要有一些数据同步,后比较麻烦,

比如:主屏播放适配 - >投影到第二屏,上面这种方法不适用了,因为涉及到适配同步显示,还有主副屏幕都要启动一个播放器才能实现,性能极大的浪费,设备性能比较好,还可以以这种方式实现,如果设备性能不是很好,使用这种方式后照成视频卡顿,严重者可能解码失败,照成视频无法播放等等一些列并发问题

针对上面开启第二屏 双屏同显,播放视频,我在原来的基础上做了极大的改善,可以避免启动两个播放器,照成性能的浪费

2、双屏异显(同显)实现方式

相信做双屏异显的同胞们,肯定看过来Presentation 的源码 ,源码中显示 Presentation 是继承与 Dialog 来实现的,在文章的开头我也有提到过,第二屏可以看作一个特殊的 Dialog 来实现

在研究Presentation 源码的时候发现它是通过  Window w = getWindow();  来获取了一个窗口,做我们android 开发的都知道 Window是android 顶级窗口,看到这里我在想为何自己不能直接去创建一个窗口然后获取屏幕数组放置在第二屏幕上呢?往下看  

 public void addPresentation(Context paramContext) {
            Display display = ((MediaRouter) paramContext.getSystemService(Context.MEDIA_ROUTER_SERVICE)).getSelectedRoute(2).getPresentationDisplay();
            this.secondDisplay = display;
            if (display != null) {
                this.windowManager = (WindowManager) paramContext.createDisplayContext(display).getSystemService(Context.WINDOW_SERVICE);
                this.secondDisplayContext = paramContext.createDisplayContext(this.secondDisplay);
                return;
            }
        }

上述代码我们获取窗口管理器,通过paramContext创建了第 paramContext.createDisplayContext(this.secondDisplay);  第二屏幕

创建好第二屏幕以后我们去给第二屏屏幕添加一个view      

 public View addView(int paramInt) {
            this.view = View.inflate(this.secondDisplayContext, paramInt, null);
            this.layoutParams = new WindowManager.LayoutParams(2003, 3, 3);
            if (Build.VERSION.SDK_INT >= 23) {
                this.layoutParams.type = 2038;
            } else {
                this.layoutParams.type = 2003;
            }
            this.windowManager.addView(this.view, this.layoutParams);
            return this.view;
        }

这样我们的第二屏幕就已经完成,只需要根据自己的需求创建一个布局,调用addView方法添加进去,把添加进去的view返回出去,在主类中进行操作,就解决了数据数据同步问题

以下是完整代码   

public class HelpHandPresentation {
        private WindowManager.LayoutParams layoutParams;
     
        private Display secondDisplay;
     
        private Context secondDisplayContext;
     
        private View view;
     
        private WindowManager windowManager;
     
        public void addPresentation(Context paramContext) {
            Display display = ((MediaRouter) paramContext.getSystemService(Context.MEDIA_ROUTER_SERVICE)).getSelectedRoute(2).getPresentationDisplay();
            this.secondDisplay = display;
            if (display != null) {
                this.windowManager = (WindowManager) paramContext.createDisplayContext(display).getSystemService(Context.WINDOW_SERVICE);
                this.secondDisplayContext = paramContext.createDisplayContext(this.secondDisplay);
                return;
            }
        }
     
        public View addView(int paramInt) {
            this.view = View.inflate(this.secondDisplayContext, paramInt, null);
            this.layoutParams = new WindowManager.LayoutParams(2003, 3, 3);
            if (Build.VERSION.SDK_INT >= 23) {
                this.layoutParams.type = 2038;
            } else {
                this.layoutParams.type = 2003;
            }
            this.windowManager.addView(this.view, this.layoutParams);
            return this.view;
        }
     
        public void presentationAddView() {
            this.windowManager.addView(this.view, this.layoutParams);
        }
     
        public void removeLayoutView() {
            this.windowManager.removeView(this.view);
        }
    }

相当于一个工具类,只复制到项目里可以直接使用

以下是调用方式

    HelpHandPresentation helpHandPresentation = new HelpHandPresentation();
    helpHandPresentation.addPresentation(context);
    View view = helpHandPresentation.addView(layout);

三行代码即可,调用方便

3、双屏异显还有一种方式是通过 投影来实现的,每次投影都会弹提示框,进行确认,有一定的局限性

 (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);

有兴趣的可以看看 MediaProjectionManager 源码实现,这里就在叙述了

猜你喜欢

转载自blog.csdn.net/qq_27489007/article/details/131330444