Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android TV開發

Android TV開發

編輯:關於Android編程

前言

這裡主要記錄幾個TV問題的解決方案,如果對這個不感興趣的其實就不用往下看了。

這幾天有一個需求就是要求出一個TV版本的app,之前沒有具體的了解Tv版的app有手機端的app到底有什麼區別,因此就做了一下研究,寫了些Demo,在做的過程中確實出現了好幾個問題。一開始碰到這些問題時,淺嘗辄止的試了試,發現很多都沒有解決方案,本著外事問google的,search了一把,也沒有結果,可能是TV做的人比較少,網上搜出來的都是照著谷歌官方的樣例實現了一把而已,因此就仔細的研究了一下這些問題,這裡把解決這些問題的方案描述出來,希望其他人能少走彎路,如果有更好的解決方案也希望大家探討分享。

樣例

這裡我們做了一個demo,demo界面如下,以下的圖都是最終運行後截出的圖,由於是在模擬器上截圖,導致了控件加載不完全,但是在真是的機頂盒上是沒有問題的,以下的圖將就著看吧:

這裡寫圖片描述
這裡寫圖片描述

開發過程

雖然google官方寫的是手機app不用做太多改動就可以運行在Tv上,但是終究兩種還是有部分區別的,這裡還是要針對TV版做部分設置。
首先是配置文件的改動,需要在AndroidManifest中配置如下屬性:


同時還需要配置一個action為android.intent.action.MAIN,category為android.intent.category.LEANBACK_LAUNCHER的Activity,類似如下:


    
        

        
    

如果記不住上面需要配置的內容其實也沒有關系,可以新創建一個TV工程,默認創建的TV工程就已經包含了上述的配置,並且該工程就相當於一個demo了,是可以直接運行的一個工程,裡面包含了Tv開發的很多控件,如果你要學習這也是很好的學習資料了,其實後續的內容也是根據這裡的內容進行參照學習的。

這裡附帶一句,Android的sdk中的samples中的tv樣例程序直接導入是運行不起來的,需要修改很多東西,但是實質內容與新創建的工程沒有什麼區別,因此也可以不用導入樣例程序進行學習了。

根據前面的樣例圖,主界面配置頁面如下:




    

        

        

        

        
    

    

    <framelayout android:id="@+id/tab_container" android:layout_height="match_parent" android:layout_width="match_parent"></framelayout>

界面的代碼如下:

public class HomeActivity extends Activity implements View.OnClickListener {

    public static void start(Context context) {
        Intent intent = new Intent(context, HomeActivity.class);
        context.startActivity(intent);
    }

    private static final String[] TAGS = {"dial", "contact", "my"};

    private FragmentManager manager;

    private int showTabIndex = -1;

    private TextView dialTab;
    private TextView contactTab;
    private TextView myTab;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        findViews();
        setViewsListener();
        init();
        selectTab(0);
    }

    private void findViews() {
        dialTab = (TextView) findViewById(R.id.dial_tab);
        contactTab = (TextView) findViewById(R.id.contact_tab);
        myTab = (TextView) findViewById(R.id.settings_tab);
    }

    private void setViewsListener() {
        dialTab.setOnClickListener(this);
        contactTab.setOnClickListener(this);
        myTab.setOnClickListener(this);
    }

    private void init() {
        manager = getFragmentManager();
    }

    private void selectTab(int index) {
        if (index == showTabIndex) {
            return;
        }
        dialTab.setSelected(index == 0);
        contactTab.setSelected(index == 1);
        myTab.setSelected(index == 2);
        FragmentTransaction transaction = manager.beginTransaction();
        hideFragment(showTabIndex, transaction);
        showTabIndex = index;
        showFragment(showTabIndex, transaction);
        transaction.commit();
    }

    private void hideFragment(int tabIndex, FragmentTransaction transaction) {
        Fragment fragment = getFragmentByIndex(tabIndex);
        if (fragment != null) {
            transaction.hide(fragment);
        }
    }

    private Fragment getFragmentByIndex(int index) {
        if (index >= 0 && index < TAGS.length) {
            return manager.findFragmentByTag(TAGS[index]);
        }
        return null;
    }

    private void showFragment(int tabIndex, FragmentTransaction transaction) {
        Fragment fragment = getFragmentByIndex(tabIndex);
        if (fragment == null) {
            switch (tabIndex) {
                case 0:
                    fragment = new DialFragment();
                    break;
                case 1:
                  /*  fragment = new ContactFragment();*/
                    fragment = new VerticalGridFragment();
                    break;
                case 2:
                    fragment = new MyFragment();
                    break;
            }
            transaction.add(R.id.tab_container, fragment, TAGS[tabIndex]);
            //transaction.addToBackStack(TAGS[tabIndex]);
        } else {
            transaction.show(fragment);
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.dial_tab:
                selectTab(0);
                return;
            case R.id.contact_tab:
                selectTab(1);
                return;
            case R.id.settings_tab:
                selectTab(2);
                //                VerticalGridActivity.start(this);
                return;
        }
    }

}

