VerticalGridView第一行获取焦点后,再次按向上键,上方的控件获取不到焦点解决方案

最近在做项目中,有这么一个需求。点击电视剧某一集进入详情页,并可以点击选集按钮进行选集。于是乎,想到了使用VerticalGridView来实现(VerticalGridView是V17新推出的控件,不熟悉的朋友可以查找资料学习下)。使用VerticalGridView很容易实现了基本的功能,但是遇到一个很棘手的问题。就是当第一行获取焦点后,再次按向上键,上方的控件获取不到焦点。在网上找了好多解决方案,最终也没有解决问题。最后,使用setOnKeyListner方法来实现。虽说解决了问题,带来了新的问题:在第二行按向上键后,第一行先获取到焦点,接着第一行有失去焦点,上方控件获取到焦点了。尼玛,我也是醉了。最终发现onKey方法执行了两次,知道了原因,问题就好解决了。下面,就介绍下实现方案。
本Demo还实现了,获取焦点高亮显示的效果。VerticalGridView的使用不在介绍,不熟悉的可以参考
android leanback使用详解以及获取焦点高亮

首先,布局页面编写:
(1)主布局页面

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:lb="http://schemas.android.com/apk/res-auto"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:background="@drawable/index_bg"  
    android:orientation="vertical">  
  
    <LinearLayout  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginTop="@dimen/h_56"  
        android:orientation="horizontal">  
  
        <View  
            android:id="@+id/btn_pd_play"  
            style="@style/pd_btn"  
            android:background="@drawable/pd_play_selector"  
            android:visibility="visible" />  
  
        <View  
            android:id="@+id/btn_pd_collect"  
            style="@style/pd_btn.non_starter"  
            android:background="@drawable/pd_collect_selector"  
            android:visibility="visible" />  
  
        <View  
            android:id="@+id/btn_pd_xuanji"  
            style="@style/pd_btn.non_starter"  
            android:visibility="visible" />  
    </LinearLayout>  
  
    <android.support.v17.leanback.widget.HorizontalGridView  
        android:id="@+id/hv_tuijian"  
        android:layout_width="match_parent"  
        android:layout_height="@dimen/h_400"  
        android:layout_marginLeft="@dimen/w_145"  
        android:layout_marginRight="@dimen/w_115"  
        android:focusable="true"  
        android:focusableInTouchMode="true"  
        lb:horizontalMargin="@dimen/w_15"  
        lb:numberOfRows="1"></android.support.v17.leanback.widget.HorizontalGridView>  
  
    <android.support.v17.leanback.widget.VerticalGridView  
        android:id="@+id/hv_suanji"  
        android:layout_width="match_parent"  
        android:layout_height="@dimen/h_480"  
        android:layout_marginLeft="@dimen/w_145"  
        android:layout_marginRight="@dimen/w_115"  
        android:focusable="true"  
        android:focusableInTouchMode="true"  
        android:paddingTop="@dimen/h_10"  
        lb:horizontalMargin="@dimen/w_15"  
        lb:numberOfColumns="10"  
        lb:numberOfRows="2"></android.support.v17.leanback.widget.VerticalGridView>  
</LinearLayout> 

(2)item布局页面

