البحث في تطبيقات التلفزيون

غالبًا ما يكون لدى المستخدمين محتوى محدد في الاعتبار عند استخدام أحد تطبيقات الوسائط على التلفزيون. إذا كان تطبيقك يتضمّن كتالوجًا كبيرًا من المحتوى، قد لا يكون التصفّح بحثًا عن عنوان محدّد هو الطريقة الأكثر فعالية للمستخدمين للعثور على ما يبحثون عنه. يمكن لواجهة البحث أن تساعد المستخدمين في الوصول إلى المحتوى الذي يريدونه بشكل أسرع من التصفُّح.

توفّر مكتبة androidx.leanback مجموعة من الفئات لتفعيل واجهة بحث عادية داخل تطبيقك تتوافق مع وظائف البحث الأخرى على التلفزيون وتوفّر ميزات مثل الإدخال الصوتي.

يناقش هذا الدليل كيفية توفير واجهة بحث في تطبيقك باستخدام فئات مكتبة دعم Leanback.

إضافة إجراء بحث

عند استخدام الفئة BrowseFragment لواجهة تصفّح الوسائط، يمكنك تفعيل واجهة البحث كجزء عادي من واجهة المستخدم. واجهة البحث هي رمز يظهر في التنسيق عند ضبط View.OnClickListener على العنصر BrowseFragment. يوضح الرمز النموذجي التالي هذا الأسلوب.

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.browse_activity)
    browseFragment = fragmentManager.findFragmentById(R.id.browse_fragment) as BrowseFragment
    browseFragment.setOnSearchClickedListener { view ->
        val intent = Intent(this@BrowseActivity, SearchActivity::class.java)
        startActivity(intent)
    }

    browseFragment.setAdapter(buildAdapter())
}

Java

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.browse_activity);

    browseFragment = (BrowseFragment)
            getFragmentManager().findFragmentById(R.id.browse_fragment);

    ...

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

    browseFragment.setAdapter(buildAdapter());
}

ملاحظة: يمكنك ضبط لون رمز البحث باستخدام طريقة setSearchAffordanceColor(int).

إضافة إدخال بحث ونتائج

عندما ينقر المستخدم على رمز البحث، يستدعي النظام نشاط بحث باستخدام الغرض المحدّد. بالنسبة إلى سجلّ البحث، استخدِم تنسيقًا خطيًا يحتوي على SearchFragment. ويجب أن يستخدم هذا الجزء أيضًا واجهة SearchFragment.SearchResultProvider لعرض نتائج البحث.

يعرض نموذج الرمز البرمجي التالي كيفية توسيع فئة SearchFragment لتوفير واجهة بحث ونتائج:

Kotlin

class MySearchFragment : SearchFragment(), SearchFragment.SearchResultProvider {
    private val rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
    private val handler = Handler()
    private val delayedLoad = SearchRunnable()

    val resultsAdapter: ObjectAdapter
    get() {
        return rowsAdapter
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setSearchResultProvider(this)
        setOnItemClickedListener(getDefaultItemClickedListener())
    }

    fun onQueryTextChange(newQuery: String): Boolean {
        rowsAdapter.clear()
        if (!TextUtils.isEmpty(newQuery)) {
            delayedLoad.setSearchQuery(newQuery)
            handler.removeCallbacks(delayedLoad)
            handler.postDelayed(delayedLoad, SEARCH_DELAY_MS)
        }
        return true
    }

    fun onQueryTextSubmit(query: String): Boolean {
        rowsAdapter.clear()
        if (!TextUtils.isEmpty(query)) {
            delayedLoad.setSearchQuery(query)
            handler.removeCallbacks(delayedLoad)
            handler.postDelayed(delayedLoad, SEARCH_DELAY_MS)
        }
        return true
    }

    companion object {
        private val SEARCH_DELAY_MS = 300
    }
}

Java

public class MySearchFragment extends SearchFragment
        implements SearchFragment.SearchResultProvider {

    private static final int SEARCH_DELAY_MS = 300;
    private ArrayObjectAdapter rowsAdapter;
    private Handler handler = new Handler();
    private SearchRunnable delayedLoad;

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

        rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
        setSearchResultProvider(this);
        setOnItemClickedListener(getDefaultItemClickedListener());
        delayedLoad = new SearchRunnable();
    }

    @Override
    public ObjectAdapter getResultsAdapter() {
        return rowsAdapter;
    }

    @Override
    public boolean onQueryTextChange(String newQuery) {
        rowsAdapter.clear();
        if (!TextUtils.isEmpty(newQuery)) {
            delayedLoad.setSearchQuery(newQuery);
            handler.removeCallbacks(delayedLoad);
            handler.postDelayed(delayedLoad, SEARCH_DELAY_MS);
        }
        return true;
    }

    @Override
    public boolean onQueryTextSubmit(String query) {
        rowsAdapter.clear();
        if (!TextUtils.isEmpty(query)) {
            delayedLoad.setSearchQuery(query);
            handler.removeCallbacks(delayedLoad);
            handler.postDelayed(delayedLoad, SEARCH_DELAY_MS);
        }
        return true;
    }
}

إنّ المثال السابق للرمز البرمجي مخصّص للاستخدام مع فئة SearchRunnable التي تُجري طلب البحث في سلسلة محادثات منفصلة. يمنع هذا الأسلوب طلبات البحث التي يُحتمل أن تكون بطيئة التشغيل من حظر سلسلة محادثات واجهة المستخدم الرئيسية.