Add Items in BrowseFragment Header in Android TV
Asked Answered
D

1

6

I have used Google sample project of android tv leanback library as a reference.

So, my question is how to add items (i.e. Button, ImageView, TextView) along with search button in BrowseFragment Header in Android TV.

I can able to hide search button using commenting below code, but I can not able to add items along with search button.

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

enter image description here

Departmentalize answered 20/1, 2016 at 13:32 Comment(2)
have you found any better solution for this?Lottielotto
@abhishek Actually i did tried many ideas to customize it but none of the worked. And lastly i haven't check on this for so long.Departmentalize
R
1

I am not sure but I think, You need to Customize Browse Fragment. You can extend Browse Fragment in a java class and then try to customize it or try to make it invisible. You can Customize Row Fragment and Header Fragment and create a custom Frame layout.

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];
            }
        };
    }

I found a blog while I was googling related to this type of customization. You can take a look of URL below: https://medium.com/building-for-android-tv

Rabbi answered 8/2, 2016 at 12:43 Comment(5)
Can you please elaborate how to customize browse fragment ?Departmentalize
Can you please specify the code to customize the header fragment only?Departmentalize
You need to customize Raw Fragment and Header fragment. Also you need to make a custom frame layout, As I already shared. I think you need to do some more R&D.Rabbi
Could you answer this question of mine: #47554985Kreutzer
BaseRowFragment is package-private class, it is impossible to implement (subclass) it outside it's packageFaires

© 2022 - 2024 — McMap. All rights reserved.