TV'de çalışan bir medya uygulamasının, kullanıcıların sunduğu içeriklere göz atmasına olanak tanıması ve seçip içeriği oynatmaya başlayabilirsiniz. İçeriğe göz atma deneyimi basit ve sezgisel, aynı zamanda görsel açıdan hoş ve ilgi çekici olmalıdır.
Bu kılavuzda, androidx.leanback kitaplığı tarafından sağlanan sınıfların nasıl kullanılacağı açıklanmaktadır. (uygulamanızın medya kataloğundaki müzik veya videolara göz atmak için kullanıcı arayüzü)
Not: Burada gösterilen uygulama örneğinde
BrowseSupportFragment
kullanımdan kaldırılan BrowseFragment
yerine
sınıfını kullanır. BrowseSupportFragment
, AndroidX'i genişletir
Fragment
sınıf,
cihazlar ve Android sürümleri arasında tutarlı davranış sağlanmasına yardımcı oluyor.
![Uygulama ana ekranı](https://developer.android.com/static/images/tv/app-browse.png?authuser=1&hl=tr)
Şekil 1. Leanback örnek uygulamasının göz atma parçası, video kataloğu verilerini gösterir.
Medyaya göz atma düzeni oluştur
BrowseSupportFragment
Leanback kullanıcı arayüzü araç setindeki sınıf
, kategorilere ve medya öğesi satırlarına göz atmak için bir
tercih edebilirsiniz. Aşağıdaki örnekte,
BrowseSupportFragment
nesne:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main_frame" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:name="com.example.android.tvleanback.ui.MainFragment" android:id="@+id/main_browse_fragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
Uygulamanın ana etkinliği, aşağıdaki örnekte gösterildiği gibi bu görünümü ayarlar:
Kotlin
class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) } ...
Java
public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } ...
BrowseSupportFragment
yöntemleri görünümü
video verilerini ve kullanıcı arayüzü öğelerini ayarlayabilir, simge, başlık ve
ve kategori başlıklarının etkin olup olmadığını gösterir.
Uygulamanın, BrowseSupportFragment
parametresini uygulayan alt sınıfı
Yöntemleri, kullanıcı arayüzü öğelerinde kullanıcı işlemleri için etkinlik işleyiciler de ayarlar ve
arka plan yöneticisini ekleyin:
Kotlin
class MainFragment : BrowseSupportFragment(), LoaderManager.LoaderCallbacks<HashMap<String, List<Movie>>> { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) loadVideoData() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) prepareBackgroundManager() setupUIElements() setupEventListeners() } ... private fun prepareBackgroundManager() { backgroundManager = BackgroundManager.getInstance(activity).apply { attach(activity?.window) } defaultBackground = resources.getDrawable(R.drawable.default_background) metrics = DisplayMetrics() activity?.windowManager?.defaultDisplay?.getMetrics(metrics) } private fun setupUIElements() { badgeDrawable = resources.getDrawable(R.drawable.videos_by_google_banner) // Badge, when set, takes precedent over title title = getString(R.string.browse_title) headersState = BrowseSupportFragment.HEADERS_ENABLED isHeadersTransitionOnBackEnabled = true // Set header background color brandColor = ContextCompat.getColor(requireContext(), R.color.fastlane_background) // Set search icon color searchAffordanceColor = ContextCompat.getColor(requireContext(), R.color.search_opaque) } private fun loadVideoData() { VideoProvider.setContext(activity) videosUrl = getString(R.string.catalog_url) loaderManager.initLoader(0, null, this) } private fun setupEventListeners() { setOnSearchClickedListener { Intent(activity, SearchActivity::class.java).also { intent -> startActivity(intent) } } onItemViewClickedListener = ItemViewClickedListener() onItemViewSelectedListener = ItemViewSelectedListener() } ...
Java
public class MainFragment extends BrowseSupportFragment implements LoaderManager.LoaderCallbacks<HashMap<String, List<Movie>>> { } ... @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); loadVideoData(); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); prepareBackgroundManager(); setupUIElements(); setupEventListeners(); } ... private void prepareBackgroundManager() { backgroundManager = BackgroundManager.getInstance(getActivity()); backgroundManager.attach(getActivity().getWindow()); defaultBackground = getResources() .getDrawable(R.drawable.default_background); metrics = new DisplayMetrics(); getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics); } private void setupUIElements() { setBadgeDrawable(getActivity().getResources() .getDrawable(R.drawable.videos_by_google_banner)); // Badge, when set, takes precedent over title setTitle(getString(R.string.browse_title)); setHeadersState(HEADERS_ENABLED); setHeadersTransitionOnBackEnabled(true); // Set header background color setBrandColor(ContextCompat.getColor(requireContext(), R.color.fastlane_background)); // Set search icon color setSearchAffordanceColor(ContextCompat.getColor(requireContext(), R.color.search_opaque)); } private void loadVideoData() { VideoProvider.setContext(getActivity()); videosUrl = getString(R.string.catalog_url); getLoaderManager().initLoader(0, null, this); } private void setupEventListeners() { setOnSearchClickedListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(getActivity(), SearchActivity.class); startActivity(intent); } }); setOnItemViewClickedListener(new ItemViewClickedListener()); setOnItemViewSelectedListener(new ItemViewSelectedListener()); } ...
Kullanıcı arayüzü öğelerini ayarlama
Önceki örnekte, setupUIElements()
gizli yönteminde birden fazla
BrowseSupportFragment
içerik kataloğu tarayıcısının stilini belirleme yöntemleri:
setBadgeDrawable()
. belirtilen çekilebilir kaynağı göz atma parçasının sağ üst köşesine yerleştirir: resimler 1 ve 2'de gösterilmiştir. Bu yöntem, başlık dizesinisetTitle()
çağrılırsa çekilebilir kaynak. Çekilebilir kaynak 52 dp olmalıdır uzun.setTitle()
. başlık dizesini göz atma parçasının sağ üst köşesindeki ayarlarsetBadgeDrawable()
çağrıldı.setHeadersState()
. vesetHeadersTransitionOnBackEnabled()
üstbilgileri gizler veya devre dışı bırakır. Daha fazla bilgi için Üstbilgileri gizleme veya devre dışı bırakma bölümüne bakın.setBrandColor()
. göz atma parçasındaki, özellikle de üstbilgideki kullanıcı arayüzü öğelerinin arka plan rengini ayarlar bölüm arka plan rengi (belirtilen renk değerine sahip).setSearchAffordanceColor()
. arama simgesinin rengini belirtilen renk değerine ayarlar. Arama simgesi simgesi, şekil 1 ve 2'de gösterildiği gibi göz atma parçasının sol üst köşesinde görünür.
Başlık görünümlerini özelleştirme
Şekil 1'de gösterilen göz atma parçası, video kategorisi adlarını, Bunlar, metin görünümlerinde video veritabanındaki satır başlıklarıdır. Ayrıca üstbilgisini, daha karmaşık bir düzende ek görünümler içerecek şekilde düzenleyebilirsiniz. Aşağıdaki bölümlerde, Şekil 2'de gösterildiği gibi, kategori adının yanında bir simge görüntüleyen bir resim görünümü ekleyin.
![Uygulama ana ekranı](https://developer.android.com/static/images/tv/custom-head.png?authuser=1&hl=tr)
Şekil 2. Göz atma parçasında, hem simge hem de ve bir metin etiketi ekleyin.
Satır başlığının düzeni şu şekilde tanımlanır:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/header_icon" android:layout_width="32dp" android:layout_height="32dp" /> <TextView android:id="@+id/header_label" android:layout_marginTop="6dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
Bir Presenter
kullanın ve
görünüm sahibini oluşturmak, bağlamak ve bağlantısını kaldırmak için soyut yöntemler kullanır. Aşağıdakiler
örnek, iki görünümle izleyiciyi birbirine nasıl bağlayacağınızı gösteriyor.
ImageView
ve TextView
.
Kotlin
class IconHeaderItemPresenter : Presenter() { override fun onCreateViewHolder(viewGroup: ViewGroup): Presenter.ViewHolder { val view = LayoutInflater.from(viewGroup.context).run { inflate(R.layout.icon_header_item, null) } return Presenter.ViewHolder(view) } override fun onBindViewHolder(viewHolder: Presenter.ViewHolder, o: Any) { val headerItem = (o as ListRow).headerItem val rootView = viewHolder.view rootView.findViewById<ImageView>(R.id.header_icon).apply { rootView.resources.getDrawable(R.drawable.ic_action_video, null).also { icon -> setImageDrawable(icon) } } rootView.findViewById<TextView>(R.id.header_label).apply { text = headerItem.name } } override fun onUnbindViewHolder(viewHolder: Presenter.ViewHolder) { // no-op } }
Java
public class IconHeaderItemPresenter extends Presenter { @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup) { LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); View view = inflater.inflate(R.layout.icon_header_item, null); return new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder viewHolder, Object o) { HeaderItem headerItem = ((ListRow) o).getHeaderItem(); View rootView = viewHolder.view; ImageView iconView = (ImageView) rootView.findViewById(R.id.header_icon); Drawable icon = rootView.getResources().getDrawable(R.drawable.ic_action_video, null); iconView.setImageDrawable(icon); TextView label = (TextView) rootView.findViewById(R.id.header_label); label.setText(headerItem.getName()); } @Override public void onUnbindViewHolder(ViewHolder viewHolder) { // no-op } }
D-pad'in kullanılabilmesi için başlıklarınıza odaklanılabilir olmalıdır. arasında gezinin. Bunu yönetmenin iki yolu vardır:
onBindViewHolder()
ürününde görünümünüzü odaklanılabilir olacak şekilde ayarlayın:Kotlin
override fun onBindViewHolder(viewHolder: Presenter.ViewHolder, o: Any) { val headerItem = (o as ListRow).headerItem val rootView = viewHolder.view rootView.focusable = View.FOCUSABLE // ... }
Java
@Override public void onBindViewHolder(ViewHolder viewHolder, Object o) { HeaderItem headerItem = ((ListRow) o).getHeaderItem(); View rootView = viewHolder.view; rootView.setFocusable(View.FOCUSABLE) // Allows the D-Pad to navigate to this header item // ... }
- Düzeninizi odaklanılabilir olacak şekilde ayarlayın:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ... android:focusable="true">
Son olarak, BrowseSupportFragment
katalog tarayıcısı için setHeaderPresenterSelector()
kullanın
yöntemini kullanın.
Kotlin
setHeaderPresenterSelector(object : PresenterSelector() { override fun getPresenter(o: Any): Presenter { return IconHeaderItemPresenter() } })
Java
setHeaderPresenterSelector(new PresenterSelector() { @Override public Presenter getPresenter(Object o) { return new IconHeaderItemPresenter(); } });
Tam bir örnek için bkz. Leanback örnek uygulaması ,
Üstbilgileri gizleme veya devre dışı bırakma
Bazı durumlarda, örneğin yeterli satır olmadığında satır başlıklarının görünmesini istemezsiniz.
kaydırılabilir liste gerektirecek. BrowseSupportFragment.setHeadersState()
hattını arayın
parçanın onActivityCreated()
işlemi sırasındaki yöntem
yöntemini kullanabilirsiniz. setHeadersState()
yöntemi, aşağıdakilerden biri kapsamında göz atma parçasındaki üstbilgilerin ilk durumunu belirler
sabit değerleri kullanabilirsiniz:
HEADERS_ENABLED
: göz atma parçası etkinliği oluşturulduğunda, üstbilgiler etkinleştirilir ve varsayılandır. Üstbilgiler, bu sayfadaki şekil 1 ve 2'de gösterildiği gibidir.HEADERS_HIDDEN
: göz atma parçası etkinliği oluşturulduğunda, üstbilgiler varsayılan olarak etkinleştirilir ve gizlenir. Ekranın başlık bölümü daraltılmış durumdadır (bkz. ) bir şekil Kart görünümü sağlayın. İlgili içeriği oluşturmak için kullanılan kullanıcı daraltılmış başlık bölümünü seçerek genişletebilir.HEADERS_DISABLED
: göz atma parçası etkinliği oluşturulduğunda başlıklar varsayılan olarak devre dışı bırakılır ve hiçbir zaman görüntülenmez.
HEADERS_ENABLED
veya HEADERS_HIDDEN
ayarlandıysa telefon edebilirsiniz
setHeadersTransitionOnBackEnabled()
satırdaki seçilen içerik öğesinden satır başlığına geri gitmeyi desteklemek için. Bu özellik,
yöntemi çağrıştırmazsanız varsayılan olarak ayarlanır. Geri hareketi kendiniz yönetmek için
setHeadersTransitionOnBackEnabled()
adlı kişiye false
geç
kendi sırt yığın işlemenizi uygulayın.
Medya listelerini görüntüle
BrowseSupportFragment
dersimiz sayesinde
göz atılabilir medya içeriği kategorilerini ve medya öğelerini tanımlamak ve görüntülemek
bağdaştırıcıları ve sunucuları kullanarak bir medya kataloğu oluşturun. Adaptörler bağlanmanızı sağlar
verileri içeren yerel veya çevrimiçi veri kaynaklarıyla tutarlı şekilde güncelleyin.
Adaptörler, görünüm oluşturmak ve verileri bu görünümlere bağlamak için sunucuları kullanır.
bir öğe görüntülenir.
Aşağıdaki örnek kod, dize verilerini görüntülemek için bir Presenter
uygulamasını göstermektedir:
Kotlin
private const val TAG = "StringPresenter" class StringPresenter : Presenter() { override fun onCreateViewHolder(parent: ViewGroup): Presenter.ViewHolder { val textView = TextView(parent.context).apply { isFocusable = true isFocusableInTouchMode = true background = parent.resources.getDrawable(R.drawable.text_bg) } return Presenter.ViewHolder(textView) } override fun onBindViewHolder(viewHolder: Presenter.ViewHolder, item: Any) { (viewHolder.view as TextView).text = item.toString() } override fun onUnbindViewHolder(viewHolder: Presenter.ViewHolder) { // no op } }
Java
public class StringPresenter extends Presenter { private static final String TAG = "StringPresenter"; public ViewHolder onCreateViewHolder(ViewGroup parent) { TextView textView = new TextView(parent.getContext()); textView.setFocusable(true); textView.setFocusableInTouchMode(true); textView.setBackground( parent.getResources().getDrawable(R.drawable.text_bg)); return new ViewHolder(textView); } public void onBindViewHolder(ViewHolder viewHolder, Object item) { ((TextView) viewHolder.view).setText(item.toString()); } public void onUnbindViewHolder(ViewHolder viewHolder) { // no op } }
Medya öğeleriniz için sunucu sınıfı oluşturduktan sonra
bir adaptöre takın ve BrowseSupportFragment
cihazına bağlayın.
kullanıcının göz atması için bu öğeleri ekranda görüntüleyin. Aşağıdaki örnek
kod, kategorileri ve öğeleri görüntülemek için bir bağdaştırıcının nasıl oluşturulacağını gösterir
bu kategorilerde gösterilen StringPresenter
sınıfını kullanarak
önceki kod örneği:
Kotlin
private const val NUM_ROWS = 4 ... private lateinit var rowsAdapter: ArrayObjectAdapter override fun onCreate(savedInstanceState: Bundle?) { ... buildRowsAdapter() } private fun buildRowsAdapter() { rowsAdapter = ArrayObjectAdapter(ListRowPresenter()) for (i in 0 until NUM_ROWS) { val listRowAdapter = ArrayObjectAdapter(StringPresenter()).apply { add("Media Item 1") add("Media Item 2") add("Media Item 3") } HeaderItem(i.toLong(), "Category $i").also { header -> rowsAdapter.add(ListRow(header, listRowAdapter)) } } browseSupportFragment.adapter = rowsAdapter }
Java
private ArrayObjectAdapter rowsAdapter; private static final int NUM_ROWS = 4; @Override protected void onCreate(Bundle savedInstanceState) { ... buildRowsAdapter(); } private void buildRowsAdapter() { rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); for (int i = 0; i < NUM_ROWS; ++i) { ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter( new StringPresenter()); listRowAdapter.add("Media Item 1"); listRowAdapter.add("Media Item 2"); listRowAdapter.add("Media Item 3"); HeaderItem header = new HeaderItem(i, "Category " + i); rowsAdapter.add(new ListRow(header, listRowAdapter)); } browseSupportFragment.setAdapter(rowsAdapter); }
Bu örnekte bağdaştırıcıların statik uygulaması gösterilmektedir. Tipik bir medya tarama uygulaması çevrimiçi bir veritabanından veya web hizmetinden alınan verileri kullanır. Örneğin, Yeşil Ofis web sitesinde web'den alınan verileri kullanıyorsa, Leanback örnek uygulaması ,
Arka planı güncelle
TV'deki bir medya tarama uygulamasına görsel ilgi eklemek için arka planı güncelleyebilirsiniz görsel olarak da kullanabilirsiniz. Bu teknik, uygulamanızla etkileşimi artırabilir sinematik ve eğlenceli.
Leanback kullanıcı arayüzü araç seti, bir BackgroundManager
sağlar.
TV uygulaması etkinliğinizin arka planını değiştirme sınıfı. Aşağıdaki örnekte,
TV uygulaması etkinliğinizde arka planı güncellemek için basit bir yöntem oluşturabilirsiniz:
Kotlin
protected fun updateBackground(drawable: Drawable) { BackgroundManager.getInstance(this).drawable = drawable }
Java
protected void updateBackground(Drawable drawable) { BackgroundManager.getInstance(this).setDrawable(drawable); }
Medya tarama uygulamalarının çoğu, kullanıcı gezinirken arka planı otomatik olarak günceller
aracılığıyla iletişim kurabilirsiniz. Bunu yapmak için bir seçim işleyiciyi otomatik olarak
kullanıcının mevcut seçimine göre arka planı güncelleyebilir. Aşağıdaki örnekte,
OnItemViewSelectedListener
sınıfını ayarlamak için
seçim etkinliklerini yakalama ve arka planı güncelleme:
Kotlin
protected fun clearBackground() { BackgroundManager.getInstance(this).drawable = defaultBackground } protected fun getDefaultItemViewSelectedListener(): OnItemViewSelectedListener = OnItemViewSelectedListener { _, item, _, _ -> if (item is Movie) { item.getBackdropDrawable().also { background -> updateBackground(background) } } else { clearBackground() } }
Java
protected void clearBackground() { BackgroundManager.getInstance(this).setDrawable(defaultBackground); } protected OnItemViewSelectedListener getDefaultItemViewSelectedListener() { return new OnItemViewSelectedListener() { @Override public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) { if (item instanceof Movie ) { Drawable background = ((Movie)item).getBackdropDrawable(); updateBackground(background); } else { clearBackground(); } } }; }
Not: Önceki uygulama, illüstrasyon. Bu işlevi kendi uygulamanızda oluştururken daha iyi performans için arka plan güncelleme işlemini ayrı bir ileti dizisinde toplayın. Ayrıca öğeler arasında gezinen kullanıcılara yanıt olarak arka planı güncellemeyi planlıyor, Arka plan resmi güncellemesini, kullanıcı bir öğe üzerinde karar verene kadar ertelemek için gereken süreyi ifade eder. Bu teknik, çok fazla arka plan resmi güncellemesi.