zjx2014430的专栏

好的要大家一起分享!

android tv焦点特效实现浅析



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

原文出自:http://blog.csdn.net/wzlas111/article/details/39741091



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

首先是item的代码:

  1. <view xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:id="@+id/item"  
  3.     android:layout_width="@dimen/home_channel_item_width"  
  4.     android:layout_height="@dimen/home_channel_item_height"  
  5.     class="com.eastelsoft.tv.widget.home.HomeItemContainer"  
  6.     android:clickable="true"  
  7.     android:focusable="true"  
  8.     android:focusableInTouchMode="true"  
  9.     android:clipChildren="false"  
  10.     android:clipToPadding="false" >  
  11.   
  12.     <com.eastelsoft.tv.widget.ESImageView  
  13.         android:id="@+id/img"  
  14.         android:layout_width="fill_parent"  
  15.         android:layout_height="fill_parent"  
  16.         android:background="@drawable/holder_nor"  
  17.         android:duplicateParentState="true"  
  18.         android:scaleType="fitXY" />  
  19.   
  20.     <!-- -->  
  21.     <com.eastelsoft.tv.widget.ESImageView  
  22.         android:id="@+id/hover"  
  23.         android:layout_width="fill_parent"  
  24.         android:layout_height="fill_parent"  
  25.         android:contentDescription="@string/desc"  
  26.         android:duplicateParentState="true"  
  27.         android:scaleType="fitXY"  
  28.         android:src="@drawable/sl_image_home_navigator" />  
  29.       
  30.     <TextView  
  31.         android:id="@+id/text"  
  32.         android:layout_width="fill_parent"  
  33.         android:layout_height="wrap_content"  
  34.         android:layout_alignParentBottom="true"  
  35.         android:layout_marginBottom="@dimen/home_item_text_margin"  
  36.         android:layout_marginLeft="@dimen/home_item_text_margin"  
  37.         android:layout_marginRight="@dimen/home_item_text_margin"  
  38.         android:ellipsize="marquee"  
  39.         android:gravity="bottom|right|center"  
  40.         android:includeFontPadding="false"  
  41.         android:marqueeRepeatLimit="5"  
  42.         android:maxWidth="@dimen/px310"  
  43.         android:shadowColor="#88333333"  
  44.         android:shadowDx="2.0"  
  45.         android:shadowDy="2.0"  
  46.         android:shadowRadius="2.0"  
  47.         android:singleLine="true"  
  48.         android:textColor="#ffffffff" />  
  49.   
  50. </view>  
<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 代码:

  1. public class HomeItemContainer extends RelativeLayout {  
  2.       
  3.     private Rect mBound;  
  4.     private Drawable mDrawable;  
  5.     private Rect mRect;  
  6.       
  7.     private Animation scaleSmallAnimation;  
  8.     private Animation scaleBigAnimation;  
  9.       
  10.     public HomeItemContainer(Context context) {  
  11.         super(context);  
  12.         init();  
  13.     }  
  14.   
  15.     public HomeItemContainer(Context context, AttributeSet attrs, int defStyle) {  
  16.         super(context, attrs, defStyle);  
  17.         init();  
  18.     }  
  19.   
  20.     public HomeItemContainer(Context context, AttributeSet attrs) {  
  21.         super(context, attrs);  
  22.         init();  
  23.     }  
  24.       
  25.     protected void init() {  
  26.         setWillNotDraw(false);  
  27.         mRect = new Rect();  
  28.         mBound = new Rect();  
  29.         mDrawable = getResources().getDrawable(R.drawable.poster_shadow_4);//nav_focused_2,poster_shadow_4  
  30.         setChildrenDrawingOrderEnabled(true);  
  31.     }  
  32.   
  33.     @Override  
  34.     protected void onAttachedToWindow() {  
  35.         super.onAttachedToWindow();  
  36.     }  
  37.       
  38.     @Override  
  39.     public void draw(Canvas canvas) {  
  40.         super.draw(canvas);  
  41.     }  
  42.       
  43.     @Override  
  44.     protected void onDraw(Canvas canvas) {  
  45.         if (hasFocus()) {  
  46.             System.out.println("HomeItemContainer focus : true ");  
  47.             super.getDrawingRect(mRect);  
  48.             mBound.set(-39+mRect.left, -39+mRect.top, 39+mRect.right, 39+mRect.bottom);  
  49.             mDrawable.setBounds(mBound);  
  50.             canvas.save();  
  51.             mDrawable.draw(canvas);  
  52.             canvas.restore();  
  53.         }  
  54.         super.onDraw(canvas);  
  55.     }  
  56.       
  57.     @Override  
  58.     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {  
  59.         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);  
  60.         if (gainFocus) {  
  61.             bringToFront();  
  62.             getRootView().requestLayout();  
  63.             getRootView().invalidate();  
  64.             zoomOut();  
  65.         } else {  
  66.             zoomIn();  
  67.         }  
  68.     }  
  69.       
  70.     private void zoomIn() {  
  71.         if (scaleSmallAnimation == null) {  
  72.             scaleSmallAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.anim_scale_small);  
  73.         }  
  74.         startAnimation(scaleSmallAnimation);  
  75.     }  
  76.       
  77.     private void zoomOut() {  
  78.         if (scaleBigAnimation == null) {  
  79.             scaleBigAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.anim_scale_big);  
  80.         }  
  81.         startAnimation(scaleBigAnimation);  
  82.     }  
  83.       
  84. }  
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缩小的效果代码:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <set xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:fillAfter="false"  
  4.     android:fillBefore="true"  
  5.     android:shareInterpolator="false" >  
  6.   
  7.     <scale  
  8.         android:duration="200"  
  9.         android:fromXScale="1.1"  
  10.         android:fromYScale="1.1"  
  11.         android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
  12.         android:pivotX="50.0%"  
  13.         android:pivotY="50.0%"  
  14.         android:repeatCount="0"  
  15.         android:toXScale="1.0"  
  16.         android:toYScale="1.0" />  
  17.   
  18. </set>  