該界面主要采用Fragment來實現三個界面,分別為撥號頁面,好友,設置界面,其中撥號界面又包含兩個子的Fragment,我們來繼續看看撥號界面與好友界面,設置界面是一個充數的界面啥都沒有做。

首先來看看撥號界面的配置代碼:



    <framelayout android:id="@+id/dial_pan" android:layout_height="match_parent" android:layout_weight="1" android:layout_width="0dp">
    </framelayout>

    <framelayout android:id="@+id/contact_pan" android:layout_height="match_parent" android:layout_marginleft="@dimen/gap_12_dp" android:layout_weight="3" android:layout_width="0dp"></framelayout>

對應的界面代碼如下:

public class DialFragment extends Fragment{

    public DialFragment() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @return A new instance of fragment DialFragment.
     */
    public static DialFragment newInstance() {
        DialFragment fragment = new DialFragment();
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_dial, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        addFragments();;
    }

    private void addFragments() {
        FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
        transaction.replace(R.id.dial_pan, new DialPanFragment());
        VerticalGridFragment fragment = new VerticalGridFragment();
        Bundle args = new Bundle();
        args.putInt(Extra.COLUMNS, Extra.DIAL_COLUMNS);
        fragment.setArguments(args);
        transaction.replace(R.id.contact_pan, fragment);
        transaction.commit();
    }

}

撥號界面被分成了兩部分,一部分為撥號盤,一部分為聯系人,分別占據了屏幕一份和三份,右邊的聯系人與主界面的好用共用了同一個Fragment,因此這裡我們再看看接下來的兩個界面,首先我們看看撥號盤的界面代碼。

由於只做展示,因此代碼寫的很粗糙,界面直接寫了N個按鈕的代碼,配置界面如下:



    

    

        

            
        

        

            
        

        

            
        
    

    

        

            
        

        

            
        

        

            
        
    

    

        

            
        

        

            
        

        

            
        
    

    

        

            
        

        

            
        

        

            
        
    

    

對應的界面代碼如下:


public class DialPanFragment extends Fragment implements View.OnClickListener {

    private TextView showPhone;

    private ImageView dialBnt;