<?xml version="1.0" encoding="utf-8"?>  
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:id="@+id/ll_item_search"  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"  
    android:clipChildren="false"  
    android:clipToPadding="false"  
    android:focusable="true"  
    android:focusableInTouchMode="true"  
    android:orientation="vertical" >  
  
    <RelativeLayout  
        android:id="@+id/rl_scale"  
        android:layout_width="@dimen/w_220"  
        android:layout_height="@dimen/h_260"  
        android:background="@drawable/bg_pic_n" >  
  
        <ImageView  
            android:id="@+id/iv_pic"  
            android:layout_width="match_parent"  
            android:layout_height="match_parent"  
            android:background="@drawable/mid_bottom"  
            android:scaleType="fitXY" />  
  
        <TextView  
            android:id="@+id/tv_target"  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:layout_alignBottom="@id/iv_pic"  
            android:textColor="@color/search_text"  
            android:gravity="center"  
            android:padding="@dimen/w_10"  
            android:text="更新至"  
            android:textSize="@dimen/w_30"  
            android:visibility="gone" />  
    </RelativeLayout>  
  
    <FrameLayout  
        android:layout_width="@dimen/w_220"  
        android:layout_height="wrap_content"  
        android:layout_below="@id/rl_scale"  
        android:layout_centerHorizontal="true"  
        android:layout_marginTop="@dimen/h_2" >  
  
        <TextView  
            android:id="@+id/tv_name"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:layout_gravity="center"  
            android:singleLine="true"  
            android:textColor="@color/search_text"  
            android:textSize="@dimen/w_32" />  
    </FrameLayout>  
  
</RelativeLayout>  

接着,编写通用的适配器:

package cn.chinaiptv.foucedemo.adapter;  
  
import android.support.v7.widget.RecyclerView;  
import android.view.LayoutInflater;  
import android.view.View;  
import android.view.ViewGroup;  
  
import java.util.Collections;  
import java.util.Comparator;  
import java.util.List;  
  
/** 
 * @author zeng_yong_chang@163.com 
 */  
public abstract class RvAdapter<T, VH extends RecyclerView.ViewHolder> extends  
        RecyclerView.Adapter<VH> {  
    protected List<T> mList;  
  
    public RvAdapter(List<T> data) {  
        setData(data);  
    }  
  
    public List<T> getData() {  
        return mList;  
    }  
  
    public void setData(List<T> data) {  
        mList = data;  
        notifyDataSetChanged();  
    }  
  
    public void sort(Comparator<T> comparator) {  
        Collections.sort(mList, comparator);  
        notifyDataSetChanged();  
    }  
  
    protected View inflateView(int itemViewLayoutId, ViewGroup parent) {  
        return LayoutInflater.from(parent.getContext()).inflate(  
                itemViewLayoutId, parent, false);  
    }  
  
    @Override  
    public int getItemCount() {  
        return mList.size();  
    }  
}  

其次,VerticalGridView适配器的编写,具体代码如下:

package cn.chinaiptv.foucedemo.adapter;  
  
import android.annotation.SuppressLint;  
import android.content.Context;  
import android.support.v17.leanback.widget.VerticalGridView;  
import android.support.v7.widget.RecyclerView.Adapter;  
import android.view.KeyEvent;  
import android.view.LayoutInflater;  
import android.view.View;  
import android.view.ViewGroup;  
  
import java.util.List;  
  
import cn.chinaiptv.foucedemo.R;  
import cn.chinaiptv.foucedemo.bean.Content;  
import cn.chinaiptv.foucedemo.cn.chinaiptv.foucedemo.holder.SearchListViewHolder;  
import cn.chinaiptv.foucedemo.utils.FocusUtisl;  
  
@SuppressLint("InflateParams")  
public class XuanJiAdapter extends  
        Adapter<SearchListViewHolder> {  
  
    private List<Content> contentList;  
  
    private Context context;  
  
    private VerticalGridView hv_suanji;  
    private View btn_pd_xuanji;  
  
    public XuanJiAdapter(List<Content> recommenList, VerticalGridView hv_suanji, View btn_pd_xuanji) {  
        contentList = recommenList;  
        this.hv_suanji = hv_suanji;  
        this.btn_pd_xuanji = btn_pd_xuanji;  
    }  
  
    @Override  
    public int getItemCount() {  
        return contentList.size();  
  
    }  
  
    @Override  
    public void onBindViewHolder(final SearchListViewHolder holder, final int position) {  
        holder.itemView.setOnClickListener(new FocusUtisl.MyClick(position));  
        holder.itemView.setOnFocusChangeListener(new FocusUtisl.MyFocuchange(holder.rl_scale, holder.tv_name,  
                position));  
        String name = "";  
        String miniPicURI = "";  
        Content content = contentList.get(position);  
        name = content.getName();  
        miniPicURI = content.getMiniPicURI();  
        holder.tv_name.setText(name);  
    }  
  
    @Override  
    public SearchListViewHolder onCreateViewHolder(ViewGroup parent, int arg1) {  
        View inflate = LayoutInflater.from(parent.getContext()).inflate(  
                R.layout.item_serialdetail_recomm, null);  
        SearchListViewHolder holder = new SearchListViewHolder(inflate);  
        context = parent.getContext();  
        return holder;  
    }  
  
}  

