新弟 發表於 2022-3-31 16:14:00

Android开发 焦点与TV

<h1><span style="color: rgba(0, 128, 128, 1)">前言</span></h1>
<p>&nbsp;  焦点一般在TV设备,投影仪设备开发中使用很多。因为这些设备不带触控与键鼠输入,而是使用遥控器。本博客讲解与记录焦点开发的一些功能与细节。&nbsp;</p>
<h1><span style="color: rgba(0, 128, 128, 1)">在xml里关于焦点的属性</span></h1>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 控制视图是否可以获取焦点。默认情况下,这是“自动”,它让框架确定用户是否可以将焦点移动到视图。通过将此属性设置为 true,视图可以获取焦点。通过将其设置为“false”,视图将不会获得焦点。
    此值不会影响直接调用 {@link android.view.ViewrequestFocus} 的行为,无论此视图如何,它都会始终请求焦点。
    它只影响焦点导航尝试移动焦点的位置。 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">attr </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="focusable"</span><span style="color: rgba(255, 0, 0, 1)"> format</span><span style="color: rgba(0, 0, 255, 1)">="boolean|enum"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 控制视图是否可以在触摸模式下获得焦点的布尔值。如果对于一个视图是这样,则该视图可以在单击时获得焦点,并且如果单击另一个未将此属性设置为 true 的视图,则可以保持焦点。
    这个开启后是有一个副作用的,点击一个View需要点击二次,第一次点击变成了获取焦点,第二次点击才会执行onClickListener</span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">attr </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="focusableInTouchMode"</span><span style="color: rgba(255, 0, 0, 1)"> format</span><span style="color: rgba(0, 0, 255, 1)">="boolean"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>

    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 此视图是否为默认焦点视图。每个键盘导航集群只有一个视图可以将此属性设置为 true。
    请参阅 {@link android.view.ViewsetFocusedByDefault(boolean)}。 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">attr </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="focusedByDefault"</span><span style="color: rgba(255, 0, 0, 1)"> format</span><span style="color: rgba(0, 0, 255, 1)">="boolean"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>

    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)">此视图在获得焦点但未在其背景中定义 {@link android.R.attrstate_focused} 时是否应使用默认焦点突出显示。 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">attr </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="defaultFocusHighlightEnabled"</span><span style="color: rgba(255, 0, 0, 1)"> format</span><span style="color: rgba(0, 0, 255, 1)">="boolean"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>

    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 屏幕阅读器辅助工具是否应将此视图视为可聚焦单元。
    请参阅 {@link android.view.ViewsetScreenReaderFocusable(boolean)}。
    默认值 {@code false} 让屏幕阅读器考虑其他信号,例如可聚焦性或文本的存在,以决定它关注的内容。</span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">attr </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="screenReaderFocusable"</span><span style="color: rgba(255, 0, 0, 1)"> format</span><span style="color: rgba(0, 0, 255, 1)">="boolean"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span></pre>
</div>
<p><span>下面的焦点指定通常并不需要设置,因为系统会自动检索焦点目标,除非你有特殊的需求</span></p>
<p><span>android:nextFocusForward="@+id/code_et" 下一个焦点目标</span></p>
<p>android:nextFocusUp="@id/book"&nbsp; 定义指定方向的焦点</p>
<p>android:nextFocusDown="@id/book"&nbsp;定义指定方向的焦点</p>
<p>android:nextFocusLeft="@id/book"&nbsp;定义指定方向的焦点</p>
<p>android:nextFocusRight="@id/book"&nbsp;定义指定方向的焦点</p>
<p>android:nextClusterForward="@id/book" 定义下一个键盘导航集群</p>
<h1><span style="color: rgba(0, 128, 128, 1)">android:descendantFocusability属性</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">android:descendantFocusability是View的一个属性。可以理解是viewGroup和其子控件焦点相关的属性。</span></p>
<ul>
<li>
<p>beforeDescendants :viewGroup会优先其子类控件而获取到焦点</p>
</li>
<li>
<p>afterDescendants :viewGroup只有当其子类控件不需要获取焦点时才获取焦点</p>
</li>
<li>
<p>blocksDescendants :viewGroup会覆盖子类控件而直接获得焦点</p>
</li>
</ul>
<h1><span style="color: rgba(0, 128, 128, 1)">设置默认焦点</span></h1>
<div class="cnblogs_code">
<pre>mBinding.piano.requestFocus()</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">监听某个view的焦点变化</span></h1>
<p><span style="color: rgba(0, 128, 128, 1)">获得焦点或者失去焦点</span></p>
<div class="cnblogs_code">
<pre>   mBinding.piano.setOnFocusChangeListener { view, b -&gt;<span style="color: rgba(0, 0, 0, 1)">
            Log.e(</span>"zh", "piano $b "<span style="color: rgba(0, 0, 0, 1)">)
      }</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">监听指定View的焦点按下的键值</span></h1>
<div class="cnblogs_code">
<pre>      mBinding.brightnessSeekBar.setOnKeyListener(View.OnKeyListener { view, i, keyEvent -&gt;
            <span style="color: rgba(0, 0, 255, 1)">if</span> (i ==<span style="color: rgba(0, 0, 0, 1)"> KeyEvent.KEYCODE_ENTER) {
                </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (mUnfoldBrightnessLayout) {
                  mUnfoldBrightnessLayout </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
                  mBinding.brightnessLayout.isFocusable </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
                  mBinding.brightnessLayout.isSelected </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
                  mBinding.unfoldBrightnessGroup.visibility </span>=<span style="color: rgba(0, 0, 0, 1)"> View.GONE
                  mBinding.brightnessLayout.requestFocus()
                  animationUp(mBinding.brightnessIcon2)
                }
                </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
            }
            </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
      })</span></pre>
