美文网首页
Toolbar按键焦点无法获取

Toolbar按键焦点无法获取

作者: 梧叶已秋声 | 来源:发表于2019-12-06 10:18 被阅读0次

    在处理DocumentsUI的时候,由于是不带触屏的设备,遇到了一个问题,Toolbar按键焦点无法获取。我尝试通过改frameworks中的 一些参数,但是最终都没有解决。最后的解决办法还是自定义控件。

    <android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/coordinator_layout">
    
        <android.support.v4.widget.DrawerLayout
            android:id="@+id/drawer_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
    
                <Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?android:attr/actionBarSize"
                    android:background="?android:attr/colorPrimary"
                    android:elevation="8dp"
                    android:theme="?actionBarTheme"
                    android:popupTheme="?actionBarPopupTheme">
    
                    <com.android.documentsui.DropdownBreadcrumb
                        android:id="@+id/dropdown_breadcrumb"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="4dp"
                        android:popupTheme="?actionBarPopupTheme"
                        android:background="@android:color/transparent"
                        android:overlapAnchor="true" />
    
                </Toolbar>
    
                <FrameLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">
    
                    <include layout="@layout/directory_cluster"/>
    
                    <!-- Drawer edge is a dummy view used to capture hovering event
                         on view edge to open the drawer. (b/28345294) -->
                    <View
                        android:id="@+id/drawer_edge"
                        android:background="@android:color/transparent"
                        android:layout_width="@dimen/drawer_edge_width"
                        android:layout_height="match_parent"/>
                </FrameLayout>
    
            </LinearLayout>
    
            <LinearLayout
                android:id="@+id/drawer_roots"
                android:layout_width="256dp"
                android:layout_height="match_parent"
                android:layout_gravity="start"
                android:orientation="vertical"
                android:elevation="16dp"
                android:background="@color/drawer_background">
    
                <Toolbar
                    android:id="@+id/roots_toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?android:attr/actionBarSize"
                    android:background="?android:attr/colorPrimary"
                    android:elevation="8dp"
                    android:theme="?actionBarTheme"
                    android:popupTheme="?actionBarPopupTheme" />
    
                <FrameLayout
                    android:id="@+id/container_roots"
                    android:layout_width="match_parent"
                    android:layout_height="0dp"
                    android:layout_weight="1" />
    
            </LinearLayout>
    
        </android.support.v4.widget.DrawerLayout>
    </android.support.design.widget.CoordinatorLayout>
    

    将Toolbar这个控件设置为invisible,以及 android:layout_height="0dp"。
    这里没有设置为gone的原因在于如果设置为gone的话处理菜单栏的时候弹窗会在左侧显示,设置为invisible的话菜单栏的弹窗才会显示在右侧,但是由于invisible依旧占有高度所以需要把height修改为0dp。
    然后在后面加一个<include layout="@layout/tool_bar" />。

    <android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/coordinator_layout">
    
        <android.support.v4.widget.DrawerLayout
            android:id="@+id/drawer_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
                <Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="0dp"
                    android:background="?android:attr/colorPrimary"
                    android:elevation="8dp"
                    android:theme="?actionBarTheme"
                    android:popupTheme="?actionBarPopupTheme"
                    android:visibility="invisible">
                    <com.android.documentsui.DropdownBreadcrumb
                        android:id="@+id/dropdown_breadcrumb"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="4dp"
                        android:popupTheme="?actionBarPopupTheme"
                        android:background="@android:color/transparent"
                        android:overlapAnchor="true" />
    
                </Toolbar>
    
                <include layout="@layout/tool_bar" />
                <FrameLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">
    
                    <include layout="@layout/directory_cluster"/>
    
                    <!-- Drawer edge is a dummy view used to capture hovering event
                         on view edge to open the drawer. (b/28345294) -->
                    <View
                        android:id="@+id/drawer_edge"
                        android:background="@android:color/transparent"
                        android:layout_width="@dimen/drawer_edge_width"
                        android:layout_height="match_parent"/>
                </FrameLayout>
    
            </LinearLayout>
    
            <LinearLayout
                android:id="@+id/drawer_roots"
                android:layout_width="256dp"
                android:layout_height="match_parent"
                android:layout_gravity="start"
                android:orientation="vertical"
                android:elevation="16dp"
                android:background="@color/drawer_background">
    
                <Toolbar
                    android:id="@+id/roots_toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?android:attr/actionBarSize"
                    android:background="?android:attr/colorPrimary"
                    android:elevation="8dp"
                    android:theme="?actionBarTheme"
                    android:popupTheme="?actionBarPopupTheme" />
    
                <FrameLayout
                    android:id="@+id/container_roots"
                    android:layout_width="match_parent"
                    android:layout_height="0dp"
                    android:layout_weight="1" />
    
            </LinearLayout>
    
        </android.support.v4.widget.DrawerLayout>
    </android.support.design.widget.CoordinatorLayout>
    
    

    tool_bar具体如下所示。

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="?android:attr/actionBarSize"
        android:orientation="horizontal"
        android:background="#303F9F"
        android:gravity="center">
    
        <ImageView
            android:id="@+id/iamge_menu"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_margin="4dp"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:src="@drawable/menu" />
    
        <TextView
            android:id="@+id/document_text_view"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="4"
            android:layout_margin="4dp"
            android:text="sdcard" />
        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="6"
            android:layout_margin="4dp" />
        <ImageView
            android:id="@+id/setting_iamge_view"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_margin="4dp"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:src="@drawable/setting" />
    </LinearLayout>
    

    menu.xml

    <vector xmlns:android="http://schemas.android.com/apk/res/android"
            android:width="24dp"
            android:height="24dp"
            android:viewportWidth="24.0"
            android:viewportHeight="24.0">
        <path
            android:fillColor="#FF000000"
            android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z"/>
    </vector>
    

    setting.xml

    <vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24"
        android:viewportHeight="24">
        <path
            android:fillColor="#000000"
            android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z"/>
    </vector>
    

    menu和setting是通过Android studio的vector asset新建的,修改了一下fillColor。

    由于FilesActivity使继承于BaseActivity,所以把这个初始化写在BaseActivity中。

    //vendor\mediatek\proprietary\packages\xx\xx\src\com\android\documentsui\BaseActivity.java
    public abstract class BaseActivity
            extends Activity implements CommonAddons, NavigationViewManager.Environment {
        ...........
        protected ImageView mMenuImageView;
        protected ImageView mSettingImageView;
        protected TextView mDocumentTextView;
        ...........
     @CallSuper
        @Override
        public void onCreate(Bundle icicle) {
            ...........
            mMenuImageView = (ImageView) findViewById(R.id.iamge_menu);
            mMenuImageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    setRootsDrawerOpen(true);
                    focusSidebar();
                }
            });
            mSettingImageView = (ImageView) findViewById(R.id.setting_iamge_view);
            mSettingImageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    openOptionsMenu();
                }
            });
            mDocumentTextView = (TextView) findViewById(R.id.document_text_view);
       }
        ...........
    
         @Override
        public void onRootPicked(RootInfo root) {
            mDocumentTextView.setText(root.title);
            ...........
        }
        ...........
    }
    

    然后documentsui中还有一个问题就是由于文件内容的显示部分使用的是recyclerview,而由于recyclerview存在一个焦点乱飞的问题因此documentsui源码中定义了FocusManager这个类,FocusManager中有一个ContentScope,限定了按键移动时焦点只能在recyclerview中移动。

    public final class FocusManager implements FocusHandler {
        ...........
       @Override
        public boolean handleKey(DocumentHolder doc, int keyCode, KeyEvent event) {
            // Search helper gets first crack, for doing type-to-focus.
            if (mSearchHelper.handleKey(doc, keyCode, event)) {
                return true;
            }
    
            if (Events.isNavigationKeyCode(keyCode)) {
                // Find the target item and focus it.
                int endPos = findTargetPosition(doc.itemView, keyCode, event);
    
                if (endPos != RecyclerView.NO_POSITION) {
                    focusItem(endPos);
                }
                // Swallow all navigation keystrokes. Otherwise they go to the app's global
                // key-handler, which will route them back to the DF and cause focus to be reset.
                return true;
            }
            return false;
        }
       ...........
    }
    

    RecyclerView.NO_POSITION是recyclerview的顶部,当按键移动到recyclerview的顶部的时候,return true,消费了事件,因此不会再处理事件,按键无法移动到recyclerview之外的地方,所以需要修改使endPos = RecyclerView.NO_POSITION的时候返回false,把return true移动到判断条件if (endPos != RecyclerView.NO_POSITION)内即可。
    修后改如下

    public final class FocusManager implements FocusHandler {
        ...........
        @Override
        public boolean handleKey(DocumentHolder doc, int keyCode, KeyEvent event) {
            // Search helper gets first crack, for doing type-to-focus.
            if (mSearchHelper.handleKey(doc, keyCode, event)) {
                return true;
            }
            if (Events.isNavigationKeyCode(keyCode)) {
                // Find the target item and focus it.
                int endPos = findTargetPosition(doc.itemView, keyCode, event);
                if (endPos != RecyclerView.NO_POSITION) {
                    focusItem(endPos);
                    return true;
                }
                // Swallow all navigation keystrokes. Otherwise they go to the app's global
                // key-handler, which will route them back to the DF and cause focus to be reset.
    
            }
            return false;
        }
        ...........
    }
    

    还有 一点,由于FilesActivity中使用的SharedInputHandler实际上定义了当移动按键事件时,直接调用了focusDirectoryList,所以会按键会直接定位到recyclerview中。

    //vendor\mediatek\proprietary\packages\xx\xx\src\com\android\documentsui\SharedInputHandler.java
    public class SharedInputHandler {
        ...........
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            switch (keyCode) {
                // Unhandled ESC keys end up being rethrown back at us as BACK keys. So by returning
                // true, we make sure it always does no-op.
                case KeyEvent.KEYCODE_ESCAPE:
                    return onEscape();
    
                case KeyEvent.KEYCODE_DEL:
                    return onDelete();
    
                // This is the Android back button, not backspace.
                case KeyEvent.KEYCODE_BACK:
                    return onBack();
    
                case KeyEvent.KEYCODE_TAB:
                    return onTab();
    
                default:
                    // Instead of duplicating the switch-case in #isNavigationKeyCode, best just to
                    // leave it here.
                    if (Events.isNavigationKeyCode(keyCode)) {
                        // Forward all unclaimed navigation keystrokes to the directory list.
                        // This causes any stray navigation keystrokes to focus the content pane,
                        // which is probably what the user is trying to do.
                        mFocusManager.focusDirectoryList();
                        return true;
                    }
                    return false;
            }
        }
        ...........
    }
    

    由于正常情况下,按键移动不处理事件返回的是false,因此把这一部分屏蔽掉即可。

        public boolean onKeyDown(int keyCode, KeyEvent event) {
            switch (keyCode) {
                // Unhandled ESC keys end up being rethrown back at us as BACK keys. So by returning
                // true, we make sure it always does no-op.
                case KeyEvent.KEYCODE_ESCAPE:
                    return onEscape();
    
                case KeyEvent.KEYCODE_DEL:
                    return onDelete();
    
                // This is the Android back button, not backspace.
                case KeyEvent.KEYCODE_BACK:
                    return onBack();
    
                case KeyEvent.KEYCODE_TAB:
                    return onTab();
    
                default:
                    // Instead of duplicating the switch-case in #isNavigationKeyCode, best just to
                    // leave it here.
                    /*if (Events.isNavigationKeyCode(keyCode)) {
                        // Forward all unclaimed navigation keystrokes to the directory list.
                        // This causes any stray navigation keystrokes to focus the content pane,
                        // which is probably what the user is trying to do.
                        mFocusManager.focusDirectoryList();
                        return true;
                    }*/
                    return false;
            }
        }
    

    参考链接:
    关于Toolbar按键焦点的问题
    TV端开发之焦点处理
    TV端开发之RecyclerView

    相关文章

      网友评论

          本文标题:Toolbar按键焦点无法获取

          本文链接:https://www.haomeiwen.com/subject/hfssgctx.html