Android TV

VerticalGridView第一行获取焦点后,再次按向上键,

2017-09-18  本文已影响300人  dongdengke

最近在做项目中,有这么一个需求。点击电视剧某一集进入详情页,并可以点击选集按钮进行选集。于是乎,想到了使用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
上一篇下一篇

猜你喜欢

热点阅读