android tv焦点放大特效实现及RecyclerView使用

如下是tv焦点放大大致分析,具体实现已优化,请查看demo:https://github.com/lewic1987/tvdemo.git


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

android tv焦点放大特效实现及RecyclerView使用_第1张图片


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

首先是item的代码:

[html]  view plain copy
  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,代码在后面放上,每个item里添加了一个img,用于放置内容图片,一个hover,用于显示选中的边框,以及一个text,显示一些文字说明。

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

自定义的HomeItemContainer 代码:

[java]  view plain copy
  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. }  



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

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

[html]  view plain copy
  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>  

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

最后放上item的父view:

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

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

你可能感兴趣的:(android,tv)