    public DialPanFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_dial_pan, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        findViews();
    }

    private void findViews() {
        showPhone = (TextView) getView().findViewById(R.id.show_phone);
        dialBnt = (ImageView) getView().findViewById(R.id.dial_icon);
        dialBnt.setOnClickListener(this);
        dialBnt.setTag(-2);
        dialBnt.setEnabled(false);
        View view0 = getView().findViewById(R.id.input_key_number_0);
        view0.setTag(0);
        view0.setOnClickListener(this);
        View view1 = getView().findViewById(R.id.input_key_number_1);
        view1.setTag(1);
        view1.setOnClickListener(this);
        view1.setNextFocusUpId(R.id.dial_tab);
        View view2 = getView().findViewById(R.id.input_key_number_2);
        view2.setTag(2);
        view2.setOnClickListener(this);
        view2.setNextFocusUpId(R.id.dial_tab);
        View view3 = getView().findViewById(R.id.input_key_number_3);
        view3.setTag(3);
        view3.setOnClickListener(this);
        view3.setNextFocusUpId(R.id.dial_tab);
        View view4 = getView().findViewById(R.id.input_key_number_4);
        view4.setTag(4);
        view4.setOnClickListener(this);
        View view5 = getView().findViewById(R.id.input_key_number_5);
        view5.setTag(5);
        view5.setOnClickListener(this);
        View view6 = getView().findViewById(R.id.input_key_number_6);
        view6.setTag(6);
        view6.setOnClickListener(this);
        View view7 = getView().findViewById(R.id.input_key_number_7);
        view7.setTag(7);
        view7.setOnClickListener(this);
        View view8 = getView().findViewById(R.id.input_key_number_8);
        view8.setTag(8);
        view8.setOnClickListener(this);
        View view9 = getView().findViewById(R.id.input_key_number_9);
        view9.setTag(9);
        view9.setOnClickListener(this);
        View viewDel = getView().findViewById(R.id.input_key_number_del);
        viewDel.setTag(-1);
        viewDel.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        int tag = (int) v.getTag();
        if (tag == -2) {
            dial();
        } else if (tag == -1) {// DEL
            delNumber();
        } else {
            inputNumber(tag);
        }
    }

    private void delNumber() {
        String text = showPhone.getText().toString();
        if (text != null && text.length() > 0) {
            text = text.substring(0, text.length() - 1);
            showPhone.setText(text);
        }
        dialBtnState(text);
    }

    private void inputNumber(int tag) {
        String text = showPhone.getText().toString();
        if (text == null) {
            text = new String(String.valueOf(tag));
        } else {
            text = text + tag;
        }
        dialBtnState(text);
        showPhone.setText(text);
    }

    private void dial() {
        String text = showPhone.getText().toString();
        int len = TextUtils.isEmpty(text) ? 0 : text.length();
        if (len != 11) {
            ToastUtil.showToast("你輸入的賬號不合法!");
            showPhone.setText("");
        } else {
            String uid = ContactProvider.getUidByPhone(text);
            if (TextUtils.isEmpty(uid)) {
                ToastUtil.showToast("該賬號不存在!");
            } else {
                // TODO
            }
        }
    }

    private void dialBtnState(String text) {
        dialBnt.setEnabled(!TextUtils.isEmpty(text));
    }
}

最後我們再來看看好友界面,改界面本地是沒有xml的,因此我們直接來看看代碼:

這裡將使用到數據bean,與數據源的代碼也貼出來如下:

public class Contact implements Parcelable {

    private String phone;

    private int headResId;

    private String name;

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public int getHeadResId() {
        return headResId;
    }

    public void setHeadResId(int headResId) {
        this.headResId = headResId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Contact() {

    }


    public Contact(Parcel in) {
        phone = in.readString();
        headResId = in.readInt();
        name = in.readString();
    }

    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(phone);
        dest.writeInt(headResId);
        dest.writeString(name);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(200);
        sb.append("Contact{");
        sb.append("phone='" + phone + '\'');
        sb.append(", headResId='" + headResId + '\'');
        sb.append(", name='" + name + '\'');
        sb.append('}');
        return sb.toString();
    }

    public static final Creator CREATOR = new Creator() {
        public Contact createFromParcel(Parcel in) {
            return new Contact(in);
        }

        public Contact[] newArray(int size) {
            return new Contact[size];
        }
    };
}

//////
public class ContactProvider {

    private static List contactList;
    private static Context sContext;

