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就可以實現放大而不被遮擋了。
好了,焦點特效的教程就說到這裏了,有問題可以在評論中反饋。