# 合目处理

为了实现合目，除了需要相同的UI布局左右各放置一份外，每次对左边的view进行修改，同样的操作也需要在右边的布局也重新设置一遍，这两点都会引入不少繁琐的模版代码。为了解决这个问题，基于ViewBinding，SDK中提供了**BindingPair**工具类，将左右布局对应的ViewBinding对象传入它的构造函数后，通过**updateView**方法同时操作左右的布局，通过**setLeft**方法只操作左边的布局，通过checkIsLeft方法，在updateView的block中判断当前是左边的布局还是右边的布局。

### 合目组件

合目镜像操作主要由基于**ViewBinding**的**BindingPair**工具类完成，基于BindingPair封装了以下UI组件：

* Activity级别：**BaseMirrorActivity**，实现了Activity自动镜像布局视图的逻辑，开发者只用关注业务的UI需求，按照原生的xml布局方式开发布局文件就能实现合目的效果，具体可以参考sample中的FusionVisionActivity或者所有的BaseMirrorActivity子类

<figure><img src="/files/q14JPhvhrCTe3JbkVuyl" alt=""><figcaption></figcaption></figure>

* Fragment级别：**BaseMirrorFragment**，实现了Fragment级别的合目封装，添加方式与原生的Fragment一致，但是注意不能添加到BaseMirrorActivity中去,具体实现可参考sample中的FragmentDemoActivity

<figure><img src="/files/YS9psFtMibrRVWPPkT9e" alt=""><figcaption></figcaption></figure>

注：Fragment+RecyclerView合目的场景可以参考sample中的**FragmentRecyclerViewDemo**<br>

* View级别：基于组合的**MirrorContainerView** 和基于继承的**BaseMirrorContainerView**

MirrorContainerView的使用可参考MirrorContainerViewActivity，注意一定要实现**mirrorContainer.bindTo**方法，否则合目不生效；BaseMirrorContainerView的使用可参考com.ffalcon.mercury.android.sdk.demo.ui.wedget.TitleView,需要注意的是MirrorView也同样不能添加到BaseMirrorActivity中

* Toast: **FToast**，支持合目的通用toast封装，具体使用可参考FusionVisionHomeActivity
* Dialog：**FDialog**，支持合目的通用Dialog封装，具体使用可参考DialogActivity

\
上述的合目组件除了FToast和FDialog是独立于页面的组件外，其他的合目组件可以根据业务需求合理的进行组装，以达到完美的双屏合目效果

### updateView & setLeft

**mBindingPair.updateView{}：**&#x4E0A;述的合目组件均由**BindingPair**工具类实现了布局左右映射的效果，一份布局文件同时映射到左右两个屏幕，所以实质上在内存中还是同时存在两份布局的，为了处理繁琐的镜像操作，SDK提供了**mBindingPair.updateView**方法来同时处理左右布局同一个组件的更新操作。eg:

<pre class="language-kotlin"><code class="lang-kotlin">//在updateView block中的所有操作均会映射到左右两个布局文件中
<strong>mBindingPair.updateView {
</strong><strong>    tvTitle.text = "my title"
</strong><strong>}
</strong>//等价于
mBindingPair.left.tvTitle.text = "my title"
mBindingPair.right.tvTitle.text = "my title"
</code></pre>

需要注意的是，由于该特性，推荐的做法是只在updateView block中做UI层面的更新操作，**如果在该方法中进行了对外部源数据的修改或者绑定了事件，可以预知的行为是源数据的修改和绑定的事件均会执行两次**，造成不可预知的后果！！！如果有外部数据更新的操作或者对具体组件进行事件绑定的逻辑，推荐的做法是使用**mBindingPair.setLeft** 方法

\
**mBindingPair.setLeft{}：**

<pre class="language-kotlin"><code class="lang-kotlin"><strong>mBindingPair.setLeft {
</strong><strong>    focusHolder.addFocusTarget(
</strong><strong>    //一般的做法是将事件绑定在左边的视图组件上面
</strong>        FocusInfo(
            btnEvent,
<strong>            eventHandler = { action -> handleAction(action) },
</strong><strong>            focusChangeHandler = { hasFocus ->
</strong><strong>            //如果响应某个事件需要同步更新两边的组件，这个时候可以再次调用updateView的方法
</strong><strong>                mBindingPair.updateView {
</strong><strong>                    triggerFocus(hasFocus, btnEvent, mBindingPair.checkIsLeft(this))
</strong><strong>                }
</strong><strong>            }
</strong><strong>        )
</strong>    )
    focusHolder.currentFocus(mBindingPair.left.btnEvent)
<strong>}
</strong></code></pre>

当然如果复杂业务需要**updateView**中进行逻辑整合又不期望被调用两次的话，也可以在updateView的block中对当前执行区域进行判断，具体的实现如下：

<pre class="language-kotlin"><code class="lang-kotlin"><strong>mBindingPair.updateView {
</strong><strong>    //do something.......
</strong>    
    //check isLeft
    if (mBindingPair.checkIsLeft(this)){
        // only left do something.......
    }
<strong>}
</strong></code></pre>

更多具体的场景实现可以参考sample中的代码

### 视频合目（SurfaceView）

上文中的合目组件只能对于常规的UI组件进行镜像合目的操作，但对于视频播放需要用到SurfaceView这种类型的场景，还需要进行其他的特殊处理。rayneo对于这种合目场景提供了**MirroringView**组件进行支持，具体使用详情可参考sample中的VideoPlayActivity，主要的步骤如下：

1.确保布局中视频播放组件（SurfaceView）与镜像组件（MirroringView）分别处于不同的屏幕

<figure><img src="/files/NXSFdEBoxeqoKvTLkxaC" alt=""><figcaption></figcaption></figure>

2.将SurfaceView设置到MirroringView中，并开启Mirroring，此时surface开始渲染的同时也会将画面镜像到MirroringView中去

<pre class="language-kotlin"><code class="lang-kotlin">binding.textureView.let { leftTexture ->
<strong>    binding.mirrorView.setSource(leftTexture)
</strong>    binding.mirrorView.startMirroring()
<strong>}
</strong>
........

//在必要的地方停止
override fun onDestroy() {
    super.onDestroy()
    binding.mirrorView.stopMirroring()
    mPlayer?.release()
}
</code></pre>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://rayneo.gitbook.io/rayneo-devdoc/x-xi-lie/android-kai-fa/neng-li-jie-shao/he-mu-chu-li.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
