Android TV 10开发实战03VerticalGridView边界拦截
上一篇简单实现列表不同分类展示具体列表详情,这篇就解决列表返回分类的时候没有恢复默认选择的情况。
具体可以看上一篇:https://www.sunofbeach.net/a/1483075472387506178
整个入门系列
Android Tv开发01体验demo (sunofbeach.net)
Android TV开发02强制聚焦到某个控件 (sunofbeach.net)
Android TV开发03学习HorizontalGridView (sunofbeach.net)
Android TV开发04学习两个RecyclerView焦点转移 (sunofbeach.net)
Android TV开发05学习VerticalGridView (sunofbeach.net)
Android TV开发06学习RecyclerView焦点换行 (sunofbeach.net)
Android TV 07开发工具推荐 (sunofbeach.net)
Android TV 08开发实战01焦点控制案例 (sunofbeach.net)
Android TV 09开发实战02VerticalGridView (sunofbeach.net)
修复这种返回定位错误的问题。
为什么会错位?
焦点从右边分类切换到左边列表后,然后列表返回左边边界,如果再向右移动,这个时候走的是系统寻焦,就近原则,就好比上面gif,从左往右,靠近右边item的那个分类获得焦点了。这个情况下系统寻焦就不好使了,我们要改手动模式。
什么情况下才恢复到分类上面呢?
这是一个网格列表,当焦点到达rv的右边边沿,再往右的时候,我们就要拦截下来。
那我怎么知道我现在在最右边呢?
ViewGroup中有按键分发逻辑,在vg内重写,然后处理向右搜索焦点的时候,然后在vg内搜索失败了,那表示向右没有了子view。
情况1:如果列表不满一排,向右搜索焦点null,那么就是最右边
情况2:如果当前位置+1%column == 0那也是刚好达到一排最大数目
情况3:当我们在最后一排,不满一排情况下,我们需要切换上一排的下一个聚焦。
@Override public boolean dispatchKeyEvent(KeyEvent event) { try { // 按键按下,切向右 if (KeyEvent.ACTION_DOWN == event.getAction() && KeyEvent.KEYCODE_DPAD_RIGHT == event.getKeyCode()) { // 获取当前vg的聚焦的view View focusedChild = getFocusedChild(); // 搜索向右方向的下个焦点 View nextFocus = FocusFinder.getInstance().findNextFocus(this, focusedChild, FOCUS_RIGHT); if (nextFocus == null) { //当前聚焦的view,是不是列的倍数 LayoutManager layoutManager = getLayoutManager(); if (layoutManager == null) { return false; // 获得当前聚焦的item在整个rv中的位置 int position = layoutManager.getPosition(focusedChild); // 如果位置刚好是边界,比如设置column 4 那就是position + 1 然后% == 0 证明到了右边 if ((position + 1) % numColumns == 0) { Log.d(TAG, "->> 右边尽头了 edge " + position); if (mOnEdgeInterceptListener != null) { mOnEdgeInterceptListener.onEdgeRight(); } else { //右边聚焦切换上一行情况,如果不足一排4个,要特殊处理 if (getAdapter().getItemCount() <= numColumns) { if (mOnEdgeInterceptListener != null) { mOnEdgeInterceptListener.onEdgeRight(); } else { int targetIndex = position - numColumns + 1; View edgeRightView = layoutManager.findViewByPosition(targetIndex); if (edgeRightView != null) { boolean requestFocus = edgeRightView.requestFocus(); Log.i(TAG, "->>>>> 右边尽头了,修复聚焦" + position + "," + targetIndex + "," + requestFocus); Log.i(TAG, "->>>>> 右边尽头了 " + position); return true; } else if (KeyEvent.ACTION_DOWN == event.getAction() && KeyEvent.KEYCODE_DPAD_DOWN == event.getKeyCode()) { View focusedChild = getFocusedChild(); View nextFocus = FocusFinder.getInstance().findNextFocus(this, focusedChild, FOCUS_DOWN); if (nextFocus == null) { LayoutManager layoutManager = getLayoutManager(); if (layoutManager == null || focusedChild == null || focusedChild.getLayoutParams() == null) { return false; int itemCount = getAdapter().getItemCount(); int position = layoutManager.getPosition(focusedChild); int lastRowCount = itemCount % numColumns; //如果等于0,就是刚好填满 if (lastRowCount != 0 && position < (itemCount - lastRowCount)) { //那就是已经最后排了,给系统处理,否则 int dx = itemCount - 1 - position; //dx就是剩下的item数目,这个数目必须小于span count 4 Log.i(TAG, "->>>>> 尽头了,剩下" + dx + "," + position + "," + itemCount); if (dx > 0 && dx < numColumns) { //直接聚焦最后一个就行了 View edgeBottomView = layoutManager.findViewByPosition(itemCount - 1); if (edgeBottomView != null) { boolean focus = edgeBottomView.requestFocus(); Log.i(TAG, "->>>>> 尽头了, " + focus); return true; } catch (Exception e) { e.printStackTrace(); return super.dispatchKeyEvent(event);右边拦截的接口
private OnEdgeInterceptListener mOnEdgeInterceptListener; public void setOnEdgeInterceptListener(OnEdgeInterceptListener onEdgeInterceptListener) { mOnEdgeInterceptListener = onEdgeInterceptListener; public interface OnEdgeInterceptListener { void onEdgeRight();恢复分类焦点
上面我们的Grid列表已做好了右边拦截,当我们到达右边之后,要恢复分类焦点。
每次分类聚焦后,我们把聚焦的view保存下来,恢复的时候直接拿到view,请求焦点就行了。
mCategoryAdapter.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (!hasFocus) { return; currentFocusType = focus_type; // 焦点变化之后,记录下来 typeFocusView = v; int categoryIndex = (int) v.getTag(); BlogCategory.DataCategoryBean bean = mCategoryAdapter.getData().get(categoryIndex); // 请求对应分类数据 getArticleById(bean.getId()); tvCategory.setText(bean.getName());在拦截Grid列表右边时候恢复。
vgv.setOnEdgeInterceptListener(new BlogGridView.OnEdgeInterceptListener() { @Override public void onEdgeRight() { // 恢复分类的聚焦 if (typeFocusView == null) { return; currentFocusType = focus_type; typeFocusView.requestFocus();整个效果就是这样啦。
重点找到拦截的条件,和知道vg内部搜索焦点机制和相关api。
项目地址 https://gitee.com/dong_rong/android-tv-sample
具体代码可以看Demo07Activity。
这一个实战案例到此完结了~~
原创发布于 ,未经作者授权,禁止转载