Android双屏异显(Presentation)与后台动态配置副屏内容

2,107 阅读5分钟

【版权声明】本文原发布于上海竖排网络科技有限公司官方知乎账号,由本人撰写发布,现摘抄复习,有什么问题可以直接来我掘金提问。 【前言】Android 从4.2开始支持双屏显示,支持版本为17以上。
Android 双屏原理说白了,自定义一个Presentation类,Android 的标准实现是使用 API Presentation 来实现异显的功能。 Presentation 是扩展自 dialog.相当于一个升级版的弹窗,其实还是在同一个activity里面完成的,两个屏幕里面的内容,可以通过同一个activity进行控制和相关数据变动展示。在副屏上显示不同内容。它的显示内容是依附在主屏的Activity上的,如果Activity被销毁Presentation也不会再显示,主副屏内容会再次恢复成相同的页面。

【作者】清泓

官方文档:

Presentation | Android 开发者 | Android Developers​developer.android.google.cn/reference/android/app/Presentation#public-constructors

直接上核心代码,在这里有几个需要注意的点,在用户更新界面UI的时候,需要确定Presentation是否处于开启状态中,如果处于存在的状态,直接更新UI即可,在用户多次触发双屏异显的时候,保证Presentation只有一个,不重复创建。至于对非activity的动态UI更新,我直接用的Rxbus.

Presentation继承自Dialog,获取到Presentation要显示的设备后,就要将Activity的context对象和设备信息作为参数来创建Presentation对象;将设备记录在成员变量mDisplay中,将Presentation设置为不可在外部点击取消;所以我封装了一下。以下你可以直接把需要的值带入进去。

**
 *   双屏异显
 *   2020129日
  */

    @SuppressLint("NewApi")
    private void setCustomerProductList(String url) {
        DisplayManager displayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
        presentationDisplays = displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
        if (presentationDisplays.length>1){
            Log.e(TAG, "setCustomerProductList: "+"小屏弹窗超出数量,会造成返回的时候程序奔溃");
            return;
        }else {
            Display display = presentationDisplays[0];
            if (presentation != null) {
//                presentation.dismiss();
//                presentation = null;
//                Log.e(TAG, "setCustomerProductList: "+"清空" );
                Log.e(TAG, "setCustomerProductList: "+"直接传值!" );
                RxBus.get().send(ConStant.DOUBLE_SCRENN_UI, mDoubleScreenUrl);
            }else {
                Log.e(TAG, "setCustomerProductList: "+"双屏实例为空启用初始化!" );
                presentation = new DifferentDislay(this, display,url);
                presentation.show();
                RxBus.get().send(ConStant.DOUBLE_SCRENN_UI, mDoubleScreenUrl);
            }

        }
    }

副屏,也就是Presentation 中,我们通过一个webView对接收到的内容,触发的内容进行展示。在将presentation显示出来之前,最重要的事情就是选择要将presentation显示在哪个设备上。要选择显示在哪个设备可能是一件非常困难的事情,因为可能此时系统中有多个显示设备。应用程序应该让系统选择合适的Display,而不是试图猜测哪个显示最佳。Android系统为我们提供了两种方式选择Display,我用的DisplayMannager。

/**
 * Author:QingHong
 * Data:2020年12月8日
 */
public class DifferentDislay extends Presentation {
    private FrameLayout frameScreen;
    private WebView mWebView;
    private BroadcastReceiver MyReceiver;
    private Context context;
    private ScreenDoubleLiveData mutableLiveData;
    private String mDoubleScreen;


    public DifferentDislay(Context outerContext, Display display , String url) {
        super(outerContext, display);
//        mDoubleScreen=url;
        context=App.getContext();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.double_display_block_test);
        frameScreen =findViewById(R.id.double_screen_frame);
//        接收通知
        RxBus.get().register(this);


//        new ViewModelProvider(this,new CurrentConversationsViewModel.Factory(withWho)).get(CurrentConversationsViewModel.class);
//        mutableLiveData = ViewModelProviders.of(App.getContext()).get(ScreenDoubleLiveData.class);
//        MutableLiveData<String> urlEvent = mutableLiveData.getScreenContextEvent();

//        if (SharedPreferencesUtil.getInstance(App.getContext()).getDoubleScreenUrl()==""){
//            addWeb("https://www.bookstack.cn/");
//        }else {
//            addWeb(SharedPreferencesUtil.getInstance(App.getContext()).getDoubleScreenUrl());
//        }
    }

    @Subscribe(code = ConStant.DOUBLE_SCRENN_UI, threadMode = ThreadMode.MAIN)
    public void progressChange(String url) {
//            mDoubleScreen=url;
        Log.e(TAG, "progressChange: "+"走入此方法:双屏异显" );
        if (url!=null){
//            showProgressDialog("xx卡丁车收银系统","正在生成订单,请稍候~");
            addWeb(url);
//            hideProgressDialog();
        }else {
            Log.e(TAG, "onCreate: "+"无法接收到值");
            Toast.makeText(context, "无法接收到值", Toast.LENGTH_SHORT).show();
        }

    }
        public void addWeb(String url) {

        //        避免内存泄漏,上下文用ApplicationContext.
        WebView webView = new WebView(App.getContext());
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        );
        webView.setLayoutParams(params);
        WebSettings settings = webView.getSettings();
