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 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就可以实现放大而不被遮挡了。
好了,焦点特效的教程就说到这里了,有问题可以在评论中反馈。