</div>
<p>&nbsp;</p>
<h1><span style="color: rgba(0, 128, 128, 1)">监听全局焦点</span></h1>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span> val focusChangeListener: ViewTreeObserver.OnGlobalFocusChangeListener =<span style="color: rgba(0, 0, 0, 1)"> object : ViewTreeObserver.OnGlobalFocusChangeListener {
      override fun onGlobalFocusChanged(oldFocus: View</span>?, newFocus: View?<span style="color: rgba(0, 0, 0, 1)">) {
            Log.e(</span>"zh", "oldFocus ==" + oldFocus + " id:" + oldFocus?<span style="color: rgba(0, 0, 0, 1)">.id)
            Log.e(</span>"zh", "newFocus ==" + newFocus + " id:" + newFocus?<span style="color: rgba(0, 0, 0, 1)">.id)
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (newFocus != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (mBinding.viewPage.currentItem == 1 &amp;&amp; (newFocus.id ==<span style="color: rgba(0, 0, 0, 1)"> R.id.hut
                            </span>|| newFocus.id == R.id.tv || newFocus.id == R.id.song || newFocus.id ==<span style="color: rgba(0, 0, 0, 1)"> R.id.story)
                ) {
                  mBinding.viewPage.currentItem </span>= 0
                  <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
                }
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (mBinding.viewPage.currentItem == 1 &amp;&amp; (newFocus.id ==<span style="color: rgba(0, 0, 0, 1)"> R.id.draw
                            </span>|| newFocus.id == R.id.book || newFocus.id == R.id.piano || newFocus.id ==<span style="color: rgba(0, 0, 0, 1)"> R.id.chess)
                ) {
                  mBinding.viewPage.currentItem </span>= 2
                  <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
                }
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> ((mBinding.viewPage.currentItem == 0 || mBinding.viewPage.currentItem == 2) &amp;&amp; (newFocus.id ==<span style="color: rgba(0, 0, 0, 1)"> R.id.news
                            </span>|| newFocus.id == R.id.lwlx || newFocus.id == R.id.time || newFocus.id ==<span style="color: rgba(0, 0, 0, 1)"> R.id.chest
                            </span>|| newFocus.id == R.id.film || newFocus.id == R.id.applicationMarket || newFocus.id ==<span style="color: rgba(0, 0, 0, 1)"> R.id.weather
                            </span>|| newFocus.id ==<span style="color: rgba(0, 0, 0, 1)"> R.id.music)
                ) {
                  mBinding.viewPage.currentItem </span>= 1
                  <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
                }
            }
      }
    }</span></pre>
</div>
<p>注册</p>
<div class="cnblogs_code">
<pre>    override fun onCreate(savedInstanceState: Bundle?<span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">略。。</span>
<span style="color: rgba(0, 0, 0, 1)">      mBinding.root.getViewTreeObserver().addOnGlobalFocusChangeListener(focusChangeListener)
    }

    override fun onDestroy() {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onDestroy()
      mBinding.root.viewTreeObserver.removeOnGlobalFocusChangeListener(focusChangeListener)
    }</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">子View焦点状态跟随父布局</span></h1>
<div class="cnblogs_code">
<pre>android:duplicateParentState="true"</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">TV开发中常用函数</span></h1>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 设置焦点放大
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
fun View.setFocusEnlarge(</span><span style="color: rgba(0, 0, 255, 1)">float</span><span style="color: rgba(0, 0, 0, 1)">: Float) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setOnFocusChangeListener { view, b -&gt;
      <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (b) {
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.scaleX = <span style="color: rgba(0, 0, 255, 1)">float</span>
            <span style="color: rgba(0, 0, 255, 1)">this</span>.scaleY = <span style="color: rgba(0, 0, 255, 1)">float</span><span style="color: rgba(0, 0, 0, 1)">
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.scaleX = 1.0f
            <span style="color: rgba(0, 0, 255, 1)">this</span>.scaleY = 1.0f<span style="color: rgba(0, 0, 0, 1)">
      }
    }
}


</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 鼠标选中放大
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
fun View.setHoverEnlarge(</span><span style="color: rgba(0, 0, 255, 1)">float</span><span style="color: rgba(0, 0, 0, 1)">: Float){
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setOnHoverListener { v, event -&gt;<span style="color: rgba(0, 0, 0, 1)">
      when(event.action){
            MotionEvent.ACTION_HOVER_ENTER</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">{
                </span><span style="color: rgba(0, 0, 255, 1)">this</span>.scaleX = <span style="color: rgba(0, 0, 255, 1)">float</span>
                <span style="color: rgba(0, 0, 255, 1)">this</span>.scaleY = <span style="color: rgba(0, 0, 255, 1)">float</span><span style="color: rgba(0, 0, 0, 1)">
            }
            MotionEvent.ACTION_HOVER_EXIT</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">{
                </span><span style="color: rgba(0, 0, 255, 1)">this</span>.scaleX = 1.0f
                <span style="color: rgba(0, 0, 255, 1)">this</span>.scaleY = 1.0f<span style="color: rgba(0, 0, 0, 1)">
            }
            MotionEvent.ACTION_HOVER_MOVE</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">{}
      }
      </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
    }
}</span></pre>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>End</p>

</div>
<div id="MySignature" role="contentinfo">
    <div style="text-align: center">
    <p style="color:orange;font-size:16px;" >本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/16082399.html </p>
    <div style="color:orange;font-size:16px;">本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。 </div>
</div><br><br>
来源:https://www.cnblogs.com/guanxinjing/p/16082399.html
頁: [1]
查看完整版本: Android开发 焦点与TV