2

是否可以在leanback 展示应用程序的自定义标题视图中使用多个 SearchOrb 按钮或其他可聚焦按钮?问题是我只能聚焦一个按钮...

更新:

我做了什么:

有自定义标题视图类,其中包含搜索球、标题文本视图、自定义球和模拟时钟。

问题是当我尝试聚焦搜索球体时,焦点转到自定义球体......仅此而已。仅专注于自定义球体。

单击侦听器以设置主搜索球体。我不知道问题出在哪里。如果需要,我可以按需发布其他代码。

感谢您的帮助!

标题视图.xml:

<?xml version="1.0" encoding="utf-8"?>
    <com.olegmart.smart.base.CustomTitleView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/browse_title_group"
    android:padding="16dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

自定义标题视图.java:

package com.olegmart.smart.base;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v17.leanback.widget.SearchOrbView;
import android.support.v17.leanback.widget.TitleViewAdapter;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.ImageView;

import com.olegmart.smart.R;

public class CustomTitleView extends RelativeLayout implements TitleViewAdapter.Provider {
    private final TextView mTitleView;
    private final View mAnalogClockView;
    private final View mGridOrbView;
    private final View mSearchOrbView;

private final TitleViewAdapter mTitleViewAdapter = new TitleViewAdapter() {
    @Override
    public View getSearchAffordanceView() {
        return mSearchOrbView;
    }

    @Override
    public void setTitle(CharSequence titleText) {
        CustomTitleView.this.setTitle(titleText);
    }

    @Override
    public void setBadgeDrawable(Drawable drawable) {
        //CustomTitleView.this.setBadgeDrawable(drawable);
    }

    @Override
    public void updateComponentsVisibility(int flags) {
        /*if ((flags & BRANDING_VIEW_VISIBLE) == BRANDING_VIEW_VISIBLE) {
            updateBadgeVisibility(true);
        } else {
            mAnalogClockView.setVisibility(View.GONE);
            mTitleView.setVisibility(View.GONE);
        }*/

        int visibility = (flags & SEARCH_VIEW_VISIBLE) == SEARCH_VIEW_VISIBLE
                ? View.VISIBLE : View.INVISIBLE;
        mSearchOrbView.setVisibility(visibility);
    }

    private void updateBadgeVisibility(boolean visible) {
        if (visible) {
            mAnalogClockView.setVisibility(View.VISIBLE);
            mGridOrbView.setVisibility(View.VISIBLE);
            mTitleView.setVisibility(View.VISIBLE);

        } else {
            mAnalogClockView.setVisibility(View.GONE);
            mGridOrbView.setVisibility(View.GONE);
            mTitleView.setVisibility(View.GONE);
        }
    }
};

public CustomTitleView(Context context) {
    this(context, null);
}

public CustomTitleView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public CustomTitleView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    View root  = LayoutInflater.from(context).inflate(R.layout.custom_titleview, this);
    mTitleView = (TextView) root.findViewById(R.id.title_tv);
    mAnalogClockView = root.findViewById(R.id.clock);
    mGridOrbView = root.findViewById(R.id.grid_orb);

    mSearchOrbView = root.findViewById(R.id.search_orb);
}

public void setTitle(CharSequence title) {
    if (title != null) {
        mTitleView.setText(title);
        mTitleView.setVisibility(View.VISIBLE);
        mGridOrbView.setVisibility(View.VISIBLE);
        mAnalogClockView.setVisibility(View.VISIBLE);
    }
}


public void setBadgeDrawable(Drawable drawable) {
    if (drawable != null) {
        mTitleView.setVisibility(View.GONE);
        mGridOrbView.setVisibility(View.VISIBLE);
        mAnalogClockView.setVisibility(View.VISIBLE);
    }
}

@Override
public TitleViewAdapter getTitleViewAdapter() {
    return mTitleViewAdapter;
}
}

custom_titleview.xml:

<?xml version="1.0" encoding="utf-8"?>
<merge 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="wrap_content">

    <android.support.v17.leanback.widget.SearchOrbView
        android:id="@+id/search_orb"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:transitionGroup="true"
        android:layout_gravity="center_vertical|start"
        android:layout_marginTop="8dp"
        android:paddingStart="48dp"
        android:focusable="true"
        android:nextFocusRight="@+id/grid_orb"
        />

    <AnalogClock
            android:id="@+id/clock"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:padding="6dp"
            android:layout_alignParentEnd="true"
            android:layout_gravity="center_vertical|end"
            android:layout_marginEnd="24dp" />

    <android.support.v17.leanback.widget.SearchOrbView
        android:id="@+id/grid_orb"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="24dp"
        android:layout_centerVertical="true"
        android:layout_toStartOf="@id/clock"
        android:nextFocusLeft="@+id/search_orb"
        android:focusable="true"
        />



    <TextView
            android:id="@+id/title_tv"
            android:textAppearance="@android:style/TextAppearance.Large"
            android:visibility="gone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="24dp"
            android:layout_centerVertical="true"
            android:layout_toStartOf="@id/grid_orb" />

</merge>
4

3 回答 3

6

Teng-pao Yu 是正确的...... BrowseFragment 中的 OnFocusSearchListener 是焦点更改未按您预期发生的原因。