接下来就要在Activity中设置数据了,代码如下:

package cn.chinaiptv.foucedemo;  
  
import android.app.Activity;  
import android.os.Bundle;  
import android.support.v17.leanback.widget.HorizontalGridView;  
import android.support.v17.leanback.widget.VerticalGridView;  
import android.view.View;  
  
import java.util.ArrayList;  
  
import cn.chinaiptv.foucedemo.adapter.SerialDetailRecommAdapter;  
import cn.chinaiptv.foucedemo.adapter.XuanJiAdapter;  
import cn.chinaiptv.foucedemo.bean.Content;  
  
/** 
 * 连续剧详情页 
 */  
public class MainActivity extends Activity {  
  
    private  HorizontalGridView hv_tuijian;  
    private VerticalGridView hv_suanji;  
    private  XuanJiAdapter xuanjiAdapter;  
    private View btn_pd_xuanji;  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_series_detail);  
        btn_pd_xuanji=findViewById(R.id.btn_pd_xuanji);  
        initView();  
    }  
  
    private void initView() {  
        hv_tuijian=(HorizontalGridView)findViewById(R.id.hv_tuijian);  
        hv_suanji=(VerticalGridView)findViewById(R.id.hv_suanji);  
        getListRecommen();  
        getXuanJi();  
  
    }  
    private  ArrayList<Content> recommenList;  
    private  ArrayList<Content> xuanJiList;  
    private SerialDetailRecommAdapter recommenResultAdapter;  
    /** 
     * 获取可能要找的内容 
     */  
    private void getListRecommen() {  
        recommenList=new ArrayList<Content>();  
        for(int i=0;i<10;i++){  
            Content content=new Content();  
            content.setName("十月风暴"+i);  
            content.setType("ddd");  
            recommenList.add(content);  
        }  
        recommenResultAdapter = new SerialDetailRecommAdapter(recommenList);  
        hv_tuijian.setAdapter(recommenResultAdapter);  
    }  
    /** 
     * 获取可能要找的内容 
     */  
    private void getXuanJi() {  
        xuanJiList=new ArrayList<Content>();  
        for(int i=0;i<20;i++){  
            Content content=new Content();  
            content.setName("测试"+i);  
            content.setType("ddd");  
            xuanJiList.add(content);  
        }  
        xuanjiAdapter = new XuanJiAdapter(xuanJiList,hv_suanji,btn_pd_xuanji);  
        hv_suanji.setAdapter(xuanjiAdapter);  
    }  
}  

如果只这样去实现,第一行获取焦点后,再按向上键,上方的控件是获取不到代码的。解决方案如下:

@Override  
  public void onBindViewHolder(final SearchListViewHolder holder, final int position) {  
      holder.itemView.setOnClickListener(new FocusUtisl.MyClick(position));  
      holder.itemView.setOnFocusChangeListener(new FocusUtisl.MyFocuchange(holder.rl_scale, holder.tv_name,  
              position));  
      String name = "";  
      String miniPicURI = "";  
      Content content = contentList.get(position);  
      name = content.getName();  
      miniPicURI = content.getMiniPicURI();  
      holder.tv_name.setText(name);  
      holder.itemView.setOnKeyListener(new View.OnKeyListener() {  
          @Override  
          public boolean onKey(View v, int keyCode, KeyEvent event) {  
              if (event.getAction() == KeyEvent.ACTION_UP) {  
                  return true;  
              } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {  
                  if (position < 10) {  
                      hv_suanji.setFocusable(false);  
                      btn_pd_xuanji.requestFocus();  
                  }  
              }  
              return false;  
          }  
      });  
  
  }  