    private static int[] head = {R.drawable.avater1, R.drawable.avater2, R.drawable.avater3, R.drawable.avater4, R
            .drawable.avater5, R.drawable.avater6, R.drawable.avater7, R.drawable.avater8, R.drawable.avater9, R
            .drawable.avater10, R.drawable.avater11, R.drawable.avater12};


    private static String[] names = {"夢潔", "雅靜", "韻寒", "莉姿", "沛玲", "欣妍", "歆瑤", "凌菲", "靖瑤", "瑾萱", "芳蕤", "若華"};


    private static String[] phones = {"18618188630", "18158103936", "18620145337", "15116333186", "18618188630",
            "18158103936", "18620145337", "15116333186", "18618188630", "18158103936", "18620145337", "18767106408"};

    public static void setContext(Context context) {
        if (sContext == null)
            sContext = context;
    }

    public static List getContactList() {
        buildContact();
        return contactList;
    }

    public static List buildContact() {
        if (null != contactList) {
            return contactList;
        }
        contactList = new ArrayList();
        for (int i = 0; i < 12; ++i) {
            contactList.add(buildContactInfo(phones[i], names[i], head[i]));
        }
        return contactList;
    }

    private static Contact buildContactInfo(String phone, String name, int resId) {
        Contact contact = new Contact();
        contact.setPhone(phone);
        contact.setName(name);
        contact.setHeadResId(resId);
        return contact;
    }
}
/*
 * VerticalGridFragment shows a grid of videos
 */
public class VerticalGridFragment extends android.support.v17.leanback.app.VerticalGridFragment {
    private static final String TAG = "VerticalGridFragment";

    private static final int DEFAULT_COLUMNS = 4;

    private int numColumns = DEFAULT_COLUMNS;

    private ArrayObjectAdapter mAdapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "onCreate");
        super.onCreate(savedInstanceState);
        //setTitle(getString(R.string.vertical_grid_title));
        getParams();
        setupFragment();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View root = super.onCreateView(inflater, container, savedInstanceState);
        return root;
    }

    private void setupFragment() {
        VerticalGridPresenter gridPresenter = new VerticalGridPresenter(FocusHighlight.ZOOM_FACTOR_NONE);
        gridPresenter.setNumberOfColumns(numColumns);
        //gridPresenter.setShadowEnabled(false);
        setGridPresenter(gridPresenter);

        mAdapter = new ArrayObjectAdapter(new ContactPresenter());

        List contacts = ContactProvider.getContactList();
        mAdapter.addAll(0, contacts);

        setAdapter(mAdapter);

        setOnItemViewClickedListener(new ItemViewClickedListener());
        setOnItemViewSelectedListener(new ItemViewSelectedListener());
    }

    public void getParams() {
        if (getArguments() != null) {
            numColumns = getArguments().getInt(Extra.COLUMNS);
        }
    }

    private final class ItemViewClickedListener implements OnItemViewClickedListener {
        @Override
        public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder
                rowViewHolder, Row row) {

            if (item instanceof Contact) {
                Contact contact = (Contact) item;
                // TODO
            }
        }
    }

    private final class ItemViewSelectedListener implements OnItemViewSelectedListener {
        @Override
        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder
                rowViewHolder, Row row) {
        }
    }

}

在Fragment中我們自己實現了一個ContactPresenter,該Presenter是仿照官方的CardPresenter,但是CardPresenter中使用的ImageCardView是系統support包中提供的控件,而ContactPresenter中使用的是自己自定義的控件, 代碼如下:

public class ContactPresenter extends Presenter {
    private static final String TAG = "CardPresenter";
    private static int sSelectedBackgroundColor;
    private static int sDefaultBackgroundColor;

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent) {
        Log.d(TAG, "onCreateViewHolder");

        sDefaultBackgroundColor = parent.getResources().getColor(R.color.white_35_transparent);
        sSelectedBackgroundColor = parent.getResources().getColor(R.color.white_60_transparent);


        ContactView contactView = new ContactView(parent.getContext()) {
            @Override
            public void setSelected(boolean selected) {
                updateCardBackgroundColor(this, selected);
                super.setSelected(selected);
            }
        };

        contactView.setFocusable(true);
        contactView.setFocusableInTouchMode(true);
        updateCardBackgroundColor(contactView, false);
        return new ViewHolder(contactView);
    }

    private static void updateCardBackgroundColor(ContactView view, boolean selected) {
        int color = selected ? sSelectedBackgroundColor : sDefaultBackgroundColor;
        view.setBackgroundColor(color);
        //view.findViewById(R.id.info_field).setBackgroundColor(color);
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, Object item) {
        Contact contact = (Contact) item;
        ContactView contactView = (ContactView) viewHolder.view;
        Log.d(TAG, "onBindViewHolder");
        contactView.setHead(contact.getHeadResId());
        contactView.setName(contact.getName());
        contactView.setPhone(contact.getPhone());

    }

    @Override
    public void onUnbindViewHolder(ViewHolder viewHolder) {
        Log.d(TAG, "onUnbindViewHolder");
        ContactView contactView = (ContactView) viewHolder.view;
        // Remove references to images so that the garbage collector can free up memory
        contactView.setHead(0);
    }
}

ContactView是一個繼承自LinearLayout的自定義控件,包含了一個ImageView和兩個TextView。

到此整個界面的代碼就完成了,接下來我們來看看遇到的問題。

問題列表

問題1:控件遙控器不能選中,不能導航

出現這種問題往往是控件沒有設置android:focusable=”true”屬性,只有默認能夠選中焦點的才不需要設置改屬性,比如Button,EditText。

問題2:控件選中後,看不出選中效果

由於默認選中是沒有視覺效果的,因此你需要對控件設置選中效果,比如說背景圖片,以前在手機上可能只需要設置selector中的pressed屬性,或者selected屬性,現在針對TV你必須要設置focused屬性,比如撥號鍵盤選中後會出現一個圓形的選中背景框, 如下:

這裡寫圖片描述

要實現上述效果,因此對每一鍵盤輸入按鈕添加如下的selector。




    
    
    
    



key_board_hover.xml


    
    

問題3:TV launcher中沒有入口圖標

如果需要出現入口圖標,你必須要在AndroidManifest中配置action為android.intent.action.MAIN,category為android.intent.category.LAUNCHER的Activity。該配置與上面的LEANBACK_LAUNCHER不沖突,可以對入口Activity配置LAUNCHER,之後一個頁面配置LEANBACK_LAUNCHER,配置如下:


    
        

        
    

問題4:TV launcher中的圖標不清晰,太糊

如果直接將手機app的launcher圖標直接使用到TV中,則圖標會拉伸,由於TV的圖標往往都比較大,拉伸後就會變糊,因此需要重新切launcher圖標,手機裡面是48*48, 72*72,96*96等,而tv需要更大的尺寸,雖然沒有在官方找到建議的尺寸,但是這裡推薦一個尺寸180*180,可以多個文件夾都放同一個圖標,這樣界面加載的圖標就會變得清晰。

問題5:遙控器導航下一個不是自己希望導航的控件

系統中如果界面中有多個可focus的控件,上下左右導航,則會找到與當前控件最鄰近的控件作為下一個選中的控件,因此如果你確切想指定下一個導航的控件,則可以指定下一個控件的ID,只要該id在當前顯示的界面中,比如向上 view1.setNextFocusUpId(R.id.dial_tab);

問題6:官方VerticalGridFragment加載後,默認選中第一個,但是第一個占據了整個界面。

該問題應該是官方的一個bug,如果不是第一次加載VerticalGridFragment,則不會出現該問題,並且我嘗試了多個版本的,都會出現該問題,原因是選中後系統會在在選中的控件後插入兩幀NonOverlappingView,插入的布局代碼如下:


    
    

該布局插入了兩幀NonOverlappingView,每一幀都使用了一個.9圖標作為背景,而當系統第一次加載時,最終第一個選中的控件寬高計算錯誤,計算成了一個16777211類似的一個值,遠遠超出了界面的大小,解決方案如下:

方案1,將布局中的match_parent改為wrap_content
方案2,對VerticalGridFragment中使用的VerticalGridPresenter設置ShadowEnabled,如gridPresenter.setShadowEnabled(false);
方案3,替換掉.9圖片

問題6:官方VerticalGridFragment加載後,如果ArrayObjectAdapter使用的是自己實現的Presenter,而Presenter使用的不是系統提供的ImageCardView,則會導致選中效果不居中,當選中效果放大後會向右向下覆蓋,而不是在當前位置放大覆蓋四周。

該問題,我查了對應的style、只有針對ImageCardView的style,我也還沒有仔細研究怎麼調整,不過這裡給出一個避免的方案,對VerticalGridPresenter選中後的高亮效果選擇為不放大,如new VerticalGridPresenter(FocusHighlight.ZOOM_FACTOR_NONE)。

問題6:VerticalGridFragment頂層控件不能向上導航,比如不能導航到撥號,好友控件

該問題其實是被系統給攔截了。系統的VerticalGridFragment加載了lb_vertical_grid_fragment布局,該布局包含了一個BrowseFrameLayout,對
BrowseFrameLayout設置了setOnFocusSearchListener。如下:

    private void setupFocusSearchListener() {
        BrowseFrameLayout browseFrameLayout = (BrowseFrameLayout) getView().findViewById(
                R.id.grid_frame);
        browseFrameLayout.setOnFocusSearchListener(getTitleHelper().getOnFocusSearchListener());
    }

當系統在VerticalGridPresenter最頂層時,向上找最近一個控件時,發現當前布局已經沒有控件,則會向父布局查找,代碼如下:

public View focusSearch(View focused, int direction) {
    if (isRootNamespace()) {
        // root namespace means we should consider ourselves the top of the
        // tree for focus searching; otherwise we could be focus searching
        // into other tabs.  see LocalActivityManager and TabHost for more info
        return FocusFinder.getInstance().findNextFocus(this, focused, direction);
    } else if (mParent != null) {
        return mParent.focusSearch(focused, direction);
    }
    return null;
}

而VerticalGridPresenter的父布局則是BrowseFrameLayout,因此最終執行的是上面設置的getTitleHelper().getOnFocusSearchListener(),我們去看看改listener:

 private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
            new BrowseFrameLayout.OnFocusSearchListener() {
        @Override
        public View onFocusSearch(View focused, int direction) {
            if (focused != mTitleView && direction == View.FOCUS_UP) {
                return mTitleView;
            }
            final boolean isRtl = ViewCompat.getLayoutDirection(focused) ==
                    View.LAYOUT_DIRECTION_RTL;
            final int forward = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
            if (mTitleView.hasFocus() && direction == View.FOCUS_DOWN || direction == forward) {
                return mSceneRoot;
            }
            return null;
        }
};

發現問題所在沒有,當focused != mTitleView && direction == View.FOCUS_UP時,強制指定了mTitleView,就算沒有沒有顯示title,效果也一樣。我認為這應該算系統的一個bug,那怎麼解決吶?

我們可以重寫一個一模一樣的lb_vertical_grid_fragment,自己寫的布局會覆蓋掉系統的布局,再將BrowseFrameLayout重寫成我們自己的BrowseFrameLayout。如下

public class BrowseFrameLayout extends android.support.v17.leanback.widget.BrowseFrameLayout {
    public BrowseFrameLayout(Context context) {
        super(context);
    }

    public BrowseFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public BrowseFrameLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * Sets a {@link OnFocusSearchListener}.
     */
    public void setOnFocusSearchListener(OnFocusSearchListener listener) {

    }
}

這樣就可以實現向上導航的功能了。

總結

可能還有這樣或者那樣的問題這裡沒有遇到,希望大家指正上面的問題,也可以探討遇到的新問題。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved