Agregar elementos en BrowseFragment Header en Android TV

He utilizado el proyecto de muestra de Google de la biblioteca de androide tv leanback como referencia.

Por lo tanto, mi pregunta es cómo agregar elementos (es decir, Button, ImageView, TextView) junto con el botón de búsqueda en BrowseFragment Header en Android TV.

Puedo ocultar el botón de la búsqueda usando el código abajo de comentario, pero no puedo capaz de agregar artículos junto con el botón de la búsqueda.

setOnSearchClickedListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(getActivity(), SearchActivity.class); startActivity(intent); } }); 

Introduzca aquí la descripción de la imagen

No estoy seguro, pero creo que, usted necesita personalizar Browse Fragment . Puede extender Browse Fragment en una clase java y luego intentar personalizarla o tratar de hacerla invisible. Puede personalizar el fragmento de fila y el fragmento de encabezado y crear un diseño de marco personalizado.

Activity_main.xml

  <android.support.v17.leanback.widget.SearchOrbView android:id="@+id/custom_search_orb" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="27dp" android:layout_marginLeft="56dp" android:layout_gravity="top|left" android:visibility="gone"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="hello"/> <FrameLayout android:id="@+id/header_container" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="top|left" /> <FrameLayout android:id="@+id/rows_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="top|left" android:layout_marginLeft="300dp" /> </com.ttnd.androidtv.views.CustomFrameLayout>` 

