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