//        设置缓存模式
        if (NetUtils.isNetworkAvailable(App.getContext())){
            settings.setCacheMode(WebSettings.LOAD_DEFAULT);
        }else {
            settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        }
        // 启用 WebView 调试模式。
        // 注意:请勿在实际 App 中打开!
//        webView.setWebContentsDebuggingEnabled(true);
        // 启用二方/三方 Cookie 存储和 DOM Storage
        // 注意:若要在实际 App 中使用,请先了解相关设置项细节。
//        CookieManager.getInstance().setAcceptCookie(true);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
        }
        settings.setJavaScriptEnabled(true);
        settings.setDomStorageEnabled(true);
        settings.setDatabaseEnabled(true);
        settings.setCacheMode(WebSettings.LOAD_DEFAULT);
        settings.setUseWideViewPort(true);//将图片调整到适合webview的大小
        settings.setLoadWithOverviewMode(true);//缩放至屏幕的大小
        settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        webView.loadUrl(url);
//        webView.setWebViewClient(
//                new WebViewClient() {
//                    @Override
//                    public boolean shouldOverrideUrlLoading(WebView view, String url) {
//                        view.loadUrl( url );
//                        return true;
//                    }
//
//                    @Override
//                    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
//                        handler.proceed();    //表示等待证书响应
//                    }
//
//                    //设置加载前的函数
//                    @Override
//                    public void onPageStarted(WebView view, String url, Bitmap favicon) {
//
//                    }
//
//                    //设置结束加载函数
//                    @Override
//                    public void onPageFinished(WebView view, String url) {
//                        //super.onPageFinished( view, null );
////                        makeToastByHandlerPost("SHOW_DOWN_DIALOG_MS");
//                    }
//                }
//            );
        mWebView = webView;
        frameScreen.addView(mWebView);
    }

    @Override
    public void setOnDismissListener(@Nullable OnDismissListener listener) {
        super.setOnDismissListener(listener);
        RxBus.get().unRegister(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        RxBus.get().unRegister(this);
    }

    public static class Myreceiver extends BroadcastReceiver{
                @Override
                public void onReceive(Context context, Intent intent) {
                    Handler handler;
                    Message message = null;
                    //获取Intent中携带的数据

                    Bundle bundle = intent.getExtras();
                    if(bundle != null) {
                        //                int num1 = bundle.getInt("num1");
                        final String url = bundle.getString("url");

                        Log.i("IT_Real", "onReceive1: url  = " + url);
//                        addWeb(url);
                        Toast.makeText(context, url, Toast.LENGTH_SHORT).show();
                    }
            }
         }


    //       public  class  MyReceiver1 extends BroadcastReceiver {
//        public MyReceiver1() {
//        }
//
//        /**
//         * 实现该方法即可,系统会自动调用处理
//         * @param context 上下文
//         * @param intent 对应的Intent
//         */
//        @Override
//        public void onReceive(Context context, Intent intent) {
//            Handler handler;
//            Message message = null;
//            //获取Intent中携带的数据
//
//            Bundle bundle = intent.getExtras();
//            if(bundle != null){
////                int num1 = bundle.getInt("num1");
//                final String url = bundle.getString("url");
//
//                Log.i("IT_Real", "onReceive1: url  = " + url);
//                Toast.makeText(context, url, Toast.LENGTH_SHORT).show();
////                makeToastByHandlerPost(url);
//
//
//            }
//        }
//    }
    //方法二:通过handler.post,更新UI主线程
    private  void makeToastByHandlerPost( final String msg){
        Handler handler;
        handler =new Handler();
        handler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(App.getContext(), msg, Toast.LENGTH_SHORT).show();
//                if (msg=="SHOW_DIALOG_MSG"){
//                    showProgressDialog("跑跑卡丁车...","结账中~");
//                }else if (msg=="SHOW_DOWN_DIALOG_MS"){
//                    hideProgressDialog();
//                }
            }
        });
    }


}

其余补充:

以下是我用到的权限。

<!--  2020年12月8日  -->
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

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

动态更新传值部分:(handler机制)

//    加载的URL
    private String mDoubleScreenUrl =null;

//在静态常量类里面定义的消息序列
 public static final int DOUBLE_SCRENN_UI=2344;

/**
/*在主activity中拿动态数据的时候赋值
/*硬件对接
/*当数据不为空的时候,发送消息通知给主线程,进行UI更新。
/**
  final String mDoubleScreenTest=returnDataBean.getZhengshi_banben().getVersion_remark_020();
  Log.e(TAG, "mDoubleScreen: "+mDoubleScreenTest);
  if (mDoubleScreenTest!=null){
     mDoubleScreenUrl=mDoubleScreenTest;
     webhandler.sendEmptyMessage(ConStant.DOUBLE_SCRENN_UI);
 }
//在handler里面的我的方法,整个handler就不在这里截取了,这个大家应该都知道写,
只是我的思路大家可以参考一下
  case ConStant.DOUBLE_SCRENN_UI:
       showProgressDialog("跑跑卡丁车","正在结算,清稍候~");
       setCustomerProductList(mDoubleScreenUrl);
       webhandler.removeMessages(ConStant.DOUBLE_SCRENN_UI);
       hideProgressDialog();
     break;

通过Rxbus把值传送到Presentation里面。

有其他问题,请私信我哦,讲述的不够清楚的地方请联系我修正。

初稿待更新.........