Android對(duì)接Unity3D,原生View與Unity3D視圖的層級(jí)關(guān)系

Android對(duì)接Unity3D念赶,原生View與Unity3D視圖的層級(jí)關(guān)系

場(chǎng)景

  • 1.很多人在對(duì)接unity的時(shí)候础钠,難免產(chǎn)品會(huì)讓你添加一些原生的view到unity視圖上,但是你會(huì)發(fā)現(xiàn)所添加的原生view并不能被看見(jiàn)叉谜,你甚至開(kāi)始懷疑自己的代碼旗吁,但是確實(shí)代碼沒(méi)有錯(cuò),只是原生的view被unity的UnityPlayer遮擋了停局,至于這個(gè)UnityPlayer后面會(huì)說(shuō)到很钓。
  • 2.假如原生的activity只是單純(有些產(chǎn)品可能要求其他騷操作)的加載一個(gè)unity場(chǎng)景,但是這個(gè)場(chǎng)景的模型又非常大董栽,并且unity端沒(méi)有給你一個(gè)加載場(chǎng)景的動(dòng)畫(huà)码倦,那么很殘忍的結(jié)果就是這個(gè)加載unity場(chǎng)景的過(guò)程中會(huì)出現(xiàn)界面黑屏、白屏甚至花屏(華為出現(xiàn)過(guò))锭碳。這個(gè)時(shí)候產(chǎn)品發(fā)現(xiàn)不對(duì)袁稽,認(rèn)為應(yīng)該叫unity端加一個(gè)動(dòng)畫(huà),但是擒抛,很殘忍的是就算加了動(dòng)畫(huà)推汽,仍然會(huì)出現(xiàn)短暫的界面黑屏、白屏甚至花屏歧沪。那么聰明的原生就說(shuō)歹撒,我加一個(gè)原生的加載動(dòng)畫(huà)不就ojbk了?并且通過(guò)unity端的回調(diào)來(lái)取消這個(gè)動(dòng)畫(huà)不就非常優(yōu)雅了诊胞。然而暖夭,然而發(fā)現(xiàn)新加的原生動(dòng)畫(huà)看不到,看到的是黑屏、白屏甚至花屏鳞尔。好氣嬉橙!

分析

1、按照官方的文檔集成寥假,在原生中顯現(xiàn)Unity3D場(chǎng)景首先是創(chuàng)建一個(gè)Activity市框,然后在這個(gè)Activity中實(shí)例化一個(gè)UnityPlayer對(duì)象就可以了(詳細(xì)查看官方文檔),注意這里有一個(gè)細(xì)節(jié)就是在Activity中放置UnityPlayer這個(gè)對(duì)象最好是幀布局糕韧,相對(duì)布局會(huì)卡頓枫振,因?yàn)槲覀兌贾溃谠袖秩舅俣龋篎rameLayout>LinearLayout>RelativeLayout萤彩,親測(cè)粪滤,當(dāng)模型過(guò)大的時(shí)候,RelativeLayout的容器基本上用不了雀扶。以下是放置unity的界面代碼(不是小白直接跳過(guò)):

open abstract class UnityPlayerActivity : AppCompatActivity() {
    protected lateinit var mUnityPlayer: BaseUnityPlayer // don't change the name of this variable; referenced from native code

    //提示框
    private var loading: LoadingDialog? = null
    //白屏菊花
    private var progress: ProgressDialog? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window.setFormat(PixelFormat.RGBX_8888) // <--- This makes xperia play happy
        mUnityPlayer = BaseUnityPlayer(this)
        mUnityPlayer.requestFocus()
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        // To support deep linking, we need to make sure that the client can get access to
        // the last sent intent. The clients access this through a JNI api that allows them
        // to get the intent set on launch. To update that after launch we have to manually
        // replace the intent with the one caught here.
        setIntent(intent)
    }

    override fun onResume() {
        super.onResume()
        mUnityPlayer.resume()
        mUnityPlayer.windowFocusChanged(true)//unity主動(dòng)聚焦
    }

    override fun onPause() {
        super.onPause()
        mUnityPlayer.pause()
    }

    override fun onDestroy() {
        mUnityPlayer.quit()
        super.onDestroy()
    }

    override fun onLowMemory() {
        super.onLowMemory()
        mUnityPlayer.lowMemory()
    }

    override fun onTrimMemory(level: Int) {
        super.onTrimMemory(level)
        if (level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
            mUnityPlayer.lowMemory()
        }
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        mUnityPlayer.configurationChanged(newConfig)
    }

    override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        mUnityPlayer.windowFocusChanged(hasFocus)
    }

    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        return if (event.action == KeyEvent.ACTION_MULTIPLE) mUnityPlayer.injectEvent(event) else super.dispatchKeyEvent(event)
    }

    override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
        return mUnityPlayer.injectEvent(event)
    }

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        return mUnityPlayer.injectEvent(event)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        return mUnityPlayer.injectEvent(event)
    }

    /*API12*/
    override fun onGenericMotionEvent(event: MotionEvent): Boolean {
        return mUnityPlayer.injectEvent(event)
    }

    /**
     * 用于網(wǎng)絡(luò)加載提示的彈窗
     */
    protected fun showLoading() {
        loading = loading ?: LoadingDialog()
        loading?.setCallBack(object : LoadingDialog.OnDisMissCallBack {
            override fun disMiss() {
                cancelCurrent()
            }
        })
        if (loading?.isAdded == false) {
            loading?.show(supportFragmentManager, "Loading")
        } else {
            loading?.dialog?.show()
        }
    }

    protected fun disMisLoading() {
        loading?.dismiss()
    }

    /**
     * U3D白屏界面上的靜態(tài)菊花
     */
    protected fun showProgress() {
        progress = progress ?: ProgressDialog.show(this, false, null)
        if (!progress!!.isShowing) {
            progress!!.show()
        }
    }

    protected fun disMissProgress() {
        progress!!.dismiss()
    }

    abstract fun cancelCurrent()
}

xml中(筆者放置UnityPlayer的界面布局):

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             xmlns:InnerView="http://schemas.android.com/apk/res-auto"
             xmlns:app="http://schemas.android.com/apk/res-auto"
             xmlns:tools="http://schemas.android.com/tools"
             xmlns:CircleProgressBar="http://schemas.android.com/apk/res-auto"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             tools:context="com.cninct.rmm.mvp.view.activity.MainUnityActivity">

    <!--unity的宿主控件杖小,頂層和該層都是用幀布局,提高繪制效率-->
    <FrameLayout
        android:id="@+id/fl_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </FrameLayout>

    <!--底部菜單欄-->
    <include
        android:id="@+id/layout_tab"
        layout="@layout/view_main_tab"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_gravity="bottom"/>
    ......

Activity中:

 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_unity_main)
        initView()
        ......
    }

    //首頁(yè)相關(guān)控件的初始化
    private fun initView() {
        fl_content.addView(mUnityPlayer)
    ......
    }

2愚墓、在正常顯示之后就可以發(fā)現(xiàn)予权,其實(shí)真正顯示Unity的就是一個(gè)布局容器fl_content,而fl_content是通過(guò)addView的方式將UnityPlayer添加進(jìn)容器中浪册,所以UnityPlayer也是一個(gè)View扫腺,繼續(xù)查看UnityPlayer,一下是部分代碼:

public class UnityPlayer extends FrameLayout implements d {
    public static Activity currentActivity = null;
    private static boolean n;
    c a = new c(this, (byte) 0);
    i b = null;
    private boolean c = false;
    private boolean d = true;
    private l e = new l();
    private final ConcurrentLinkedQueue f = new ConcurrentLinkedQueue();
    private BroadcastReceiver g = null;
    private boolean h = false;
    private a i = new a(this, (byte) 0);
    private TelephonyManager j;
    private j k;
    private ContextWrapper l;
    private SurfaceView m;
    private boolean o;
    private Bundle p = new Bundle();
    private n q;
    private boolean r = false;
    private ProgressBar s = null;
    private Runnable t = new Runnable() {
        public final void run() {
            int p = UnityPlayer.this.nativeActivityIndicatorStyle();
            if (p >= 0) {
                if (UnityPlayer.this.s == null) {
                    UnityPlayer.this.s = new ProgressBar(UnityPlayer.this.l, null, new int[]{16842874, 16843401, 16842873, 16843400}[p]);
                    UnityPlayer.this.s.setIndeterminate(true);
                    UnityPlayer.this.s.setLayoutParams(new LayoutParams(-2, -2, 51));
                    UnityPlayer.this.addView(UnityPlayer.this.s);
                }
                UnityPlayer.this.s.setVisibility(0);
                UnityPlayer.this.bringChildToFront(UnityPlayer.this.s);
            }
        }
    };
...

以上可以發(fā)現(xiàn)村象,其實(shí)UnityPlayer就是繼承的FrameLayout笆环,并且可以猜測(cè)這個(gè)FrameLayout中有至少一個(gè)SurfaceView顯示真正的模型場(chǎng)景等,因?yàn)槠胀╒iew是沒(méi)有這個(gè)能力去渲染的厚者,只有通過(guò)SurfaceView開(kāi)啟線程去渲染(證據(jù):在與Unity通信的時(shí)候躁劣,通過(guò)Unity的消息回調(diào)去更新UI會(huì)提示不能在非主線程中更新界面的提示)。所以原生View和Unity3D場(chǎng)景問(wèn)題其實(shí)就是在這個(gè)UnityPlayer中的SurfaceView與我們的原生View發(fā)生了Z-Index上的沖突库菲,或者說(shuō)是遮擋习绢。那么怎樣把我們的原生View放在UnityPlayer上面呢?其實(shí)我們首先需要把SurfaceView的Z-Index降低蝙昙。就像開(kāi)發(fā)地圖和多媒體播放一樣,存在地圖梧却、多媒體奇颠、普通View的顯示層級(jí),我們需要將這些層級(jí)處理一下放航,通過(guò)SurfaceView的setZOrderOnTop方法和setZOrderMediaOverlay方法烈拒。當(dāng)然如果沒(méi)有多媒體不需要設(shè)置setZOrderMediaOverlay方法。

解決問(wèn)題

直接貼代碼了:

class BaseUnityPlayer(contextWrapper: ContextWrapper) : UnityPlayer(contextWrapper) {

    override fun addView(child: View) {
        if (child is SurfaceView) {
            child.setZOrderOnTop(false)
        }
        super.addView(child)
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市荆几,隨后出現(xiàn)的幾起案子吓妆,更是在濱河造成了極大的恐慌,老刑警劉巖吨铸,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件行拢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡诞吱,警方通過(guò)查閱死者的電腦和手機(jī)舟奠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)房维,“玉大人沼瘫,你說(shuō)我怎么就攤上這事×” “怎么了耿戚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)阿趁。 經(jīng)常有香客問(wèn)我膜蛔,道長(zhǎng),這世上最難降的妖魔是什么歌焦? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任飞几,我火速辦了婚禮,結(jié)果婚禮上独撇,老公的妹妹穿的比我還像新娘屑墨。我一直安慰自己,他們只是感情好纷铣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布卵史。 她就那樣靜靜地躺著,像睡著了一般搜立。 火紅的嫁衣襯著肌膚如雪以躯。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,198評(píng)論 1 299
  • 那天啄踊,我揣著相機(jī)與錄音忧设,去河邊找鬼。 笑死颠通,一個(gè)胖子當(dāng)著我的面吹牛址晕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播顿锰,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谨垃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼启搂!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起刘陶,我...
    開(kāi)封第一講書(shū)人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤胳赌,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后匙隔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體疑苫,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年牡直,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缀匕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碰逸,死狀恐怖乡小,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饵史,我是刑警寧澤满钟,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站胳喷,受9級(jí)特大地震影響湃番,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吭露,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一吠撮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧讲竿,春花似錦泥兰、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至迈嘹,卻和暖如春削彬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背秀仲。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工融痛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人神僵。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓雁刷,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親挑豌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子安券,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,081評(píng)論 25 707
  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料氓英? 從這篇文章中你...
    hw1212閱讀 12,714評(píng)論 2 59
  • (二)Unity 與 Android的布局管理 1. 簡(jiǎn)述 在上一章我們討論了最基礎(chǔ)的Unity與Android數(shù)...
    伯夷考閱讀 3,939評(píng)論 1 6
  • 11侯勉、登門(mén)拜訪 維修豬等了好久好久,都沒(méi)有見(jiàn)到科學(xué)豬铝阐。于是址貌,他決定登門(mén)拜訪。
    _小蚊子閱讀 189評(píng)論 0 0
  • 從財(cái)富管理到教育養(yǎng)老螟凭,從個(gè)人生活到職業(yè)選擇,金融已經(jīng)貫穿到我們生活的方方面面它呀,讓我們置身于互聯(lián)網(wǎng)鏈接下的“全金融”...
    智慧的狐貍閱讀 397評(píng)論 0 1