Toolbar按键焦点无法获取

2019-12-06  本文已影响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

上一篇下一篇

猜你喜欢

热点阅读