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="@drawable/sl_image_home_navigator" />
    
    <TextView
        android:id="@+id/text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="@dimen/home_item_text_margin"
        android:layout_marginLeft="@dimen/home_item_text_margin"
        android:layout_marginRight="@dimen/home_item_text_margin"
        android:ellipsize="marquee"
        android:gravity="bottom|right|center"
        android:includeFontPadding="false"
        android:marqueeRepeatLimit="5"
        android:maxWidth="@dimen/px310"
        android:shadowColor="#88333333"
        android:shadowDx="2.0"
        android:shadowDy="2.0"
        android:shadowRadius="2.0"
        android:singleLine="true"
        android:textColor="#ffffffff" />

</view>
這裏定義了一個自定義view,代碼在後面放上,每個item裏添加了一個img,用於放置內容圖片,一個hover,用於顯示選中的邊框,以及一個text,顯示一些文字說明。

hover的src是一個selector drawable,當未focus時,它的背景是tansparent,當focus,放入外框圖片。

自定義的HomeItemContainer 代碼:

public class HomeItemContainer extends RelativeLayout {
	
	private Rect mBound;
	private Drawable mDrawable;
	private Rect mRect;
	
	private Animation scaleSmallAnimation;
	private Animation scaleBigAnimation;
	
	public HomeItemContainer(Context context) {
		super(context);
		init();
	}

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

	public HomeItemContainer(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);//nav_focused_2,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()) {
			System.out.println("HomeItemContainer focus : true ");
			super.getDrawingRect(mRect);
			mBound.set(-39+mRect.left, -39+mRect.top, 39+mRect.right, 39+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);
	}
	
}



注意onFocusChanged方法,爲防止item被其他item遮擋,先調用bringToFront方法,使此item處於最上層,之後調用父view的方法進行重新繪製,其實注意一點,item必須處於同一父view中,否則requestLayout和invalidate可能會不起作用,只適用於RelativeLayout佈局,經測試LinearLayout不適用。

順便放上一個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就可以實現放大而不被遮擋了。

好了,焦點特效的教程就說到這裏了,有問題可以在評論中反饋。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章