<?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:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="horizontal"  
  6.     android:padding="10dp"  
  7.     android:clipChildren="false"  
  8.     android:clipToPadding="false" >  
  9.   
  10.     <include   
  11.         android:id="@+id/channel_0"  
  12.         android:layout_width="@dimen/home_channel_item_width"  
  13.         android:layout_height="@dimen/home_channel_item_height"  
  14.         layout="@layout/home_page_channel_item"  
  15.         android:layout_alignParentLeft="true"  
  16.         android:layout_alignParentTop="true"  
  17.         android:layout_margin="3dp" />  
  18.       
  19.     <include  
  20.         android:id="@+id/channel_1"  
  21.         android:layout_width="@dimen/home_channel_item_width"  
  22.         android:layout_height="@dimen/home_channel_item_height"  
  23.         layout="@layout/home_page_channel_item"  
  24.         android:layout_below="@id/channel_0"  
  25.         android:layout_alignLeft="@id/channel_0" />  
  26.       
  27.     <include   
  28.         android:id="@+id/channel_2"  
  29.         android:layout_width="@dimen/home_channel_item_width"  
  30.         android:layout_height="@dimen/home_channel_item_height"  
  31.         layout="@layout/home_page_channel_item"  
  32.         android:layout_toRightOf="@id/channel_0"  
  33.         android:layout_alignTop="@id/channel_0"  
  34.         android:layout_marginRight="3dp"  
  35.         android:layout_marginBottom="3dp"/>  
  36.       
  37.     <include   
  38.         android:id="@+id/channel_3"  
  39.         android:layout_width="@dimen/home_channel_item_width"  
  40.         android:layout_height="@dimen/home_channel_item_height"  
  41.         layout="@layout/home_page_channel_item"  
  42.         android:layout_alignLeft="@id/channel_2"  
  43.         android:layout_below="@id/channel_2"/>  
  44.       
  45.     <include   
  46.         android:id="@+id/channel_4"  
  47.         android:layout_width="@dimen/home_channel_item_width"  
  48.         android:layout_height="@dimen/home_channel_item_height"  
  49.         layout="@layout/home_page_channel_item"  
  50.         android:layout_toRightOf="@id/channel_2"  
  51.         android:layout_alignTop="@id/channel_2"  
  52.         android:layout_marginRight="3dp"  
  53.         android:layout_marginBottom="3dp"/>  
  54.       
  55.     <include   
  56.         android:id="@+id/channel_5"  
  57.         android:layout_width="@dimen/home_channel_item_width"  
  58.         android:layout_height="@dimen/home_channel_item_height"  
  59.         layout="@layout/home_page_channel_item"  
  60.         android:layout_alignLeft="@id/channel_4"  
  61.         android:layout_below="@id/channel_4"/>  
  62.       
  63.     <include   
  64.         android:id="@+id/channel_6"  
  65.         android:layout_width="@dimen/home_channel_item_width"  
  66.         android:layout_height="@dimen/home_channel_item_height"  
  67.         layout="@layout/home_page_channel_item"  
  68.         android:layout_toRightOf="@id/channel_4"  
  69.         android:layout_alignTop="@id/channel_4"  
  70.         android:layout_marginRight="3dp"  
  71.         android:layout_marginBottom="3dp"/>  
  72.       
  73.     <include   
  74.         android:id="@+id/channel_7"  
  75.         android:layout_width="@dimen/home_channel_item_width"  
  76.         android:layout_height="@dimen/home_channel_item_height"  
  77.         layout="@layout/home_page_channel_item"  
  78.         android:layout_alignLeft="@id/channel_6"  
  79.         android:layout_below="@id/channel_6"/>  
  80.       
  81.     <include   
  82.         android:id="@+id/channel_8"  
  83.         android:layout_width="@dimen/home_channel_item_width"  
  84.         android:layout_height="@dimen/home_channel_item_height"  
  85.         layout="@layout/home_page_channel_item"  
  86.         android:layout_toRightOf="@id/channel_6"  
  87.         android:layout_alignTop="@id/channel_6"  
  88.         android:layout_marginRight="3dp"  
  89.         android:layout_marginBottom="3dp"/>  
  90.       
  91.     <include   
  92.         android:id="@+id/channel_9"  
  93.         android:layout_width="@dimen/home_channel_item_width"  
  94.         android:layout_height="@dimen/home_channel_item_height"  
  95.         layout="@layout/home_page_channel_item"  
  96.         android:layout_alignLeft="@id/channel_8"  
  97.         android:layout_below="@id/channel_8"/>  
  98.   
  99. </RelativeLayout>  
<?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内存泄漏分析及调试
下一篇Android调用JNI方法 及 代码
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