CustomRowFragment.java

  import android.app.LoaderManager; import android.content.Intent; import android.content.Loader; import android.graphics.Color; import android.os.Bundle; import android.support.v17.leanback.app.RowsFragment; import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.HeaderItem; import android.support.v17.leanback.widget.ImageCardView; import android.support.v17.leanback.widget.ListRow; import android.support.v17.leanback.widget.ListRowPresenter; import android.support.v17.leanback.widget.OnItemViewClickedListener; import android.support.v17.leanback.widget.Presenter; import android.support.v17.leanback.widget.Row; import android.support.v17.leanback.widget.RowPresenter; import android.support.v4.app.ActivityOptionsCompat; import android.telecom.Connection; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.Utils; import com.example.ttnd.demoapptv.R; import com.ttnd.androidtv.models.Movie; import com.ttnd.androidtv.presenter.CardPresenter; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; public class CustomRowsFragment extends RowsFragment { private ArrayObjectAdapter rowsAdapter; private static String mVideosUrl; // CustomHeadersFragment, scaled by 0.9 on a 1080p screen, is 600px wide. // This is the corresponding dip size. private static final int HEADERS_FRAGMENT_SCALE_SIZE = 300; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = super.onCreateView(inflater, container, savedInstanceState); int marginOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, HEADERS_FRAGMENT_SCALE_SIZE, getResources().getDisplayMetrics()); ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) v.getLayoutParams(); params.rightMargin -= marginOffset; v.setLayoutParams(params); rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); CardPresenter cardPresenter = new CardPresenter(); ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter); listRowAdapter.add(new Movie()); HeaderItem header = new HeaderItem(0, "01234"); rowsAdapter.add(new ListRow(header, listRowAdapter)); setAdapter(rowsAdapter); //v.setBackgroundColor(getRandomColor()); return v; } private void loadVideoData() { } private int getRandomColor() { Random rnd = new Random(); return Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256)); } public void refresh() { getView().setPadding(Utils.convertDpToPixel(getActivity(), -24), Utils.convertDpToPixel(getActivity(), 128), Utils.convertDpToPixel(getActivity(), 300), 0); } } 

CustomHeaderFragment.java

  package android.support.v17.leanback.app; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.os.Bundle; import android.support.v17.leanback.R; import android.support.v17.leanback.widget.FocusHighlightHelper; import android.support.v17.leanback.widget.ItemBridgeAdapter; import android.support.v17.leanback.widget.PresenterSelector; import android.support.v17.leanback.widget.OnItemViewSelectedListener; import android.support.v17.leanback.widget.Row; import android.support.v17.leanback.widget.RowHeaderPresenter; import android.support.v17.leanback.widget.SinglePresenterSelector; import android.support.v17.leanback.widget.VerticalGridView; import android.support.v7.widget.RecyclerView; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.view.View.OnLayoutChangeListener; import android.widget.FrameLayout; public class HeadersFragment extends BaseRowFragment { interface OnHeaderClickedListener { void onHeaderClicked(); } interface OnHeaderViewSelectedListener { void onHeaderSelected(RowHeaderPresenter.ViewHolder viewHolder, Row row); } private OnHeaderViewSelectedListener mOnHeaderViewSelectedListener; private OnHeaderClickedListener mOnHeaderClickedListener; private boolean mHeadersEnabled = true; private boolean mHeadersGone = false; private int mBackgroundColor; private boolean mBackgroundColorSet; private static final PresenterSelector sHeaderPresenter = new SinglePresenterSelector( new RowHeaderPresenter(R.layout.lb_header)); public HeadersFragment() { setPresenterSelector(sHeaderPresenter); } public void setOnHeaderClickedListener(OnHeaderClickedListener listener) { mOnHeaderClickedListener = listener; } public void setOnHeaderViewSelectedListener(OnHeaderViewSelectedListener listener) { mOnHeaderViewSelectedListener = listener; } @Override VerticalGridView findGridViewFromRoot(View view) { return (VerticalGridView) view.findViewById(R.id.browse_headers); } @Override void onRowSelected(RecyclerView parent, RecyclerView.ViewHolder viewHolder, int position, int subposition) { if (mOnHeaderViewSelectedListener != null) { if (viewHolder != null && position >= 0) { Row row = (Row) getAdapter().get(position); ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) viewHolder; mOnHeaderViewSelectedListener.onHeaderSelected( (RowHeaderPresenter.ViewHolder) vh.getViewHolder(), row); } else { mOnHeaderViewSelectedListener.onHeaderSelected(null, null); } } } private final ItemBridgeAdapter.AdapterListener mAdapterListener = new ItemBridgeAdapter.AdapterListener() { @Override public void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) { View headerView = viewHolder.getViewHolder().view; headerView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mOnHeaderClickedListener != null) { mOnHeaderClickedListener.onHeaderClicked(); } } }); headerView.setFocusable(true); headerView.setFocusableInTouchMode(true); if (mWrapper != null) { viewHolder.itemView.addOnLayoutChangeListener(sLayoutChangeListener); } else { headerView.addOnLayoutChangeListener(sLayoutChangeListener); } } }; private static OnLayoutChangeListener sLayoutChangeListener = new OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { v.setPivotX(v.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? v.getWidth() : 0); v.setPivotY(v.getMeasuredHeight() / 2); } }; @Override int getLayoutResourceId() { return R.layout.lb_headers_fragment; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); final VerticalGridView listView = getVerticalGridView(); if (listView == null) { return; } if (getBridgeAdapter() != null) { FocusHighlightHelper.setupHeaderItemFocusHighlight(listView); } view.setBackgroundColor(getBackgroundColor()); updateFadingEdgeToBrandColor(getBackgroundColor()); updateListViewVisibility(); } private void updateListViewVisibility() { final VerticalGridView listView = getVerticalGridView(); if (listView != null) { getView().setVisibility(mHeadersGone ? View.GONE : View.VISIBLE); if (!mHeadersGone) { if (mHeadersEnabled) { listView.setChildrenVisibility(View.VISIBLE); } else { listView.setChildrenVisibility(View.INVISIBLE); } } } } void setHeadersEnabled(boolean enabled) { mHeadersEnabled = enabled; updateListViewVisibility(); } void setHeadersGone(boolean gone) { mHeadersGone = gone; updateListViewVisibility(); } static class NoOverlappingFrameLayout extends FrameLayout { public NoOverlappingFrameLayout(Context context) { super(context); } @Override public boolean hasOverlappingRendering() { return false; } } private final ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() { @Override public void wrap(View wrapper, View wrapped) { ((FrameLayout) wrapper).addView(wrapped); } @Override public View createWrapper(View root) { return new NoOverlappingFrameLayout(root.getContext()); } }; @Override void updateAdapter() { super.updateAdapter(); ItemBridgeAdapter adapter = getBridgeAdapter(); if (adapter != null) { adapter.setAdapterListener(mAdapterListener); adapter.setWrapper(mWrapper); } if (adapter != null && getVerticalGridView() != null) { FocusHighlightHelper.setupHeaderItemFocusHighlight(getVerticalGridView()); } } void setBackgroundColor(int color) { mBackgroundColor = color; mBackgroundColorSet = true; if (getView() != null) { getView().setBackgroundColor(mBackgroundColor); updateFadingEdgeToBrandColor(mBackgroundColor); } } private void updateFadingEdgeToBrandColor(int backgroundColor) { View fadingView = getView().findViewById(R.id.fade_out_edge); Drawable background = fadingView.getBackground(); if (background instanceof GradientDrawable) { background.mutate(); ((GradientDrawable) background).setColors( new int[] {Color.TRANSPARENT, backgroundColor}); } } int getBackgroundColor() { if (getActivity() == null) { throw new IllegalStateException("Activity must be attached"); } if (mBackgroundColorSet) { return mBackgroundColor; } TypedValue outValue = new TypedValue(); if (getActivity().getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) { return getResources().getColor(outValue.resourceId); } return getResources().getColor(R.color.lb_default_brand_color); } @Override void onTransitionStart() { super.onTransitionStart(); if (!mHeadersEnabled) { final VerticalGridView listView = getVerticalGridView(); if (listView != null) { listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); if (listView.hasFocus()) { listView.requestFocus(); } } } } @Override void onTransitionEnd() { if (mHeadersEnabled) { final VerticalGridView listView = getVerticalGridView(); if (listView != null) { listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); if (listView.hasFocus()) { listView.requestFocus(); } } } super.onTransitionEnd(); } } 

CustomFrameLayout.java

  package com.ttnd.androidtv.views; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.widget.FrameLayout; public class CustomFrameLayout extends FrameLayout { public interface OnFocusSearchListener { View onFocusSearch(View focused, int direction); } public interface OnChildFocusListener { boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect); void onRequestChildFocus(View child, View focused); } public CustomFrameLayout(Context context) { this(context, null, 0); } public CustomFrameLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } private OnFocusSearchListener mListener; private OnChildFocusListener mOnChildFocusListener; public void setOnFocusSearchListener(OnFocusSearchListener listener) { mListener = listener; } public void setOnChildFocusListener(OnChildFocusListener listener) { mOnChildFocusListener = listener; } @Override protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { if (mOnChildFocusListener != null) { return mOnChildFocusListener.onRequestFocusInDescendants(direction, previouslyFocusedRect); } return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); } @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); } @Override public void requestChildFocus(View child, View focused) { super.requestChildFocus(child, focused); if (mOnChildFocusListener != null) { mOnChildFocusListener.onRequestChildFocus(child, focused); } } } 

CardPresenter.java

  package com.ttnd.androidtv.presenter; import android.graphics.drawable.Drawable; import android.support.v17.leanback.widget.ImageCardView; import android.support.v17.leanback.widget.Presenter; import android.util.Log; import android.view.ViewGroup; import com.bumptech.glide.Glide; import com.example.ttnd.demoapptv.R; import com.ttnd.androidtv.models.Movie; public class CardPresenter extends Presenter { private static final String TAG = "CardPresenter"; private static int CARD_WIDTH = 313; private static int CARD_HEIGHT = 176; private static int sSelectedBackgroundColor; private static int sDefaultBackgroundColor; private Drawable mDefaultCardImage; @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { Log.d(TAG, "onCreateViewHolder"); sDefaultBackgroundColor = parent.getResources().getColor(R.color.default_background); sSelectedBackgroundColor = parent.getResources().getColor(R.color.selected_background); mDefaultCardImage = parent.getResources().getDrawable(R.drawable.ic_launcher); ImageCardView cardView = new ImageCardView(parent.getContext()) { @Override public void setSelected(boolean selected) { updateCardBackgroundColor(this, selected); super.setSelected(selected); } }; cardView.setFocusable(true); cardView.setFocusableInTouchMode(true); updateCardBackgroundColor(cardView, false); return new ViewHolder(cardView); } private static void updateCardBackgroundColor(ImageCardView view, boolean selected) { int color = selected ? sSelectedBackgroundColor : sDefaultBackgroundColor; view.setBackgroundColor(color); view.findViewById(R.id.info_field).setBackgroundColor(color); } @Override public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { Movie movie = (Movie) item; ImageCardView cardView = (ImageCardView) viewHolder.view; Log.d(TAG, "onBindViewHolder"); if (movie.getCardImageUrl() != null) { cardView.setTitleText(movie.getTitle()); cardView.setContentText(movie.getStudio()); cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT); Glide.with(viewHolder.view.getContext()) .load(movie.getCardImageUrl()) .centerCrop() .error(mDefaultCardImage) .into(cardView.getMainImageView()); } } @Override public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { Log.d(TAG, "onUnbindViewHolder"); ImageCardView cardView = (ImageCardView) viewHolder.view; cardView.setBadgeImage(null); cardView.setMainImage(null); } } 

Movie.java

  package com.ttnd.androidtv.models; import android.os.Parcel; import android.os.Parcelable; import java.net.URI; import java.net.URISyntaxException; public class Movie implements Parcelable { private static final String TAG = "Movie"; static final long serialVersionUID = 727566175075960653L; private static int sCount = 0; private String mId = "0"; private String mTitle = "Title Here"; private String mDescription = "Description Here"; private String mBgImageUrl = "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%2020ft%20Search/bg.jpg"; private String mCardImageUrl = "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Zeitgeist/Zeitgeist%202010_%20Year%20in%20Review/card.jpg"; private String mVideoUrl = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"; private String mStudio = "Studio Here"; private String mCategory = "Category Here"; public Movie() { } public Movie(Parcel in){ String[] data = new String[8]; in.readStringArray(data); mId = data[0]; mTitle = data[1]; mDescription = data[2]; mBgImageUrl = data[3]; mCardImageUrl = data[4]; mVideoUrl = data[5]; mStudio = data[6]; mCategory = data[7]; } public static String getCount() { return Integer.toString(sCount); } public static void incrementCount() { sCount++; } public String getId() { return mId; } public void setId(String id) { mId = id; } public String getTitle() { return mTitle; } public void setTitle(String title) { mTitle = title; } public String getDescription() { return mDescription; } public void setDescription(String description) { mDescription = description; } public String getStudio() { return mStudio; } public void setStudio(String studio) { mStudio = studio; } public String getVideoUrl() { return mVideoUrl; } public void setVideoUrl(String videoUrl) { mVideoUrl = videoUrl; } public String getBackgroundImageUrl() { return mBgImageUrl; } public void setBackgroundImageUrl(String bgImageUrl) { mBgImageUrl = bgImageUrl; } public String getCardImageUrl() { return mCardImageUrl; } public void setCardImageUrl(String cardImageUrl) { mCardImageUrl = cardImageUrl; } public String getCategory() { return mCategory; } public void setCategory(String category) { mCategory = category; } public URI getBackgroundImageURI() { try { return new URI(getBackgroundImageUrl()); } catch (URISyntaxException e) { return null; } } public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeStringArray(new String[] {mId, mTitle, mDescription, mBgImageUrl, mCardImageUrl, mVideoUrl, mStudio, mCategory}); } @Override public String toString() { StringBuilder sb = new StringBuilder(200); sb.append("Movie{"); sb.append("mId=" + mId); sb.append(", mTitle='" + mTitle + '\''); sb.append(", mVideoUrl='" + mVideoUrl + '\''); sb.append(", backgroundImageUrl='" + mBgImageUrl + '\''); sb.append(", backgroundImageURI='" + getBackgroundImageURI().toString() + '\''); sb.append(", mCardImageUrl='" + mCardImageUrl + '\''); sb.append('}'); return sb.toString(); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public Movie createFromParcel(Parcel in) { return new Movie(in); } public Movie[] newArray(int size) { return new Movie[size]; } }; } 

Encontré un blog mientras estaba en Google relacionado con este tipo de personalización. Puedes echar un vistazo a la URL a continuación: https://medium.com/building-for-android-tv

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.