最近开始接触TV端开发,初转入TV端,有好多不适应。尤其是对焦点的处理,上篇文章对leanback的使用做了简单的介绍,并对Item获取焦点后的高亮显示进行了处理。但是,如果按照同样的方法在非列表页实现时,效果并不理想,并会出现难以控制等问题。于是,开始寻找新的实现方式。在网上找了好多实现方式,均不是特别理想。最终找到了一篇比较满意的文章,自己在此基础上进行了优化改进。Demo的截图如下:
下面就详细介绍一下实现流程吧。
(1)自定义View,实现获取焦点时放大,失去焦点时缩小,详细代码如下:
package cn.chinaiptv.newaikan.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.RelativeLayout;
import cn.chinaiptv.newaikan.R;
public class FocusRelativeLayout extends RelativeLayout {
private Rect mBound;
private Drawable mDrawable;
private Rect mRect;
private Animation scaleSmallAnimation;
private Animation scaleBigAnimation;
public FocusRelativeLayout(Context context) {
super(context);
init();
}
public FocusRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public FocusRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
protected void init() {
setWillNotDraw(false);
mRect = new Rect();
mBound = new Rect();
//获取焦点后,外侧的阴影图片
mDrawable = getResources().getDrawable(R.drawable.poster_shadow_4);
setChildrenDrawingOrderEnabled(true);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
}
@Override
protected void onDraw(Canvas canvas) {
if (hasFocus()) {
super.getDrawingRect(mRect);
mBound.set(-5+mRect.left, -5+mRect.top, 5+mRect.right, 5+mRect.bottom);
mDrawable.setBounds(mBound);
canvas.save();
mDrawable.draw(canvas);
canvas.restore();
}
super.onDraw(canvas);
}
@Override
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
if (gainFocus) {
bringToFront();
getRootView().requestLayout();
getRootView().invalidate();
zoomOut();
} else {
zoomIn();
}
}
/**
* 缩小动画
*/
private void zoomIn() {
if (scaleSmallAnimation == null) {
scaleSmallAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.anim_scale_small);
}
startAnimation(scaleSmallAnimation);
}
/**
* 放倒动画
*/
private void zoomOut() {
if (scaleBigAnimation == null) {
scaleBigAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.anim_scale_big);
}
startAnimation(scaleBigAnimation);
}
}
(2)定义两个放大和缩小的动画文件
放大的动画文件:anim_scale_big.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true"
android:fillBefore="false"
android:shareInterpolator="false" >
<scale
android:duration="500"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50.0%"
android:pivotY="50.0%"
android:repeatCount="0"
android:toXScale="1.1"
android:toYScale="1.1"
android:fillAfter="true" />
</set>
缩小的动画文件:anim_scale_small.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="false"
android:fillBefore="true"
android:shareInterpolator="false" >
<scale
android:duration="500"
android:fromXScale="1.1"
android:fromYScale="1.1"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50.0%"
android:pivotY="50.0%"
android:repeatCount="0"
android:toXScale="1.0"
android:toYScale="1.0"
android:fillAfter="true" />
</set>
(3)准备工作就结束下,下面开始些布局文件。由于本例子中的图片布局基本类似,所以就单独给出一个item文件(例子中使用了圆角图片,在此不再贴出代码):
<?xml version="1.0" encoding="utf-8"?>
<cn.chinaiptv.newaikan.view.FocusRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/item"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true">
<cn.chinaiptv.newaikan.view.RoundAngleImageView
android:id="@+id/img"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/mid_bottom"
android:duplicateParentState="true"
android:scaleType="fitXY"
app:roundHeight="@dimen/w_10"
app:roundWidth="@dimen/w_10" />
<cn.chinaiptv.newaikan.view.RoundAngleImageView
android:id="@+id/hover"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/sl_image_home_navigator"
android:duplicateParentState="true"
android:scaleType="fitXY"
app:roundHeight="@dimen/w_10"
app:roundWidth="@dimen/w_10" />
</cn.chinaiptv.newaikan.view.FocusRelativeLayout>
(4)引用定义好的子布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:id="@+id/fragment_two"
android:clipChildren="false"
android:clipToPadding="false" >
<include
android:id="@+id/channel_0"
android:layout_width="@dimen/w_320"
android:layout_height="@dimen/h_240"
layout="@layout/home_page_channel_item"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginRight="@dimen/h_15" />
<include
android:id="@+id/channel_1"
android:layout_width="@dimen/w_320"
android:layout_height="@dimen/h_240"
android:layout_marginTop="@dimen/h_15"
layout="@layout/home_page_channel_item"
android:layout_below="@id/channel_0"
android:layout_marginRight="@dimen/h_15"
android:layout_alignLeft="@id/channel_0" />
<include
android:id="@+id/channel_2"
android:layout_width="@dimen/w_320"
android:layout_height="@dimen/h_240"
layout="@layout/home_page_channel_item"
android:layout_toRightOf="@id/channel_0"
android:layout_alignTop="@id/channel_0"
android:layout_marginRight="@dimen/h_15"/>
<include
android:id="@+id/channel_3"
android:layout_width="@dimen/w_320"
android:layout_height="@dimen/h_240"
layout="@layout/home_page_channel_item"
android:layout_alignLeft="@id/channel_2"
android:layout_marginTop="@dimen/h_15"
android:layout_marginRight="@dimen/h_15"
android:layout_below="@id/channel_2"/>
<include
android:id="@+id/channel_4"
android:layout_width="@dimen/w_320"
android:layout_height="@dimen/h_240"
layout="@layout/home_page_channel_item"
android:layout_toRightOf="@id/channel_2"
android:layout_alignTop="@id/channel_2"
android:layout_marginRight="@dimen/h_15"/>
<include
android:id="@+id/channel_5"
android:layout_width="@dimen/w_320"
android:layout_height="@dimen/h_240"
layout="@layout/home_page_channel_item"
android:layout_alignLeft="@id/channel_4"
android:layout_marginTop="@dimen/h_15"
android:layout_marginRight="@dimen/h_15"
android:layout_below="@id/channel_4"/>
<include
android:id="@+id/channel_6"
android:layout_width="@dimen/w_320"
android:layout_height="@dimen/h_240"
layout="@layout/home_page_channel_item"
android:layout_toRightOf="@id/channel_4"
android:layout_alignTop="@id/channel_4"
android:layout_marginRight="@dimen/h_15"/>
<include
android:id="@+id/channel_7"
android:layout_width="@dimen/w_320"
android:layout_height="@dimen/h_240"
android:layout_marginTop="@dimen/h_15"
layout="@layout/home_page_channel_item"
android:layout_alignLeft="@id/channel_6"
android:layout_marginRight="@dimen/h_15"
android:layout_below="@id/channel_6"/>
<include
android:id="@+id/channel_8"
android:layout_width="@dimen/w_320"
android:layout_height="@dimen/h_240"
layout="@layout/home_page_channel_item"
android:layout_toRightOf="@id/channel_6"
android:layout_alignTop="@id/channel_6"
android:layout_marginRight="@dimen/h_15"/>
<include
android:id="@+id/channel_9"
android:layout_width="@dimen/w_320"
android:layout_marginTop="@dimen/h_15"
android:layout_height="@dimen/h_240"
layout="@layout/home_page_channel_item"
android:layout_alignLeft="@id/channel_8"
android:layout_marginRight="@dimen/h_15"
android:layout_below="@id/channel_8"/>
</RelativeLayout>
最后,在JAVA代码处理滑动到最左侧,最右侧,最下方时。进行模块切换,具体代码如下:
package cn.chinaiptv.newaikan.fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.RelativeLayout;
import cn.chinaiptv.newaikan.R;
import cn.chinaiptv.newaikan.view.FocusRelativeLayout;
/**
* Created by ddklsy163com on 16/12/19.
*/
public class FragmentTwo extends Fragment implements ViewTreeObserver.OnGlobalFocusChangeListener {
private View view;
private FocusRelativeLayout channel_0;
private FocusRelativeLayout channel_1;
private FocusRelativeLayout channel_2;
private FocusRelativeLayout channel_3;
private FocusRelativeLayout channel_4;
private FocusRelativeLayout channel_5;
private FocusRelativeLayout channel_6;
private FocusRelativeLayout channel_7;
private FocusRelativeLayout channel_8;
private FocusRelativeLayout channel_9;
private RelativeLayout fragment_two;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (view != null) {
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null) {
parent.removeView(view);
}
return view;
}
view = inflater.inflate(R.layout.fragment_two, null);
initView(view);
fragment_two.getViewTreeObserver().addOnGlobalFocusChangeListener(this);
return view;
}
private void initView(View view) {
fragment_two = (RelativeLayout) view.findViewById(R.id.fragment_two);
channel_0 = (FocusRelativeLayout) view.findViewById(R.id.channel_0);
channel_1 = (FocusRelativeLayout) view.findViewById(R.id.channel_1);
channel_2 = (FocusRelativeLayout) view.findViewById(R.id.channel_2);
channel_3 = (FocusRelativeLayout) view.findViewById(R.id.channel_3);
channel_4 = (FocusRelativeLayout) view.findViewById(R.id.channel_4);
channel_5 = (FocusRelativeLayout) view.findViewById(R.id.channel_5);
channel_6 = (FocusRelativeLayout) view.findViewById(R.id.channel_6);
channel_7 = (FocusRelativeLayout) view.findViewById(R.id.channel_7);
channel_8 = (FocusRelativeLayout) view.findViewById(R.id.channel_8);
channel_9 = (FocusRelativeLayout) view.findViewById(R.id.channel_9);
}
@Override
public void onGlobalFocusChanged(View oldFocus, View newFocus) {
switch (newFocus.getId()) {
case R.id.channel_0:
case R.id.channel_1:
channel_0.setNextFocusLeftId(R.id.tv_one);
channel_1.setNextFocusLeftId(R.id.tv_one);
channel_1.setNextFocusDownId(R.id.tv_two);
case R.id.channel_8:
case R.id.channel_9:
channel_8.setNextFocusRightId(R.id.tv_three);
channel_9.setNextFocusRightId(R.id.tv_three);
channel_9.setNextFocusDownId(R.id.tv_two);
break;
case R.id.channel_2:
case R.id.channel_3:
case R.id.channel_4:
case R.id.channel_5:
case R.id.channel_6:
case R.id.channel_7:
channel_3.setNextFocusDownId(R.id.tv_two);
channel_5.setNextFocusDownId(R.id.tv_two);
channel_7.setNextFocusDownId(R.id.tv_two);
break;
}
}
}
到此,就完美实现了获取焦点的高亮显示。