fragment_test.xml
<!-- recyclerview的item有获取焦点放大效果,为了不让item放大后边缘被遮挡,recyclerview的父布局要添加
android:clipChildren="false"和android:clipToPadding="false"
-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="horizontal">
<!-- MenuItem -->
<!-- 要item选中修改焦点,需要实现setOnItemSelectListener.onItemSelect()处理 -->
<!--<ListView
android:id="@+id/lv_menu_item"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.3"
android:background="@color/colorPrimary"
android:paddingTop="50dp" />
-->
<!-- 指定listSelector的item选中时的背景 -->
<ListView
android:id="@+id/lv_menu_item"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.3"
android:background="@color/colorPrimary"
android:listSelector="@drawable/menu_item_focus_bg"
android:focusable="false" />
<!-- CardItem -->
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_card_content"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_margin="15dp" />
</LinearLayout>
menu_item_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:descendantFocusability="blocksDescendants" // 让item比ListView先获取到焦点
android:padding="20dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/ic_launcher_round"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/tv_menu_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:textColor="@android:color/white"
android:textSize="20sp"
tools:text="Row Fragment" />
</LinearLayout>
card_content_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="true" // recyclerview的item要设置focusable=true,方便监听setOnFocusChangeListener
android:layout_margin="10dp"
app:cardBackgroundColor="@color/colorAccent"
app:cardCornerRadius="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:background="@mipmap/ic_launcher"
tools:ignore="contentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:textColor="@android:color/white"
android:textSize="13sp"
android:text="Meerirgendwas" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:textColor="@android:color/tertiary_text_light"
android:textSize="12sp"
android:text="$3.00/lb" />
</LinearLayout>
</android.support.v7.widget.CardView>
menu_item_focus_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focus="true">
<shape>
<stroke
android:width="2dp"
android:color="@color/colorAccent"/>
</shape>
</item>
<item android:state_focus="false">
<shape>
<stroke
android:width="2dp"
android:color="@android:color/transparent"/>
</shape>
</item>
<item>
<shape>
<stroke
android:width="2dp"
android:color="@android:color/transparent"/>
</shape>
</item>
</selector>
card_content_focus_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/colorAccent"/>
<stroke
android:width="2dp"
android:color="@android:color/white"/>
</shape>
MenuItemAdapter.java
package com.example.customleanback.normal;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.example.customleanback.R;
import java.util.List;
public class MenuItemAdapter extends BaseAdapter {
private Context mContext;
private LayoutInflater mInflater;
private List<String> mMenuItemList;
private int mSelectedPosition;
public MenuItemAdapter(Context context, List<String> menuItemList) {
this.mContext = context;
this.mInflater = LayoutInflater.from(context);
this.mMenuItemList = menuItemList;
}
@Override
public int getCount() {
return mMenuItemList.size();
}
@Override
public String getItem(int position) {
return mMenuItemList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final HeaderTitleViewHolder viewHolder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.menu_item_list_item, parent, false);
viewHolder = new HeaderTitleViewHolder();
viewHolder.tvMenuItem = convertView.findViewById(R.id.tv_menu_item);
convertView.setTag(viewHolder);
} else {
viewHolder = (HeaderTitleViewHolder) convertView.getTag();
}
viewHolder.tvMenuItem.setText(mMenuItemList.get(position));
// 如果ListView或GridView没有设置listSelector属性指定背景就这样处理item获取焦点时背景转换
if (mSelectedPosition == position) {
convertView.setBackgroundResource(R.drawable.menu_item_focus_bg);
} else {
convertView.setBackgroundResource(0);
}
return convertView;
}
// 监听setOnItemSelectedListener,选中获取到焦点时通知item更新背景
public void setSelected(int position) {
mSelectedPosition = position;
notifyDataSetChanged();
}
private static class HeaderTitleViewHolder {
TextView tvMenuItem;
}
}
CardContentAdapter.java
package com.example.customleanback.normal;
import android.content.Context;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.customleanback.R;
import com.example.customleanback.utils.AnimationUtils;
import java.util.List;
public class CardContentAdapter extends RecyclerView.Adapter<CardContentAdapter.ContentViewHolder> {
private LayoutInflater mInflater;
private List<String> mCardContentList;
public CardContentAdapter(Context context, List<String> cardContentList) {
this.mInflater = LayoutInflater.from(context);
this.mCardContentList = cardContentList;
}
@Override
public ContentViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ContentViewHolder(mInflater.inflate(R.layout.card_content_list_item, parent, false));
}
@Override
public void onBindViewHolder(final ContentViewHolder holder, int position) {
// 监听遥控器ok确认键
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(holder.itemView, holder.getAdapterPosition());
}
}
});
// 监听item获取焦点修改背景和item放大
holder.itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
float scaleX = hasFocus ? 1.15f : 1f;
float scaleY = hasFocus ? 1.15f : 1f;
AnimationUtils.focusScale(v, scaleX, scaleY);
holder.itemView.setBackgroundResource(
hasFocus ? R.drawable.card_content_focus_bg : R.color.colorAccent);
}
});
}
@Override
public int getItemCount() {
return mCardContentList.size();
}
public static class ContentViewHolder extends RecyclerView.ViewHolder {
public ContentViewHolder(View itemView) {
super(itemView);
}
}
private OnItemClickListener mOnItemClickListener;
public void setOnItemClickListener(OnItemClickListener listener) {
this.mOnItemClickListener = listener;
}
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
}
AnimationUtils.java
package com.example.customleanback.utils;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.view.View;
import android.view.ViewGroup;
public class AnimationUtils {
public static void focusScale(View view, float scaleX, float scaleY) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ViewCompat.animate(view)
.scaleX(scaleX)
.scaleY(scaleY)
.translationZ(1)
.start();
} else {
ViewCompat.animate(view)
.scaleX(scaleX)
.scaleY(scaleY)
.start();
ViewGroup parent = (ViewGroup) view.getParent();
parent.requestLayout();
parent.invalidate();
}
}
}
TestFragment.java
package com.example.customleanback.normal;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import com.example.customleanback.R;
import java.util.ArrayList;
import java.util.List;
/**
* 1、ListView、GridView的item不能设置focusable=true,否则无法响应onItemClickListener
*
* 2、ListView、GridView的item焦点监听:
* 方法1:ListView.setOnItemSelectedListener、GridView.setOnItemSelectedListener,在callback刷新列表内容
* 方法2:在xml文件中设置ListView或GridView的listSelector指定item选中时的背景
*
* 3、RecyclerView的item根布局要设置focusable=true,才能监听holder.itemView.setOnFocusChangeListener
*
* 4、添加获取焦点放大item时,为了防止边缘被遮挡,在xml中RecyclerView的ViewGroup布局添加clipToPadding=false和clipChildren=false
*
* 5、遥控器控制上下左右时,比如ListView按下右键切换到RecyclerView获取焦点,或者RecyclerView按左键切换到ListView,
* 焦点的选中时随机的,会让距离当前view最近的view获取焦点
*
* 6、为了防止焦点乱跑,可以监听setOnKeyListener拦截事件处理
*/
public class TestFragment extends Fragment {
private ListView mLvMenuItem;
private RecyclerView mRvCardContent;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_test, container, false);
mLvMenuItem = rootView.findViewById(R.id.lv_menu_item);
mRvCardContent = rootView.findViewById(R.id.rv_card_content);
return rootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setupMenuItem();
setupCardContent();
}
private void setupCardContent() {
List<String> contentList = new ArrayList<>();
for (int i = 0; i < 15; i++) {
contentList.add("item" + i);
}
mRvCardContent.setHasFixedSize(true);
mRvCardContent.setLayoutManager(new GridLayoutManager(getContext(), 4));
CardContentAdapter cardContentAdapter = new CardContentAdapter(getContext(), contentList);
mRvCardContent.setAdapter(cardContentAdapter);
// 响应遥控器ok确认键点击
cardContentAdapter.setOnItemClickListener(new CardContentAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(getContext(), "click position " + position, Toast.LENGTH_SHORT).show();
}
});
}
private void setupMenuItem() {
List<String> menuItemList = new ArrayList<>();
menuItemList.add("Page Fragment");
menuItemList.add("Row Fragment");
menuItemList.add("Settings Fragment");
menuItemList.add("User Agreement Fragment");
final MenuItemAdapter menuItemAdapter = new MenuItemAdapter(getContext(), menuItemList);
mLvMenuItem.setAdapter(menuItemAdapter);
// 响应遥控器ok确认点击
mLvMenuItem.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(getContext(), "click position " + position, Toast.LENGTH_SHORT).show();
}
});
// 响应遥控器上下左右按键获取焦点
// 如果ListView或GridView已经设置了listSelector修改item背景,一般写该监听是要处理item选中时的其他逻辑
mLvMenuItem.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
// menuItemAdapter.setSelected(position);
// 其他逻辑
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
// 拦截遥控器右键,自动选中RecyclerView第一个位置,防止焦点乱跑
mLvMenuItem.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
mRvCardContent.requestFocus();
mRvCardContent.smoothScrollToPosition(0);
return true;
}
return false;
}
});
}
}