这样上方的控件就可以获取到焦点了。
最后,还有一些获取焦点时,高亮显示的方法,代码如下:

package cn.chinaiptv.foucedemo.utils;  
  
import android.view.View;  
  
import cn.chinaiptv.foucedemo.R;  
  
/** 
 * Created by ddklsy163com on 17/1/13. 
 */  
  
public class FocusUtisl {  
    private static OnItemCallBack onItemCallBack;  
  
    public static class MyFocuchange implements View.OnFocusChangeListener {  
  
        int position;  
        View rl_scale;  
        View tv_name;  
  
        /** 
         * 图片,文字都放缩 
         * 
         * @param position 
         * @param rl_scale 
         */  
        public MyFocuchange(View rl_scale, View tv_name, int position) {  
            this.position = position;  
            this.rl_scale = rl_scale;  
            this.tv_name = tv_name;  
//            Log.e("111111",position);  
        }  
  
        /** 
         * 仅仅放缩图片 
         * 
         * @param position 
         * @param rl_scale 
         */  
        public MyFocuchange(int position, View rl_scale) {  
            this.position = position;  
            this.rl_scale = rl_scale;  
        }  
  
        @Override  
        public void onFocusChange(View v, boolean hasFocus) {  
            if (hasFocus) {  
                if (rl_scale != null) {  
                    rl_scale.animate().scaleX(1.1f).scaleY(1.1f).start();  
                    rl_scale.setBackgroundResource(R.drawable.bg_pic_s);  
                }  
                if (tv_name != null)  
                    tv_name.animate().scaleX(1.1f).scaleY(1.1f).start();  
            } else {  
                if (rl_scale != null) {  
                    rl_scale.animate().scaleX(1.f).scaleY(1.f).start();  
                    rl_scale.setBackgroundResource(R.drawable.bg_pic_n);  
                }  
                if (tv_name != null)  
                    tv_name.animate().scaleX(1f).scaleY(1f).start();  
  
            }  
            if (onItemCallBack != null) {  
                onItemCallBack.onFocusChange(v, hasFocus, position);  
            }  
        }  
  
    }  
  
    public static class MyClick implements View.OnClickListener {  
  
        int position;  
  
        public MyClick(int position) {  
            this.position = position;  
        }  
  
        @Override  
        public void onClick(View v) {  
            if (onItemCallBack != null) {  
                onItemCallBack.onItemClick(v, position);  
            }  
        }  
    }  
  
    public void setOnItemCallBack(OnItemCallBack onItemCallBack) {  
        this.onItemCallBack = onItemCallBack;  
    }  
  
    public interface OnItemCallBack {  
        public void onFocusChange(View v, boolean hasFocus, int posiotion);  
  
        public void onItemClick(View v, int position);  
    }  
}  

到此,已经完美实现了获取焦点时高亮显示,并且解决了上方控件获取不到焦点的bug,Demo截图如下:

20170114161500728.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,932评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,554评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,894评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,442评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,347评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,899评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,325评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,980评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,196评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,163评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,085评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,826评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,389评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,501评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,753评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,171评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,616评论 2 339

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,223评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,972评论 4 60
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,333评论 0 17
  • 1.添加pom 2.启动类上添加注解 @EnableCircuitBreaker附图: 3.在@RequestMa...
    FantJ阅读 908评论 0 1
  • 题目:是什么决定了选择 选择真的非常重要,它决定了我们的过去现在和将来。怎么去做出正确的选择也一直是人们思索的话题...
    Doppio阅读 142评论 0 0