先说一点背景...

当发生焦点搜索时,它将首先调用当前 ViewGroup 中的 focusSearch 方法。在这种情况下,虽然搜索球具有当前焦点,并且用户按下 d-pad 以将焦点移到不同的方向,但焦点搜索的第一个回调将在您的 CustomTitleView 类中。只有CustomTitleView 将 focusSearch 传播到父级之后,BrowseFragment 的 OnFocusSearchListener 才会被调用。

这里真正的问题是(在撰写本文时),Leanback BrowseFragment 的 OnFocusSearchListener 已编码,因此您添加到 CustomTitleLayout 的其他视图将永远无法真正获得焦点。

但是,有一种方法可以将其他子视图集中在您的 CustomTitleLayout....

覆盖 CustomTitleLayout 中的 focusSearch 方法,如下面的代码示例所示,并返回一个存在于自定义标题布局中的可聚焦视图(而不是返回到 super.focusSearch)。

请注意,此代码取决于在 CustomTitleView 的子项上设置 nextFocusLeft 和 nextFocusRight 属性。此外,子视图必须是可聚焦的视图(按钮...等)。如果默认情况下子视图不可聚焦(类似于 TextView),您将必须手动启用视图,以便通过 XMLandroid:focusable="true"或在代码中使用View.setFocusable(true).

public class CustomTitleView extends RelativeLayout implements TitleViewAdapter.Provider {

    .... all your custom view stuff....

    @Override
    public View focusSearch(View focused, int direction) {

        View nextFoundFocusableViewInLayout = null;

        // Only concerned about focusing left and right at the moment
        if (direction == View.FOCUS_LEFT || direction == View.FOCUS_RIGHT) {

            // Try to find the next focusable item in this layout for the supplied direction
            int nextFoundFocusableViewInLayoutId = -1;
            switch(direction) {
                case View.FOCUS_LEFT :
                    nextFoundFocusableViewInLayoutId = focused.getNextFocusLeftId();
                    break;
                case View.FOCUS_RIGHT :
                    nextFoundFocusableViewInLayoutId = focused.getNextFocusRightId();
                    break;
            }

            // View id for next focus direction found....get the View
            if (nextFoundFocusableViewInLayoutId != -1) {
                nextFoundFocusableViewInLayout = findViewById(nextFoundFocusableViewInLayoutId);
            }
        }

        //  Return the found View in the layout if it's focusable
        if (nextFoundFocusableViewInLayout != null && nextFoundFocusableViewInLayout.isFocusable()) {
            return nextFoundFocusableViewInLayout;
        } else {
            // No focusable view found in layout...propagate to super (should invoke the BrowseFrameLayout.OnFocusSearchListener
            return super.focusSearch(focused, direction);
        }
    }

    @Override
    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
        // Gives focus to the SearchOrb first....if not...default to normal descendant focus search
        return getSearchAffordanceView().requestFocus() || super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
    }
}
于 2017-12-02T18:55:51.940 回答
2

BrowseFrameLayout正常的 focusSearch() 被拦截OnFocusSearchListener

@Override
public View focusSearch(View focused, int direction) {
    if (mListener != null) {
        View view = mListener.onFocusSearch(focused, direction);
        if (view != null) {
            return view;
        }
    }
    return super.focusSearch(focused, direction);
}

并且很可能在您的情况下返回 null ,请参阅:

private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
        new BrowseFrameLayout.OnFocusSearchListener() {
    @Override
    public View onFocusSearch(View focused, int direction) {
        // if headers is running transition,  focus stays
        if (mCanShowHeaders && isInHeadersTransition()) {
            return focused;
        }
        if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction);

        if (getTitleView() != null && focused != getTitleView() &&
                direction == View.FOCUS_UP) {
            return getTitleView();
        }
        if (getTitleView() != null && getTitleView().hasFocus() &&
                direction == View.FOCUS_DOWN) {
            return mCanShowHeaders && mShowingHeaders ?
                    mHeadersFragment.getVerticalGridView() : mMainFragment.getView();
        }

        boolean isRtl = ViewCompat.getLayoutDirection(focused) == View.LAYOUT_DIRECTION_RTL;
        int towardStart = isRtl ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
        int towardEnd = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
        if (mCanShowHeaders && direction == towardStart) {
            if (isVerticalScrolling() || mShowingHeaders || !isHeadersDataReady()) {
                return focused;
            }
            return mHeadersFragment.getVerticalGridView();
        } else if (direction == towardEnd) {
            if (isVerticalScrolling()) {
                return focused;
            } else if (mMainFragment != null && mMainFragment.getView() != null) {
                return mMainFragment.getView();
            }
            return focused;
        } else if (direction == View.FOCUS_DOWN && mShowingHeaders) {
            // disable focus_down moving into PageFragment.
            return focused;
        } else {
            return null;
        }
    }
};

一个非常丑陋的解决方法是扩展您自己的BrowseFragmentBrowseFrameLayout

于 2016-11-08T02:20:35.660 回答
0

如果您想对屏幕上的可聚焦项目进行更多自定义,您可能需要查看docsnextFocusForward中的和类似的 XML 属性。

于 2016-07-08T01:58:46.280 回答