精华内容
下载资源
问答
  • android Tv

    2018-08-30 14:53:58
    androidtv的一个demo,可以装在电视上,亲测可用,方便大家学习。
  • Android TV

    2019-03-01 10:17:34
    垂直的RecyclerView,并实现了可以控制横向recyclerView的效果。 一些关于自定义的RecyclerView扩展功能效果
  • android tv

    2014-03-05 13:58:35
    一个完善中的简单的android网络电视,可观看绝大多数国内电视节目,用户可加入自定义节目。
  • python-androidtv 可在上找到此软件包的文档。 androidtv是一个Python软件包,可通过ADB提供状态信息以及对Android TV和Fire TV设备的控制。 Home Assistant中的集成使用此程序包。 安装 pip install androidtv 要...
  • Jellyfin Android TVAndroid TV,Nvidia Shield和Amazon Fire TV设备的Jellyfin客户端。 我们欢迎所有贡献并提出要求! 如果您打算使用更大的功能,请提出一个问题,以便我们在开始之前讨论实现。 从我们的实例...
  • Jellyfin Android TV Part of the Jellyfin Project Jellyfin Android TV is a Jellyfin client for Android TV, nVidia Shield, and Amazon Fire TV devices.
  • Android TV Remote Service详情This is the service that allows use of your Android phone or tablet as a remote for your Android TV. Easily switch between d-pad and touchpad modes to navigate content and...

    Android TV Remote Service详情

    This is the service that allows use of your Android phone or tablet as a remote for your Android TV. Easily switch between d-pad and touchpad modes to navigate content and play games on your Android TV device. Tap the mic to start a voice search, or use the keyboard to input text on Android TV.

    To get started, connect your Android phone or tablet to the same network as your Android TV device or find your Android TV via bluetooth.

    Works with all Android TV devices.

    Android TV Remote Service中文

    这是允许使用Android手机或平板电脑作为您的Andr​​oid电视的远程服务。 D – 垫和触摸板模式之间轻松切换浏览内容和Android TV设备上玩游戏。点击麦克风图标,开始进行语音搜索,或者使用键盘到Android电视上输入文本。

    要开始,你的Andr​​oid手机或平板电脑连接到同一个网络的Andr​​oid TV的设备或通过蓝牙找到你的Andr​​oid电视。

    适用于所有Android TV设备。

    展开正文 ▼

    展开全文
  • Android TV 客户项目

    2018-04-25 17:33:44
    Android TV 客户项目, Android TV 客户项目, Android TV 客户项目
  • Android TV support

    2020-12-26 16:20:30
    <div><p>Reddit user requested Android TV support. <p>What needs to happen for the APK to be supported on Android TV? My guess is better landscape support to start. </p><p>该提问来源于开源项目:...
  • 因为我将从创建项目开始写这篇日志,这意味着会比较无聊,你要有心理准备,当然如果你是一个新手的话,恰好最近又要做AndroidTV的开发,那么你就来对地方了,好了,话不多说了,进入正题吧。 简介 Android TV 开发,...
  • Android TV Apps Development

    2018-06-09 12:27:16
    Android TV Apps Development: Building Media and Games will demystify some of the newest APIs and present the tools necessary for building applications that run on Android TV. Walking through example ...
  • Android-tv-widget, Android tv,盒子,投影仪 控件
  • android tv leftbardemo

    2014-06-07 11:01:17
    android tv leftbar demo.
  • Android TV Support

    2020-11-29 13:41:17
    <div><p>Is Android TV, Specifically NVIDIA Shield TV supported?</p><p>该提问来源于开源项目:thestr4ng3r/chiaki</p></div>
  • Android TIF(Android TV Input Framework)是Google向电视制造商提供了一套标准的API,用于创建Input模块来控制Android电视。这套API的底层实现的原理AIDL和provider,从而进行了跨进程通信(Bunder)。系统或第三方的...

    Tamic/CSDN
    http://blog.csdn.net/sk719887916/article/details/53645615

    做TV开发一段时间了,国内目前关于这方面的资料并不多,这里我来分享一下我对TIF的使用心得。Android TIF(Android TV Input Framework)是Google向电视制造商提供了一套标准的API,用于创建Input模块来控制Android电视。这套API的底层实现的原理AIDL和provider,从而进行了跨进程通信(Bunder)。系统或第三方的应用可以通过TIF获得所有输入(input)的信源(输入的模块包括:搜台模块,MDMI模块,网络模块等),然后通过aidl切台输出到屏幕上。
    #电视相关:

    • HDMI:高清晰度多媒体接口(英文:High Definition Multimedia
      Interface,

    • HDMI)是一种数字化视频/音频接口技术,是适合影像传输的专用型数字化接口

    • IPTV:网络电视,也叫VOD电视,三方比如说某某视频公司提供的视频资源在电视上播放

    • DTV:数字电视

    • ATV:模拟电视

    #TIF的组成部分:
     
    1. TV Provider(com.android.providers.tv.TvProvider):
    一个包含频道、节目和相关权限的数据库。  
    2. TV App (com.android.tv.TvActivity):
    一个和用户交互的系统应用。
    3. TV Input Manager (android.media.tv.TvInputManager):
    一个中间接口层,能够让TV Inputs和TV App进行通讯。
    4. TV Input:
    可以看做是一个代表物理或者虚拟的电视接收器或者输入端口的应用。Input在TIF中可以看做是一个输入源。
    5. TV Input HAL (tv_input module):
    TV Input的硬件抽象层,可以让系统的TV inputs访问TV特有硬件。
    6. Parental Control:
    儿童锁,一种可以锁住某些频道和节目的技术。
    7. HDMI-CEC:
    一种可以通过HDMI在多种设备上进行远程控制的技术。CEC(Consumer Electronics
    Control消费电子控制)

    #TIF的整理使用流程。

    这里写图片描述

    如上图所示,liveTVApp通过turning调用TV Input Manager获得一个session,session里面放的是一路信源的状态。TvInput将获得的Channel和Programs信息写入到/data/data/com.android.providers.tv/databases/tv.db数据库中。liveTVApp通过session以aidl的方式调用TVinputService获得相关的频道和具体的节目信息进行播放。
      
    #TIF为开发者提供的接口

    ##TvView
      负责显示播放的内容。它是一个ViewGroup的子类,它是切台的入口,内置surface用于显示视频播放的内容和通过控制session可以控制音量的大小等。
    ##TvInputService
    TvInputService是一个重要的类,继承了它并实现一些规范就可以实现一路input信源供其它应用使用。在该service中要实现onCreatSession()方法该方法要返回一个TvInputService.Session对象。这里的service在Manifest中定义时要注意要添加permission和action,具体如图2。添加完之后系统的TvInputManager可以检测到该service是一个TvInputService,也就是一路信源。

    这里写图片描述

    ##TvInputService.Sssion
    该session类TvView通过Tune方法会指定相应的inputId(往往是该service对应的“报名/.类名”)和uri,uri中包含对应的节目id,该tune方法会调用Session的Onturn方法中,在这个方法中解析传过来的id,根据id利用TvProvider去查询数据库的数据,设置给player,这里使用onSetSurface()方法将TvView创建的surface设置给player,然后player就在该surface上显示内容。
     
    ##TvContract
     介于TvProvider和TvApp之间的一层封装,它里面封装了一些uri。里面有两个内部类是两个javaBean。他们分别是TvContract.channels(频道表),TvContract.Programs(频道里面的节目单,比如少儿频道里面海贼王第5集,火影忍者第6集等)。
    ##TvInputManager
      
    这个是TIF的核心类,它是系统的类,可以监测到在系统的service中注册"android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS"action的类,并将其设为一路信源。它来管理一些回调,比如video是否可用,video的大小尺寸是否变换。通过下面的代码可以获得一个TvInputManager,

    TvInputManager tvInputManager =(TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);`
    

    得到TvInputManager后我们可以遍历拿到系统当前有多少个service是Tv信源。代码如下:

    List<TvInputInfo> list = tvInputManager.getTvInputList();    for(TvInputInfo info:list){    Log.i(TAG, "id:" + info.getId()); }

    我这里打出的log如下:

    01-03 06:58:11.893 29023-29023/com.lenovo.tvviewsimple I/swj: id:com.mediatek.tvinput/.component.ComponentInputService/HW1
    01-03 06:58:11.893 29023-29023/com.lenovo.tvviewsimple I/swj: id:com.mediatek.tvinput/.dtv.TunerInputService/HW0
    01-03 06:58:11.893 29023-29023/com.lenovo.tvviewsimple I/swj: id:com.mediatek.tvinput/.composite.CompositeInputService/HW2
    01-03 06:58:11.893 29023-29023/com.lenovo.tvviewsimple I/swj: id:lenovo.com.ismartvlive/.Ismartvliveservice

    可以看出一共有这么多的信源可以使用。我们可以拿到inputId,在TvView的tune方法中设置。这里的信源就是注册了服务并没有开启。在TvView的tune方法调用的时候会开启服务。

    ##TvInputInfo
    TvInput的信息。包括频道类型,图标,名称等信息。
      
    ##TvInputCallback
    这里是TvView的一个内部类,TvInputCallBack可以反馈给TvView一些信息比如连接service是否成功,Video是否可用等。部分代码如下:

    tvView.setCallback(new TvView.TvInputCallback() {    
        @Override    
       public void onConnectionFailed(String inputId) {
             super.onConnectionFailed(inputId);
             LogUtil.i(this,"MainActivity.onConnectionFailed:"+inputId); 
        }
        @Override    
         public void onDisconnected(String inputId) { 
            super.onDisconnected(inputId);
             LogUtil.i(this,"MainActivity.onDisconnected."); 
        }    
    @Override    
    public void onVideoSizeChanged(String inputId, int width, int height) { 
           super.onVideoSizeChanged(inputId, width, height); 
           LogUtil.i(this,"MainActivity.onVideoSizeChanged.");    
    }    
    @Override
        public void onVideoAvailable(String inputId) {
            super.onVideoAvailable(inputId);
            LogUtil.i(this,"MainActivity.onVideoAvailable.inputId:"+inputId);
        }
        @Override
        public void onVideoUnavailable(String inputId, int reason) {
            super.onVideoUnavailable(inputId, reason);
            LogUtil.i(this,"MainActivity.onVideoUnavailable.");
        }    
    ......
    });
    

    #简单的例子
    效果图:

    show.g

    这里我使用了TvInputservice和TvView分开写的方法,这样写更能体现出跨进程的特点。
    详见https://github.com/songwenju/TIFSample,如果对您有帮助,欢迎star和fork。
    这个例子使用的视频源是google提供的https://storage.googleapis.com/android-tv/android_tv_videos_new.json ,里面使用了retrofit+RxJava做数据请求,对这方面不了解的同学可以查阅相关的资料。
    这里写图片描述项目结构如下图:

    ##下面说一下项目的大致流程:

    第一步:

    1.在tifService module中有三个功能,一是负责请求网络数据,这里使用的是retrofit+rxjava,并将网络数据使用TvProvider写入tv.db ,二是用来加载提供TvInputService类,这个类是Tif的controler。三是提供播放器负责播放。这里要说明一下,播放器在service中,但是显示在TvView的界面上,原因是TvView在tune的时候传过来一个surface,这里将播放的内容显示在这个surface上。这三个步骤的核心代码分别是:
    1)请求数据

    private void addData() {
        LogUtil.i(this,"MainActivity.addData.");
        mChannelService.getResult() 
               .subscribeOn(Schedulers.newThread()) //请求数据在子线程
                .map(new Func1<ChannelResult, ChannelResult>() { 
                   @Override
                    public ChannelResult call(ChannelResult channelResult) {
                        List<GooglevideosBean> googlevideos = channelResult.getGooglevideos();
                        for (GooglevideosBean googlevideosBean : googlevideos) {
                            for (VideosBean videoBean : googlevideosBean.getVideos()) {
                                insertChannelsData(mContext, videoBean);
                            }
                        }
                        return channelResult;
                    }
                }).subscribeOn(Schedulers.newThread()) 
               .observeOn(AndroidSchedulers.mainThread()) 
               .subscribe(new Action1<ChannelResult>() {    // 相当于onNext()
                    @Override
                    public void call(ChannelResult s) {
                        LogUtil.i(this, "MainActivity.endCall.");
                        Toast.makeText(mContext,"数据写入完毕",Toast.LENGTH_SHORT).show();
                    }
                }, new Action1<Throwable>() {                       // 相当于onError()
                    @Override
                    public void call(Throwable throwable) {
                        throwable.printStackTrace();
                    }
                });}
    /** * 写入channel到数据库 
    * *@param context   上下文 
    * @param videoBean videoBean 
    */
    public static void insertChannelsData(Context context, VideosBean videoBean) { 
       ContentValues value = new ContentValues();
        value.put(TvContract.Channels.COLUMN_INPUT_ID, "com.songwenju.tifservice/.TvService");
        value.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, videoBean.getSources().get(0));    //url
        value.put(TvContract.Channels.COLUMN_DISPLAY_NAME, videoBean.getTitle());               //name
        value.put(TvContract.Channels.COLUMN_DESCRIPTION, videoBean.getDescription());
               //description    context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, value);
    }
    

    2)TvInputService

    public class TvService extends TvInputService {
        private SimpleSessionImpl mSimpleSession;
        private Context mContext;
        @Nullable
        @Override
        public Session onCreateSession(String inputId) {
            LogUtil.i(this, "TvService.onCreateSession.inputId:" + inputId);
            mContext = this;
            mSimpleSession = new SimpleSessionImpl(this);
            return mSimpleSession;
        }
        public class SimpleSessionImpl extends Session {
            private MediaPlayer mMediaPlayer;
            private Surface mSurface;
            /**         
              * Creates a new Session. 
             *         
             * @param context The context of the application
             */
            public SimpleSessionImpl(Context context) {
                super(context);
                LogUtil.i(this, "SimpleSessionImpl.SimpleSessionImpl.");
            }
            @Override
            public void onRelease() {
                LogUtil.i(this, "SimpleSessionImpl.onRelease.");
            }
            @Override
            public boolean onSetSurface(Surface surface) {
                //
                LogUtil.i(this, "SimpleSessionImpl.onSetSurface." + surface);
                mSurface = surface;
                return true;
           }
            @Override
            public void onSetStreamVolume(float volume) {
                LogUtil.i(this, "SimpleSessionImpl.onSetStreamVolume.");
            }
            @Override
            public boolean onTune(Uri channelUri) { 
               LogUtil.i(this, "SimpleSessionImpl.onTune.");
                Long channelId = ContentUris.parseId(channelUri);
                LogUtil.d(this, "channelId:" + channelId);
                return setChannelIdAndPlay(channelId); 
           }
    }
    

    3)播放的逻辑

    /**
     * 设置ChannelId并播放
     *
     * @param channelId
     * @return
     */
    private boolean setChannelIdAndPlay(Long channelId) {
        VideosBean dbChannel = getDbChannel(mContext, channelId);
        LogUtil.i(this, "SimpleSessionImpl.setChannelIdAndPlay." + dbChannel.toString());
        mMediaPlayer = new MediaPlayer();
        String playUrl;
        try {
            playUrl = dbChannel.getSources().get(0); //google的json有时候不能用
            if (TextUtils.isEmpty(playUrl)) {
                if (channelId == 1) {
                //如果google的网连接不上的话,这里设置了一个默认的地址
                    playUrl = "http://cord.tvxio.com/v1_0/I2/frk/api/live/m3u8/9/5f754b84-ec33-4d62-bb81-3e4de21c8460/medium/";
                }else {
                    playUrl = " http://cord.tvxio.com/v1_0/I2/frk/api/live/m3u8/9/577da15a-9007-4fdd-a9cf-6e19d7a04528/medium/";
                }
            }
            LogUtil.i(this, "SimpleSessionImpl.setChannelIdAndPlay.playUrl=" + playUrl);
            mMediaPlayer.reset();
            mMediaPlayer.setDataSource(playUrl);
            mMediaPlayer.setSurface(mSurface);
            mMediaPlayer.setOnErrorListener(new OnErrorListener());
            mMediaPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener());
            mMediaPlayer.setOnInfoListener(new OnInfoListener());
            mMediaPlayer.setOnPreparedListener(new OnPreparedListener());
            mMediaPlayer.prepareAsync();
        } catch (IOException e) {
            e.printStackTrace();
        } 
       return false;
    }
    

    第二步:

    2 在app module中,很简单的一个TvView,通过上面的步骤5)获得inputId,将id和Uri,我这里uri使用的是
    Uri.parse(“content://main/250”),最后一个250就是要解析的id,通过这个id去拿到频道的播放列表。设置给MediaPlayer去播放。

    #注意事项
     
    ##1通过uri解析id:

    Long channelId = ContentUris.parseId(channelUri);
    

    ##对于状态的回传
    在TvView中我们如果想要获取一些播放器的状态,比如buffer状态,在开始播放之前有一个loading的状态,获取节目的size的变换,以及自定义的一些状态。下面依次说明:
    1)lodding状态的回传

    在tune方法的时候使用mSimpleSession.notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
    通知Video不可用,原因是tuning
      
    其他对应的状态还有:

    • TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN:未知原因

    • TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL:信号弱

    • TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING:缓冲  
      TvInputManager.VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY:仅仅是音频

    在视频播放的时候即在onprepared时调用

    mSimpleSession.notifyVideoAvailable();
    

    2)buffer状态的回传:在MediaPlayer中Buffer的两种状态,开始缓冲和结束缓冲对应的是701和702两个状态。在MediaPlayer的onInfo方法中收到了701开始调用mSimpleSession.notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING);

    3)自定义的状态,这个使用make的方式编代码的时候才能引用,因为这个方法用@system api注解了。可以传一个bundle对象。
    notifySessionEvent(@NonNull final String eventType, final Bundle eventArgs)

    ##3.Program表
    在使用TvProvider提供的Program表的时候,我这里遇到了一个问题,发现表的数据会被不定期的清空。测试那边给的也是偶现的。通过断网,切台,重启系统发现programs表总是被清空。对于开发来说找到bug的复现步骤是最好不过的事情了。通过阅读TvProvider的源码可以看到有一个类专门负责清空Programs的数据,代码如下:
    在EpgDataCleanupService.java中会去清除当前时间以前的节目信息,在这个字段对应的时间信息COLUMN_END_TIME_UTC_MILLIS,而这个时间是以毫秒为单位的,我们服务器给的数据是以秒为单位的,所以会被清空。修改一下就可以了。

     /**
      * Clear program info that ended before {@codemaxEndTimeMillis}.
      */
      @VisibleForTesting
     void clearOldPrograms(long maxEndTimeMillis) {
     81         int deleteCount = getContentResolver().delete(
     82                 Programs.CONTENT_URI,
     83                 Programs.COLUMN_END_TIME_UTC_MILLIS + "<?",
     84                 new String[] { String.valueOf(maxEndTimeMillis) });
     85         if (DEBUG && deleteCount > 0) {
     86             Log.d(TAG, "Deleted " + deleteCount + "programs"
     87                   + " (reason: ended before "
     88                   + DateUtils.getRelativeTimeSpanString(this,  maxEndTimeMillis) + ")");
     89         }
     90     }
    

    例子详见https://github.com/songwenju/TIFSample,如果对您有帮助,欢迎star和fork。
    到此关于Android TIF的介绍和框架的使用部分结束了,以后若有新的理解再来添加。

    Tamic: CSDN
    地址:http://blog.csdn.net/sk719887916/article/details/53645615

    参考原文:http://www.jianshu.com/p/385c92fceb16

    作者:Tamic 更多原创关注开发者技术前线

    开发者技术前线

    展开全文
  • Jellyfin mutes Android TV

    2020-12-01 21:24:41
    <div><p>Everytime i finish an episode of a series the app somehow mutes my Android TV forcing me to click on volume up to reenable audio. <p>Does not happen other ...jellyfin/jellyfin-androidtv</p></div>
  • Android tv support

    2020-12-06 05:44:24
    m using MPD from an Android TV (a Shield) and the MPD UI settings was hidden in system preferences. These commits make the MPD launcher available from TV apps and fix the foreground permission that is...
  • Full Android TV Support

    2020-11-25 13:03:54
    t an Android TV specific interface and because Android TV doesn't have a menu button there is no way to change any of the settings. It would be preferable to have a box art based file picker ...
  • android tv apk -crx插件

    2021-04-01 13:35:17
    Android TV Home是在Android TV娱乐设备上启动所有活动的起点。 Android TV Home是在Android TV娱乐设备上启动所有活动的起点。 主屏幕中的频道将您喜欢的内容放在首位和居中。 因此,无论您是要寻找新的节目来狂欢...
  • android tv开发资料

    2018-03-11 22:55:26
    android tv开发资料, makrdown语法,很多TV开发资料手到擒来,避免到处找的麻烦
  • AndroidTVLauncher源码

    2017-11-29 17:48:52
    适用于初学者的AndroidTVLauncher demo ,简单易懂结构清晰
  • 资源为个人android TV版本开发学习用,几个TV开发常见的问题,Android TV TextView如何实现增加滚动条,TV上屏幕适配总结,Android Studio中模拟器中关于VT-x is disabled in BIOS错误的解决方案,Home界面实现原理...
  • AndroidTV焦点控制实例

    2020-08-06 11:35:04
    简单的相关与AndroidTV焦点控制的实例,在手机端和TV端的区别上焦点很重要,本人初学,写的小dome,勿喷
  • Android TV Input Framework(Android TV 一)

    万次阅读 2014-12-31 16:55:13
    Android TV Input Framework (TIF)简化了向Android TV提供实时内容的工作。Android TIF向电视制造商提供了一套标准的API,用于创建Input模块来控制Android电视。这套框架还能搜索实时TV内容和一些推荐的内容。但是这...

    前言

    Android TV是Android 5.0新的内容,当前国内的智能电视大部分都是基于Android系统的,Android TV作为事实上的标准,它的推出必将极大的影响下一代智能电视的开发。

    近两年来一直在从事智能电视的系统开发,也一直在关注这方面的技术,做过Android系统开发的兄弟们都知道,Android的应用开发文档很多,但是Google介绍Android系统方面尤其是Framework方面设计的文档很少,也许是那些工程师都很忙吧,很多都是我们国内开发人员阅读完源码后写的总结文章。

    Android 5.0出来之后欣喜的发现,Android网站上介绍系统设计的技术文档越来越多了,其中就包括这篇Android TV的文章。可是也发现从国内登陆Android网站是越来越慢了,为了给自己一个记录,同时也为了分享给大家,免得大家翻墙,我翻译了这篇文章,Android TV的文章一共两篇,这是第一篇。

    匆匆而就,定有很多不足之处,忘大家多多拍砖指正。国内智能电视行业的兄弟们也可多多切磋,Email:aladdin_hou@163.com

    简介


    Android TV Input Framework (TIF)简化了向Android TV提供实时内容的工作。Android TIF向电视制造商提供了一套标准的API,用于创建Input模块来控制Android电视。这套框架还能搜索实时TV内容和一些推荐的内容。但是这套框架不会实现TV的各种标准和一些基于地域性的需求。

    但是Android TIF能够让电视厂商更容易的满足各地区的数字电视广播标准,而不用重新定义架构。同时这份文档也能够对Android TV的三方应用开发者提供很多信息。

    组成部分


    TIF包含一个TV Input Manager,同时还需要TV App一起工作,TV App是一个System层次的应用,可以访问内置或者IPTV的频道,这个应用不能被三方的应用替换。TV App通过TV Input Manager和电视厂商提供的TV Input模块或者是其他模块通讯。

    TV Input Framework由下列部分组成:

    • TV Provider (com.android.providers.tv.TvProvider):一个包含频道、节目和相关权限的数据库。
    • TV App (com.android.tv.TvActivity):一个和用户交互的系统应用。
    • TV Input Manager (android.media.tv.TvInputManager):一个中间接口层,能够让TV InputsTV App进行通讯。
    • TV Input:可以看做是一个代表物理或者虚拟的电视接收器或者输入端口的应用。InputTIF中可以看做是一个输入源。
    • TV Input HAL (tv_input module):TV Input的硬件抽象层,可以让系统的TV inputs访问TV特有硬件。
    •  Parental Control:儿童锁,一种可以锁住某些频道和节目的技术。
    • HDMI-CEC:一种可以通过HDMI在多种设备上进行远程控制的技术。CEC(Consumer Electronics Control消费电子控制)

    这些组成部分会在后面的章节中描述。下图详细描述了Android TV Input Framework的架构。

    1. Android TV Input Framework (TIF)架构

    流程


    这里介绍上图的架构是如何运行的:

    1. 用户通过TV App进行观看和并且和TV交互,这是一个System级别的应用并且不能被三方应用替换。
    2. TV App显示从TV Input获取到的音频和视频内容。
    3. TV App必须通过TV Input ManagerTV Inputs进行通讯,而不能直接和其进行通讯,TV Input Manager会监控TV Inputs的状态并且提供给TV App

    权限


    • 只有signatureOrSystemTV InputsTV App才具有访问TV Provider数据库的所有权限,才能够接收按键事件。
    • 只有sysgtem级别的TV Inputs才有权限通过TV Input Manager service访问TV Input HALTV Inputs可以通过TV Input Manager会话访问其他的TV Inputs
    •  三方的TV Inputs访问TV Provider数据库中是受限的,只能访问本应用对应的数据行。
    • 三方的TV Inputs可以显示自己的内容,也可以显示直通TV Inputs的内容,例如HDMI1等。他们不能显示非直通的TV Inputs的内容,例如内置的或者IPTV的电视接收器的内容。
    •  TV_INPUT_HARDWARE权限用在基于硬件的TV Input应用中。在系统启动的时候,这个权限标志TV Input Manager Service要通知TV Input ServiceTV Input Service来调用并在TV Input Manager Service注册自己的TV Inputs。这个权限同时允许一个硬件的TV Input应用在一个TV Input service中可以支持多个TV Inputs,可以动态的增加和删除自己支持的TV Inputs

    TV Provider


    TV Provider数据库保存每个TV Inputs的频道和节目信息。TV Provider也管理和发布数据库相关记录的权限,这样的话TV Inputs只能看到他们自己的记录。例如,一个特定的TV Input只能看到自己提供的频道和节目,而不能看到其他TV Inputs的频道和节目。

    TV Provider在内部把“broadcast genre(广播分类)”映射到“canonical genre(Android TV 的统一分类)”。TV Inputs负责填入基于广播电视标准的“broadcast genre”的值,然后“canonical genre“列就会填入从android.provider.TvContract.Genres中得到正确的值。例如,对于广播电视标准ATSC A/65来说,节目分类0x25就是“Sports”,TV Input将会填入“broadcast genre”的字符串“Sports”,然后TV Provider将会在“canonical genre“字段填入相应的值android.provider.TvContract.Genres.SPORTS

    因为广播电视标准是多种多样的,每个国家不同,Android TV通过这个映射将其统一起来,用户在Android TV上看到的就是一样的,而不会造成什么迷惑。

    下图详细的描述了TV Provider.

    2. Android TV Provider

    只有具有System权限的应用才能读取TV Provider数据库中所有的数据。

    直通的TV Inputs(例如HDMI)不会保存频道和节目数据。

    除了频道和节目这些标准的字段外,TV Provider也提供一个BLOB类型的字段,COLUMN_INTERNAL_PROVIDER_DATA,在任何一个表中TV Inputs可以保存任意的数据。BLOB数据可以包含自定义数据,例如相对应的电视接收器的频率,或者在某个广播电视协议的缓冲,或者其他形式。也可以针对一个可搜索的字段,让某些频道不出现在搜索中(例如为了满足在某些特定的国家版权保护的需要)。

    数据库字段示例

    TV Provider在频道(android.provider.TvContract.Channels)和节目 (android.provider.TvContract.Programs)表中支持结构化的数据。这些表可以被TV InputsSystem级别的应用(例如TV App)操作和访问。这些表包含四种类型的字段:

    1. 显示:显示的这些字段包含那些用于向用户显示的信息,例如频道名字(COLUMN_DISPLAY_NAME)和数字(COLUMN_DISPLAY_NUMBER),或者浏览的节目的标题等。
    2. 元数据:根据相关的标准,有三个字段用于标志某项内容,例如频道的传输流ID(COLUMN_TRANSPORT_STREAM_ID),原来的网络ID(COLUMN_ORIGINAL_NETWORK_ID)和服务ID(COLUMN_SERVICE_ID)
    3.  内部数据:一些保存TV Inputs自定义信息的字段。例如COLUMN_INTERNAL_PROVIDER_DATA字段,是一个自定义的BLOB的字段,TV Input可以保存关于频道或者节目的任意的数据。
    4. 标志:标志相关的字段用于描述一个频道是否在搜索、浏览、观看时是否受限。这个可以设置在频道的层次。所有的节目的设置可以服从所在频道的设置。
      • COLUMN_SEARCHABLE:限制某些频道是否可以在搜索结果中列出。COLUMN_SEARCHABLE = 0表示这个频道不能在搜索结果中显示。
      • COLUMN_BROWSABLE:这个接口只有system级别的应用可用。限制频道是否可以被导航浏览。COLUMN_BROWSABLE = 0表示这个频道不能出现在频道列表中。
      • COLUMN_LOCKED:这个接口只有system级别的应用可用。用于需要输入用户名和密码的频道。COLUMN_LOCKED = 1表示频道被儿童锁保护。

    想查看全部的字段列表,请查阅以下文件

    android/frameworks/base/media/java/android/media/tv/TvContract.java

    权限和访问控制

    对于有权限访问相应数据库行的用户,此行所有的字段都是可见的。注意所有的字段不能被用户直接访问,用户只能看到TV App,系统应用或者TV Inputs的界面。

    • 每一行有PACKAGE_NAME字段,这个应用拥有这一行数据,在TvProvider.java中进行Insert, Update操作时会进行检查。一个TV Input可能会访问其他TV Inputs写入和保护的信息。
    • 可以再AndroidManifest.xml中定义READ, WRITE权限来控制那些频道可用。
    • 只有具有signatureOrSystem的应用才能得到ACCESS_ALL_EPG_DATA权限来访问整个数据库。

    TV Input Manager


    TV Input Manager为整个Android TV Input Framework提供主要的系统API。它负责中转、控制应用和TV Inputs之间的交互,同时提供儿童锁的功能。TV Input Manager会话必须是和TV Inputs一一对应的。TV Input Manager允许访问安装的TV Inputs,这样的话应用可以:

    • 列出TV inputs,并且检查他们的状态。
    • 创建会话,并且管理会话的监听者。

    对于会话来说,TV App可以通过一个TV Input写在TV Provider数据库里的URI来对其进行调台,而直通的TV Inputs是通过TvContract.buildChannelUriForPassthroughInput()来进行调台。一个TV Input还可以有自己的音量设置。那些由电视制造商提供的(签名)或者那些安装在系统分区的应用,可以访问整个TV Provider数据库。这些访问可以供应用来搜索和浏览所有可用的频道和节目。

    一个应用可以通过android.media.tv.TvInputManager创建和注册TvInputCallback,这个应用会在TV Input状态变化或者增加、卸载的时候被回调。例如,在一个TV Inputs断开连接的时候,TV App可以作出响应,显示断开状态并且阻止它被用户选中。

    TV Input Manager是对TV AppTV Inputs之间通讯的一个抽象。TV Input Manager TV Input的标准接口允许制造商创建自己的TV App,并且能够让所有三方的TV Inputs在所有的TV App上工作。

    TV Inputs


    TV Inputs是一些Android应用,某种意义上说具有AndroidManifest.xml,通过预装或者应用商店安装的应用。Android电视支持预装的系统级别的应用,具有电视厂商签名的应用,或者一些三方的TV Inputs

    有一些TV Inputs,例如HDMI输入源或者内置的电视接收器,由于这些TV Inputs直接访问硬件,可以由电视制造商提供。其他的像IPTV、位移播放、外置机顶盒这些输入源,可以在Google Play上由三方通过应用来提供,一旦下载并且安装后,这个新的TV Input可以被TV App来使用了。

    直通的TV Inputs示例

    3. Android TV System Input

    在这个例子中,电视厂商提供的TV Input是被信任的,具有访问TV Provider的所有的权限。作为一个直通的TV Input,它没有在TV Provider注册频道和节目信息。为了获取这个TV InputURI,用android.media.tv.TvContract的方法buildChannelUriForPassthroughInput(String inputId)TV AppTV Input Manager通讯来访问HDMI TV Input

    内置的接收器示例

    4. Android TV Built-in Tuner Input

    在这个例子里,电视制造商提供的内置接收器的TV Input是被信任的,具有访问TV Provider的所有权限。

    三方的TV Input示例

    5. Android TV third-party input

    在这个例子中,是一个有三方提供的外置的机顶盒TV Input。这个TV Input不能直接访问HDMI视频源,必须通过TV Input Manager访问设备制造商提供的HDMI TV Input

    通过TV Input Manager,外置的机顶盒TV Input可以访问HDMI TV Input,并且显示HDMI1的视频。在HDMI TV Input显示视频的时候,机顶盒TV Input可以控制电视。

    画中画 (PIP)示例

    6. Android TV KeyEvents

    这个图展示了遥控器上的按键输入是怎么传递到特定的显示PIPTV Input中。那些按键按下后,被硬件驱动扫描到,从硬件扫描码转化到Android keycodes,然后传递到Android标准的输入管道,InputReaderInputDispatcher中。在TV App获取焦点后,这些输入事件也会按顺序触发。

    只有系统级别的TV Inputs才有资格接收InputEvents,而且是只有在声明了RECEIVE_INPUT_EVENT系统权限的基础上。TV Input负责判断哪些InputEvents自己处理,哪些InputEventsTV App处理。

    TV App负责判断当前那个系统TV Input被用户选中,是当前活动的TV Input,然后将KeyEvents分发给正确的TV Input Manager会话,调用dispatchInputEvent()将这些输入事件传递给对应的TV Input

    MHEG-5源示例

    下图展示了在Android TIFKeyEvents是如何被分发的。

    7. Android TV Red button example

    这幅图描述了红键(Red button)应用的流程,这是在欧洲常见的让用户在电视上和应用交互的功能。这个应用可以在电视流媒体中传输。当这个键被按下后,可以让用户和这些广播应用交互。例如,你可以用这些广播应用来获取相关的网页和体育比赛结果。

    Broadcast app部分介绍了这些广播应用是如何和TV App交互的。

    在这个示例中:

    1. TV App获取了焦点,可以接收所有的输入事件。
    2. KeyEvents(例如红色按钮)被作为InputEvents分发到当前活动的TV Input
    3. 集成了MHEG-5协议栈的系统级别的TV Input并且具有RECEIVE_INPUT_EVENT权限。
    4. 当收到激活的按键事件(例如红键),TV Input激活广播应用。
    5. TV input把KeyEvents当成InputEvents,广播应用获得焦点然后处理InputEvents。

    注意:三方的TV inputs不能接收按键事件。

    TV Input HAL


    TV Input硬件抽象层协助TV Inputs访问电视相关的硬件。和其他的Android硬件抽象层一样,TV Input 硬件抽象层在AOSP代码库由样例代码,设备厂商会开发它自己的实现。

    TV App


    TV App通过(com.android.tv.search.TvProviderSearch)提供频道和节目的搜索结果,同时通过TV Input Manager向各个TV Inputs分发按键、调台和音量等调用。电视制造商必须提供TV App来保证用户搜索功能,同时,用户也需要在搜索结果中导航。由于TV App需要系统或者平台签名的权限,三方的开发者不能开发TV App

    对于TIF来说,TV App没有寻求实现每个国家或者电视制造商特有的电视标准。实际上,它通常包含以下工作:

    安装和配置

    • 自动检测TV Inputs
    • TV Inputs初始化频道信息
    • 控制儿童锁设置
    • 更改电视相关的设置

    o    编辑频道

    观看

    • 访问和浏览所有的电视频道
    • 访问电视节目信息栏
    • 多音道和字幕支持
    • 儿童锁控制和密码校验
    • ·         允许某些TV标准(例如HbbTV)TV Input UI层叠

    儿童锁


    儿童锁能够让用户阻止某些不想显示的频道和节目,但是输入正确的密码后就会解除这些锁定。

    TV App, TV Input Manager service, TV Provider, TV Input都要负责儿童锁的功能。

    TV Provider

    每一个频道表中的数据行有一个COLUMN_LOCKED字段,用来在密码验证前锁住特定的频道不被观看。节目表中的COLUMN_CONTENT_RATING字段只是用来显示而不会强制用于儿童锁。

    TV Input Manager

    TV Input Manager保存所有应该被阻止的TvContentRating,在isRatingBlocked()调用中判断所给的分级是否应该被阻止。

    TV Input

    当用户观看的内容内容更改(例如频道或者节目变化),或者儿童的设置发生变化(在ACTION_BLOCKED_RATINGS_CHANGED  ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED)时,TV Input通过TV Input Manager isRatingBlocked()调用来检查目前的内容是否应该被阻止。如果目前的内容应该被阻止,TV Input会关闭音视频的输出,然后通过notifyContentBlocked(TvContentRating)通知TV App当前内容被禁止。如果播放的内容不应该被禁止,TV Input会使能音视频输出,并且通过调用notifyContentAllowed()来通知TV App

    TV App

    TV Input通知当前用户准备播放的频道被儿童锁阻止时,TV App向用户显示儿童锁的设置和需要输入密码的界面。

    TV App不会自己保存儿童锁的设置。当用户更改儿童锁的设置后,所有被阻止的TvContentRatingTV Input Manager中保存,被锁的频道保存在TV Provider中。

    HDMI-CEC


    HDMI-CEC允许一个设备来控制其他的设备,这样的话就能够用一个遥控器来控制家庭中的多个设备。这个功能被Android TV用来通过集中的TV App来加速配置和远程控制各个TV Inputs。例如,可以切换输入源,开关设备等操作。

    Android TIFHDMI-CEC实现为HDMI Control Service,这样的话电视制造商只需要开发底层的驱动,并且和轻量级的Android TV硬件抽象层交互,不需要关注复杂的业务逻辑。为了提供一个标准的实现,Android通过减少碎片化和支持可选择的功能来减少兼容性问题。HDMI Control Service基于已有的Android Services,包括输入和开关机功能。

    这意味着现有的HDMI-CEC实现需要重新设计来适配Android TIF。我们推荐在硬件上包含一个微处理器来接收CEC开机和其他命令。

    8. CEC integration on Android TV

    1. CEC总线从当前活动源接收命令切换到一个不同的源。
    2. 驱动将这个命令传给HDMI-CEC硬件抽象层。
    3. 硬件抽象层通知所有的ActiveSourceChangeListeners。
    4. HDMI Control Service通过注册ActiveSourceChangeListener被通知源切换
    5. TV Input Manager service生成广播来通知TV App切换输入源
    6. TV App为新的TV Input创建TV Input Manager会话并且在这个会话上调用setMain。
    7. TV Input Manager会话向这个HDMI TV Input发送信息。
    8. HDMI TV input请求设置显示界面。
    9. 当界面设置好后。TV Input Manager Service生成相应的切换控制命令回传给HDMI Control Service

    TV集成指南


    广播应用

    因为每个国家有自己的广播电视标准(例如MHEG, Teletext, HbbTV),电视厂商希望用自己的解决方案提供给广播应用。

    MHEG:本地协议栈MHEG-5:信息技术-多媒体和超媒体信息编码第5部分,是一个欧洲标准)

    Teletext:本地协议栈

    HbbTV:基于Opera browserwebkit内核改动而来

    Android L的发布中,Android TV期望电视厂商为区域性的电视标准协议栈集成或者使用Android的解决方案,将显示的Buffer传递给电视软件协议栈,或者将一些按键值传递给以前的协议栈。

    下面是广播应用如何和TV App交互的:

    1. TV App获取焦点,接收所有的按键事件。
    2. TV App传递按键(例如红键)到TV Input
    3. TV Input集成了老的电视协议栈。
    4. 当收到激活按键(例如红键)后,TV Input启动广播应用。
    5. 广播应用在TV App中接收焦点,然后处理用户的输入。

    对于声音搜索或者推荐,广播应用支持应用内搜索或者声音搜索。

    DVR(数字视频录制)

    如果设备支持的话,Android TV支持数字视频录制,这个功能工作如下:

    1. 所有的TV Input都可以实现数字视频录制/实时缓存。
    2. TV App把按键事件分发给TV Input(包括录制/暂停/快放/倒放按键)
    3. 当播放录制的内容时,TV Input把它假装成正在播放的层。
    4. DVR应用可以让用户浏览和管理录制的节目。

    对于声音搜索和推荐:

    • DVR应用支持应用内搜索和声音搜索。
    • DVR通过通知来提供推荐内容。

    下图提供了在Android TVDVR的一个可能实现:

    9. Digital video recording in Android TV

     

    展开全文
  • 文/Tamic:http://blog.csdn.net/sk719887916/article/details/53559667前言Android TV Launcher页在RecyclerView出来之前大家用GridView去实现(本人的FocusView)。TV开发有五向键的监听,遥控器hover监听,点击...

    文/Tamic:http://blog.csdn.net/sk719887916/article/details/53559667

    前言

    Android TV Launcher页在RecyclerView出来之前大家用GridView去实现(本人的FocusView)。TV开发有五向键的监听,遥控器hover监听,点击事件等。用GridView去处理焦点是有一定挑战性的,往往会出现不可预料焦点错乱问题。这里封装了一个针对TV的RecyclerView,很方便的处理了这些事件。

    TV系列文章请看专题:http://blog.csdn.net/column/details/lyk-androidtv.html

    开始线上效果图:

    这里写图片描述

    这里写图片描述

    本次封装了RecyclerView需实现了以下功能:

    1. 响应五向键,按下五向键的上下左右会跟着移动,并获得焦点,在获得焦点时会抬高
    2. 在鼠标hover在条目上时会获得焦点。
    3. 添加了条目的点击和长按事件。
    4. 添加了是否第一个可见条目和是否是最后一个可见条目的方法。
    5. 在item获得焦点时和失去焦点时,这里有相应的回调方法。

    实现

    下面分析一下一些关键的点:
    1.鼠标滑动时避免跟着滑动,只响应五向键和左右箭头

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        //在recyclerView的move事件情况下,拦截调,只让它响应五向键和左右箭头移动
        LogUtil.i(this, "CustomRecycleView.dispatchTouchEvent.");
        return ev.getAction() == MotionEvent.ACTION_MOVE || super.dispatchTouchEvent(ev);
    }
    

    2.使用StaggeredGridLayoutManager实现管理,如果使用GridLayoutManager会出现焦点的错乱,当使用五向键左右移动时,会从上面转移到下面。原因是GridLayoutManager会存在分组。

     //设置布局管理器
         StaggeredGridLayoutManager layoutManager = new           StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.HORIZONTAL);
    

    mRecyclerView.setLayoutManager(layoutManager);

    3.设置RecyclerView的item有焦点。按五向键,焦点会跟着一起移动

    holder.itemView.setFocusable(true);
    

    4,左右键,让RecyclerView跟着一起滚动,并获得焦点:

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        boolean result = super.dispatchKeyEvent(event);
        int dx = this.getChildAt(0).getWidth();
        View focusView = this.getFocusedChild();
        if (focusView != null) {
            switch (event.getKeyCode()) {
                case KeyEvent.KEYCODE_DPAD_RIGHT:
                    if (event.getAction() == KeyEvent.ACTION_UP) {
                        return true;
                    } else {
                        View rightView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
                        LogUtil.i(this, "rightView is null:" + (rightView == null));
                        if (rightView != null) {
                            rightView.requestFocusFromTouch();
                            return true;
                        } else {
                            this.smoothScrollBy(dx, 0);
                            return true;
                        }
                    }
                case KeyEvent.KEYCODE_DPAD_LEFT:
                    View leftView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
                    LogUtil.i(this, "left is null:" + (leftView == null));
                    if (event.getAction() == KeyEvent.ACTION_UP) {
                        return true;
                    } else {
                        if (leftView != null) {
                            leftView.requestFocusFromTouch();
                            return true;
                        } else {
                            this.smoothScrollBy(-dx, 0);
                            return true;
                        }
                    }
            }
        }
        return result;
    }
    

    这里请求获取焦点的方法是:

     rightView.requestFocusFromTouch();
    

    TV的焦点的处理的逻辑比较复杂:

    可以参考:

    http://www.cnblogs.com/myzh/p/3664544.html
    http://blog.csdn.net/sk719887916/article/details/44781475

    5.在holder里监听到焦点变化时做一些处理:

    holder.itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
                @Override
                public void onFocusChange(View v, boolean hasFocus) {
                    if (hasFocus) {
                        focusStatus(v);
                    } else {
                        normalStatus(v);
                    }
                }
            });
        /**
         * item获得焦点时调用
         *
         * @param itemView view
         */
        private void focusStatus(View itemView) {
            if (itemView == null) {
                return;
            }
            if (Build.VERSION.SDK_INT >= 21) {
                //抬高Z轴
                ViewCompat.animate(itemView).scaleX(1.10f).scaleY(1.10f).translationZ(1).start();
            } else {
                ViewCompat.animate(itemView).scaleX(1.10f).scaleY(1.10f).start();
                ViewGroup parent = (ViewGroup) itemView.getParent();
                parent.requestLayout();
                parent.invalidate();
            }
            onItemFocus(itemView);
        }
        /**
         * 当item获得焦点时处理
         *
         * @param itemView itemView
         */
        protected abstract void onItemFocus(View itemView);
        /**
         * item失去焦点时
         *
         * @param itemView item对应的View
         */
        private void normalStatus(View itemView) {
            if (itemView == null) {
                return;
            }
            if (Build.VERSION.SDK_INT >= 21) {
                ViewCompat.animate(itemView).scaleX(1.0f).scaleY(1.0f).translationZ(0).start();
            } else {
                ViewCompat.animate(itemView).scaleX(1.0f).scaleY(1.0f).start();
                ViewGroup parent = (ViewGroup) itemView.getParent();
                parent.requestLayout();
                parent.invalidate();
            }
            onItemGetNormal(itemView);
        }
        /**
         * 当条目失去焦点时调用
         *
         * @param itemView 条目对应的View
         */
        protected abstract void onItemGetNormal(View itemView);
    

    这里抽象了两个方法,当item获得焦点和失去焦点时调用。获得焦点时条目会抬高,这里是抬高了Z轴。

    6.获取在第一个和最后一个可见的条目,根据这些状态去显示和隐藏左右箭头。

    /**
        * 第一个条目是否可见
     *
     * @return 可见返回true,不可见返回false
     */
        public boolean isFirstItemVisible() {
        LayoutManager layoutManager = getLayoutManager();
        if (layoutManager instanceof StaggeredGridLayoutManager) {
            int[] firstVisibleItems = null;
            firstVisibleItems = ((StaggeredGridLayoutManager) layoutManager).
                    findFirstCompletelyVisibleItemPositions(firstVisibleItems);
            int position = firstVisibleItems[0];
            return position == 0;
        } else if (layoutManager instanceof LinearLayoutManager) {
            int position = ((LinearLayoutManager) layoutManager).findFirstCompletelyVisibleItemPosition();
            return position == 0;
        }
        return false;
    }
    /**
     * 最后一个条目是否可见
     *
     * @param lineNum    行数
     * @param allItemNum item总数
     * @return 可见返回true,不可见返回false
     */
    public boolean isLastItemVisible(int lineNum, int allItemNum) {
        LayoutManager layoutManager = getLayoutManager();
        if (layoutManager instanceof StaggeredGridLayoutManager) {
            int[] lastVisibleItems = null;
            lastVisibleItems = ((StaggeredGridLayoutManager) layoutManager).findLastCompletelyVisibleItemPositions(lastVisibleItems);
            int position = lastVisibleItems[0];
            LogUtil.i(this, "lastVisiblePosition:" + position);
            boolean isVisible = position >= (allItemNum - lineNum);
            if (isVisible) {
                scrollBy(1, 0);
            }
            return isVisible;
        } else if (layoutManager instanceof LinearLayoutManager) {
            int position = ((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition();
            return position == allItemNum - 1;
        }
        return false;
    }
    

    下面说一个坑,在处理最后一个条目时可见时,我发现拿到的数据并不是一种情况,当一共有三行时。

    用下面的代码来打出位置:

    for (int i = 0; i < lastVisibleItems.length; i++) {
      LogUtil.i(this, "order:"+i +"----->last position:" + lastVisibleItems[i]);
    }
    

    有三种情况:

    1.最后一列只有有一个时,打出的log是

     01-06 02:40:51.868 4135-4135/com.songwenju.customtvrecyclerview I/swjCustomRecyclerView: order:0----->last position:12
      01-06 02:40:51.869 4135-4135/com.songwenju.customtvrecyclerview I/swjCustomRecyclerView: order:1----->last position:10
      01-06 02:40:51.869 4135-4135/com.songwenju.customtvrecyclerview I/swjCustomRecyclerView: order:2----->last position:11
    

    2.当最后一列有两个时:

     01-06 02:41:54.285 6109- 6109/com.songwenju.customtvrecyclerview I/swjCustomRecyclerView: order:0----->last position:12
     01-06 02:41:54.286 6109-6109/com.songwenju.customtvrecyclerview I/swjCustomRecyclerView: order:1----->last position:13
      01-06 02:41:54.286 6109-6109/com.songwenju.customtvrecyclerview I/swjCustomRecyclerView: order:2----->last position:11
    

    3.当最后一行有三个时:

    01-06 02:43:21.336 8818-8818/com.songwenju.customtvrecyclerview I/swjCustomRecyclerView: order:0----->last position:12
     01-06 02:43:21.337 8818-8818/com.songwenju.customtvrecyclerview I/swjCustomRecyclerView: order:1----->last position:13
     01-06 02:43:21.337 8818-8818/com.songwenju.customtvrecyclerview I/swjCustomRecyclerView: order:2----->last position:14
    

    所以这里的处理是传入行数:

    boolean isVisible = position >= (allItemNum - lineNum);
    来判断是否可见。
    

    7.在Recycler滚动时候去处理箭头的显示状态:

    private class MyOnScrollListener extends RecyclerView.OnScrollListener {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
        }
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            //在滚动的时候处理箭头的状态
            setLeftArrStatus();
            setRightArrStatus();
        }
    }
    

    结束:

    注意在使用该控件时,要设置RecyclerView的宽度是Item的整数倍,左右箭头点击滑动的距离也要设置为RecyclerView宽度。

    TV专栏:http://blog.csdn.net/column/details/lyk-androidtv.html

    项目地址
    https://github.com/songwenju/CustomTvRecyclerView
    如果对你有帮助,欢迎star和fork。
    GridView: https://github.com/Tamicer/FocusView
    文/Tamic:http://blog.csdn.net/sk719887916/article/details/53559667
    原文链接:http://www.jianshu.com/p/566bd6188f4d

    展开全文
  • 虽然安卓智能手机有上千个文件管理器,但Android TV远没有那么多,其中只有大约一半是可靠的。当然,一些电视搭载内置文件管理器,但功能有限;正因为如此,我们决定列出市面上优秀的Android TV文件管理器。它们都...
  • Android TV listview及焦点处理 Android TV上的listview ,因为没有touch事件,只能通过按键处理,因此,用到listview时需要特殊处理: 1.复杂的view需要获取焦点,需要设置: setItemsCanFocus(true) 同时需要设置下...
  • Android TV适配

    2017-10-16 13:48:38
    Android TV适配1.TV模拟器2.TV开发

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,994
精华内容 3,197
热门标签
关键字:

androidtv