android tv 开发布局,Android TV开发总结(七)构建一个TV app中的剧集列表控件
前言:剧集类控件,在TV app中非常常见,今天将介绍构建一个TV app中的剧集列表控件,此控件上传到我的Github:https://github.com/hejunlin2013/EpisodeListView,点击【阅读原文】,可看对应的github, 喜欢可以star。Agenda如下:效果图
效果图gif
实现思路
代码分析
效果图
效果图gif:
实现思路:1、用两个RecycleView作为控件横向布局
2、PopupWindow作为该集剧情简介
3、当焦点到达Parent时,对Child进行监听,并发生变化,同理,如果Child超过10个时,通知Parent
代码分析:
EpisodeListView.java 作用:负责组配两个RecycleView填充对应的数据
焦点监听及获焦情况
public class EpisodeListView extends RelativeLayout implements View.OnFocusChangeListener { public static final String TAG = EpisodeListView.class.getSimpleName(); private Context mContext; private RelativeLayout mContentPanel; private RecyclerView mChildrenView; private RecyclerView mParentView; private LinearLayoutManager mEpisodesLayoutManager; private LinearLayoutManager mGroupLayoutManager; private EpisodeListViewAdapter mEpisodeListAdapter; private ChildrenAdapter mChildrenAdapter; private ParentAdapter mParentAdapter; private Handler mHandler = new Handler(Looper.getMainLooper()); public EpisodeListView(Context context) { this(context, null); } public EpisodeListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public EpisodeListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); if (!isInEditMode()) { mContext = context; init(); } } private void init() { LayoutInflater inflater = LayoutInflater.from(mContext); inflater.inflate(R.layout.episodelist_layout, this, true); mChildrenView = (RecyclerView) findViewById(R.id.episodes); mParentView = (RecyclerView) findViewById(R.id.groups); mEpisodesLayoutManager = new LinearLayoutManager(mContext, LinearLayout.HORIZONTAL, false); mGroupLayoutManager = new LinearLayoutManager(mContext, LinearLayout.HORIZONTAL, false); mChildrenView.setLayoutManager(mEpisodesLayoutManager); mParentView.setLayoutManager(mGroupLayoutManager); mChildrenView.setItemAnimator(new DefaultItemAnimator()); mParentView.setItemAnimator(new DefaultItemAnimator()); mChildrenView.setOnFocusChangeListener(this); mParentView.setOnFocusChangeListener(this); this.setOnFocusChangeListener(this); } public void setAdapter(final EpisodeListViewAdapter adapter) { mEpisodeListAdapter = adapter; mChildrenAdapter = adapter.getEpisodesAdapter(); mParentAdapter = adapter.getGroupAdapter(); mChildrenView.setAdapter(mChildrenAdapter); mParentView.setAdapter(mParentAdapter); mParentAdapter.setOnItemClickListener(new ParentAdapter.OnItemClickListener() { @Override public void onGroupItemClick(View view, int position) { mEpisodesLayoutManager.scrollToPositionWithOffset(adapter.getChildrenPosition(position), 0); } }); mParentAdapter.setOnItemFocusListener(new ParentAdapter.OnItemFocusListener() { @Override public void onGroupItemFocus(View view, int position, boolean hasFocus) { int episodePosition = adapter.getChildrenPosition(position); mChildrenAdapter.setCurrentPosition(episodePosition); mEpisodesLayoutManager.scrollToPositionWithOffset(adapter.getChildrenPosition(position), 0); } }); mChildrenAdapter.setOnItemFocusListener(new ChildrenAdapter.OnItemFocusListener() { @Override public void onEpisodesItemFocus(View view, int position, boolean hasFocus) { if (hasFocus) { int groupPosition = adapter.getParentPosition(position); mGroupLayoutManager.scrollToPositionWithOffset(groupPosition, 0); mParentAdapter.setCurrentPosition(adapter.getParentPosition(groupPosition)); } } }); mChildrenAdapter.setOnItemClickListener(new ChildrenAdapter.OnItemClickListener() { @Override public void onEpisodesItemClick(View view, int position) { } }); } public void setLongFocusListener(ChildrenAdapter.OnItemLongFocusListener listener) { mChildrenAdapter.setOnItemLongFocusListener(listener); } @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_UP: if (mParentView.hasFocus()) { mChildrenView.requestFocus(); return true; } break; case KeyEvent.KEYCODE_DPAD_DOWN: if (mChildrenView.hasFocus()) { mParentView.requestFocus(); return true; } break; case KeyEvent.KEYCODE_DPAD_RIGHT: if (mChildrenView.hasFocus() && mChildrenAdapter.getCurrentPosition() >= mChildrenAdapter.getData().size() - 1) { return true; } if (mParentView.hasFocus() && mParentAdapter.getCurrentPosition() >= mParentAdapter.getDatas().size() - 1) { return true; } } } return super.dispatchKeyEvent(event); } @Override public void onFocusChange(View v, boolean hasFocus) { if (v == this && hasFocus) { mChildrenView.requestFocus(); } else if (v == mChildrenView && hasFocus) { View child = mChildrenView.getLayoutManager().findViewByPosition(mChildrenAdapter.getCurrentPosition()); if (child != null) { child.requestFocus(); } else { int lastPosition = mEpisodesLayoutManager.findLastVisibleItemPosition(); child = mEpisodesLayoutManager.findViewByPosition(lastPosition); if (child != null) child.requestFocus(); } } else if (v == mParentView && hasFocus) { View child = mParentView.getLayoutManager().findViewByPosition(mParentAdapter.getCurrentPosition()); if (child != null) { child.requestFocus(); } } }}
EpisodeListViewAdapter 作用:抽象类,在实例化时负责将外部数据转成list传入
实例化ParentAdapter及ChildrenAdapter
public abstract class EpisodeListViewAdapter{ private ChildrenAdapter mChildrenAdapter; private ParentAdapter mParentAdapter; public EpisodeListViewAdapter() { mChildrenAdapter = new ChildrenAdapter(getChildrenList()); mParentAdapter = new ParentAdapter(getParentList()); } public ChildrenAdapter getEpisodesAdapter() { return mChildrenAdapter; } public ParentAdapter getGroupAdapter() { return mParentAdapter; } public void setSelectedPositions(List positions) { mChildrenAdapter.setSelectedPositions(positions); } public abstract List getChildrenList(); public abstract List getParentList(); public abstract int getChildrenPosition(int childPosition); public abstract int getParentPosition(int parentPosition);}
ParentAdapter 作用:每10集为一组,进行控制
public class ParentAdapter extends RecyclerView.Adapter { private static final int GROUPS_COLUMN_COUNT = 10; private OnItemClickListener mItemClickListener; private OnItemFocusListener mItemFocusListener; private List mDatas; private int parentWidth; private int itemWidth; private int mCurrentPosition; public ParentAdapter(List datas) { mDatas = datas; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.parent_item, parent, false); MyViewHolder holder = new MyViewHolder(view); parentWidth = parent.getMeasuredWidth(); itemWidth = (parentWidth - (holder.textView.getPaddingLeft() + holder.textView.getPaddingRight()) * (GROUPS_COLUMN_COUNT)) / GROUPS_COLUMN_COUNT + 1; return holder; } @Override public void onBindViewHolder(MyViewHolder holder, final int position) { holder.textView.setText(mDatas.get(position)); holder.textView.setWidth(itemWidth); holder.textView.setFocusable(true); holder.textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mItemClickListener.onGroupItemClick(v, position); } }); holder.textView.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { mItemFocusListener.onGroupItemFocus(v, position, hasFocus); mCurrentPosition = position; } } }); } @Override public int getItemCount() { return mDatas.size(); } public List getDatas() { return mDatas; } public int getCurrentPosition() { return mCurrentPosition; } public void setCurrentPosition(int position) { mCurrentPosition = position; } public void setOnItemFocusListener(OnItemFocusListener listener) { mItemFocusListener = listener; } public void setOnItemClickListener(OnItemClickListener listener) { mItemClickListener = listener; } class MyViewHolder extends RecyclerView.ViewHolder { TextView textView; public MyViewHolder(View view) { super(view); textView = (TextView) view.findViewById(R.id.item); } } public interface OnItemClickListener { void onGroupItemClick(View view, int position); } public interface OnItemFocusListener { void onGroupItemFocus(View view, int position, boolean hasFocus); }}
ChildrenAdapter 作用:每行最多显示10个,大于10可以左右变换
parent之间焦点变换时,children可立即响应。字数限制,不贴代码,可直接对应github看。
android tv 开发布局,Android TV开发总结(七)构建一个TV app中的剧集列表控件相关推荐
- Android TV开发总结(四)通过RecycleView构建一个TV app列表页(仿腾讯视频TV版)
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52854131 前言:昨晚看锤子手 ...
- Android TV开发总结(三)构建一个TV app的焦点控制及遇到的坑
原文:Android TV开发总结(三)构建一个TV app的焦点控制及遇到的坑 版权声明:我已委托"维权骑士"(rightknights.com)为我的文章进行维权行动.转载务必 ...
- android tv 菜单键,Android TV开发总结(三)构建一个TV app的焦点控制及遇到的坑
前言:关于<TV Metro界面(仿泰捷视频TV版)源码解析>由于都是相关代码,就不发公众号了,有兴趣的可以看链接:http://blog.csdn.net/hejjunlin/artic ...
- android tv 云播放器,Android TV开发总结(六)构建一个TV app的直播节目实例
近年来,Android TV的迅速发展,传统的有线电视受到较大的冲击,在TV上用户同样也可以看到各个有线电视的直播频道,相对于手机,这种直播节目,体验效果更佳,尤其是一样赛事节目,大屏幕看得才够痛快, ...
- Android开发-列表控件
列表控件是Android中最常见的控件之一 由于手机屏幕空间都比较有限,能够一次性在屏幕上显示的内容并不多,当我们的程序中有大量的数据需要展示的时候,就可以借助各种列表控件来实现. <ListV ...
- android 固定底部 布局_Android系统列表控件
在android系统控件中,有多个控件可以展示列表数据. 一.ListView 该组件是android中最常用的一个UI组件,用于实现在屏幕上显示多个内容,以便于我们用手指进行滑动. ListView ...
- Android怎么自定义布局,Android 创建自定义的布局
为可穿戴设备创建布局是和手持设备是一样的,除了我们需要为屏幕的尺寸和glanceability进行设计.但是不要期望通过搬迁手持应用的功能与UI到可穿戴上会有一个好的用户体验.仅仅在有需要的时候,我们 ...
- android滚动悬停布局,android布局滑动到顶端悬浮,录音
ok 首先我们来看看是不是咱们想要的效果!免得浪费大家时间 这篇没劲,我另外一篇详细一点可以看一下 怎么样要这个效果么 其实这个实现方法,很简单只需使用原生的一些控件就能实现,记得刚开始接触这个的时候 ...
- 【操作系统】HarmonyOS App开发工具HUAWEI DevEco Studio下载安装及第一个HarmonyOS App实战教程
路标 DevEco Studio简介 DevEco Studio的下载安装 安装过程演示 开发第一个基于Java的TV App 微信公众号原文链接:点击查看 2020年9月10日,华为开发者大会发布了 ...
最新文章
- MVC,MVP 和 MVVM 的图示
- Java编程之前的复习和练习
- 【错误记录】jar 执行错误 ( java.lang.UnsupportedClassVersionError: Unsupported major.minor version 52.0 )
- boost::type_erasure::add_assignable相关的测试程序
- linux拷贝文件夹怎么删除,linux文件及文件夹拷贝移动删除
- c语言鼠标移动响应,CSS鼠标响应事件经过、移动、点击示例介绍
- servlet下载文件(注意文件名字必须是英文)
- html中css二级联动,html二级联动学习笔记
- thinkphp跨库操作代码实例
- linux改狗命令,linux安装安全狗
- 【DTOJ】2701:问候
- 一步步完成FastDFS + Spring MVC上传下载整合示例
- 编译编译时,用不到的库,一定不要链接
- 22_多易教育之《yiee数据运营系统》用户画像-消费行为性别预测篇
- vs哪个版本比较好用_哪个跨境电商erp比较好用,跨境电商erp哪个好一点
- 如何向公众号添加的H5页面的链接(已解决)
- bypassing waf's in sql injection
- Aspnet Mvc 前后端分离项目手记(二)关于token认证
- 怎么卸载Safari浏览器?
- [0]SWM181-从零开发华芯微特MCU