首页 > 代码库 > android tv焦点特效实现浅析

android tv焦点特效实现浅析

Android TV上的焦点凸显特效相信大家都看到过,那么我们就来实现它吧,首先上张效果图。



先说一下实现原理,主要通过重写RelativeLayout实现item,之后在其中加入scalanimation动画效果。刚开始处理时,还是发现了一些问题,比如item放大后会被其他item遮挡,如何添加选中边框等等,以及动画的实现等等。下面放上实现细节。

首先是item的代码:

<view xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item"
    android:layout_width="@dimen/home_channel_item_width"
    android:layout_height="@dimen/home_channel_item_height"
    class="com.eastelsoft.tv.widget.home.HomeItemContainer"
    android:clickable="true"
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:clipChildren="false"
    android:clipToPadding="false" >

    <com.eastelsoft.tv.widget.ESImageView
        android:id="@+id/img"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@drawable/holder_nor"
        android:duplicateParentState="true"
        android:scaleType="fitXY" />

    <!-- -->
    <com.eastelsoft.tv.widget.ESImageView
        android:id="@+id/hover"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:contentDescription="@string/desc"
        android:duplicateParentState="true"
        android:scaleType="fitXY"
        android:src=http://www.mamicode.com/"@drawable/sl_image_home_navigator" />>这里定义了一个自定义view,代码在后面放上,每个item里添加了一个img,用于放置内容图片,一个hover,用于显示选中的边框,以及一个text,显示一些文字说明。

hover的src是一个selector drawable,当未focus时,它的背景是tansparent,当focus,放入外框图片。

自定义的HomeItemContainer 代码:

public class HomeItemContainer extends RelativeLayout {
	
	private Animation scaleSmallAnimation;
	private Animation scaleBigAnimation;
	
	public HomeItemContainer(Context context) {
		super(context);
	}

	public HomeItemContainer(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public HomeItemContainer(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
	}
	
	@Override
	protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
		super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
		if (gainFocus) {
			this.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);
	}
	
}

注意onFocusChanged方法,为防止item被其他item遮挡,先调用bringToFront方法,使此item处于最上层,之后调用父view的方法进行重新绘制,其实注意一点,item必须处于同一父view中,否则requestLayout和invalidate可能会不起作用。

顺便放上一个scaleanimation缩小的效果代码:

<?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="200"
        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" />

</set>

里面的属性就不详细介绍了,有兴趣的可以自己谷歌。

最后放上item的父view:

<?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:orientation="horizontal"
    android:padding="10dp"
    android:clipChildren="false"
    android:clipToPadding="false" >

    <include 
        android:id="@+id/channel_0"
        android:layout_width="@dimen/home_channel_item_width"
    	android:layout_height="@dimen/home_channel_item_height"
        layout="@layout/home_page_channel_item"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_margin="3dp" />
    
    <include
        android:id="@+id/channel_1"
        android:layout_width="@dimen/home_channel_item_width"
    	android:layout_height="@dimen/home_channel_item_height"
        layout="@layout/home_page_channel_item"
        android:layout_below="@id/channel_0"
        android:layout_alignLeft="@id/channel_0" />
    
    <include 
        android:id="@+id/channel_2"
        android:layout_width="@dimen/home_channel_item_width"
    	android:layout_height="@dimen/home_channel_item_height"
        layout="@layout/home_page_channel_item"
        android:layout_toRightOf="@id/channel_0"
        android:layout_alignTop="@id/channel_0"
        android:layout_marginRight="3dp"
        android:layout_marginBottom="3dp"/>
    
    <include 
        android:id="@+id/channel_3"
        android:layout_width="@dimen/home_channel_item_width"
    	android:layout_height="@dimen/home_channel_item_height"
        layout="@layout/home_page_channel_item"
        android:layout_alignLeft="@id/channel_2"
        android:layout_below="@id/channel_2"/>
    
    <include 
        android:id="@+id/channel_4"
        android:layout_width="@dimen/home_channel_item_width"
    	android:layout_height="@dimen/home_channel_item_height"
        layout="@layout/home_page_channel_item"
        android:layout_toRightOf="@id/channel_2"
        android:layout_alignTop="@id/channel_2"
        android:layout_marginRight="3dp"
        android:layout_marginBottom="3dp"/>
    
    <include 
        android:id="@+id/channel_5"
        android:layout_width="@dimen/home_channel_item_width"
    	android:layout_height="@dimen/home_channel_item_height"
        layout="@layout/home_page_channel_item"
        android:layout_alignLeft="@id/channel_4"
        android:layout_below="@id/channel_4"/>
    
    <include 
        android:id="@+id/channel_6"
        android:layout_width="@dimen/home_channel_item_width"
    	android:layout_height="@dimen/home_channel_item_height"
        layout="@layout/home_page_channel_item"
        android:layout_toRightOf="@id/channel_4"
        android:layout_alignTop="@id/channel_4"
        android:layout_marginRight="3dp"
        android:layout_marginBottom="3dp"/>
    
    <include 
        android:id="@+id/channel_7"
        android:layout_width="@dimen/home_channel_item_width"
    	android:layout_height="@dimen/home_channel_item_height"
        layout="@layout/home_page_channel_item"
        android:layout_alignLeft="@id/channel_6"
        android:layout_below="@id/channel_6"/>
    
    <include 
        android:id="@+id/channel_8"
        android:layout_width="@dimen/home_channel_item_width"
    	android:layout_height="@dimen/home_channel_item_height"
        layout="@layout/home_page_channel_item"
        android:layout_toRightOf="@id/channel_6"
        android:layout_alignTop="@id/channel_6"
        android:layout_marginRight="3dp"
        android:layout_marginBottom="3dp"/>
    
    <include 
        android:id="@+id/channel_9"
        android:layout_width="@dimen/home_channel_item_width"
    	android:layout_height="@dimen/home_channel_item_height"
        layout="@layout/home_page_channel_item"
        android:layout_alignLeft="@id/channel_8"
        android:layout_below="@id/channel_8"/>

</RelativeLayout>
这里我定义了10个item,注意RelativeLayout的两个属性,clipChildren设置false,让children view可以超出自身所设置的大小,clipToPadding设置为false,让children view可以使用padding 的位置进行绘制,有了这2个属性,item就可以实现放大而不被遮挡了。

好了,焦点特效的教程就说到这里了,有问题可以在评论中反馈。

android tv焦点特效实现浅析