Android TV 控件获取焦点特效

最近开始接触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;
        }
    }
}

到此,就完美实现了获取焦点的高亮显示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞猫个人博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值