您的位置:首页 > 移动开发 > Android开发

Android TV焦点框动画效果

2015-12-23 17:33 627 查看
背景意义

对于TV端来说,各种Android事件的处理,依赖于遥控操作,不像移动终端可以通过Touch主观感觉焦点存在位置,TV焦点需要通过图像显示出来.
因此焦点框显示效果非常影响用户体验,一般焦点效果常见的是控件背景加高亮框,或通过触发事件切换背景,亦或伸缩控件大小.实际上,我们可以实现具有动画效果的焦点框.
实现的动画效果为:使用平移动画绘制焦点框移动轨迹,同时焦点框随着控件形状动态改变.动画最终状态是,焦点框从失去焦点的位置移动到获得焦点的位置,控件放大,焦点框尺寸最后变为放大后的控件尺寸.

控件自身获得或失去焦点伸缩效果实现函数

private void showOnFocusAnimation(View v, float scale)
{
animEffect.setAttributs(1.0f, scale, 1.0f, scale, 100);
Animation anim = animEffect.createAnimation();
v.startAnimation(anim);
v.bringToFront();
}

private void showLoseFocusAnimation(View v, float scale)
{
animEffect.setAttributs(scale, 1.0f, scale, 1.0f, 100);
Animation anim = animEffect.createAnimation();
v.startAnimation(anim);
}
自定义焦点框控件类

package com.gotech.tv.launcher.view;

import com.gotech.tv.launcher.util.Constant;
import com.gotech.tv.launcher.util.DensityUtil;

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;

public class FlyBorderView extends View
{

private View mFocusView;
private View mSelectView;
private boolean isTvScreen = false;

public FlyBorderView(Context context)
{
super(context, null, 0);
init(context);
}

public FlyBorderView(Context context, AttributeSet attrs)
{
super(context, attrs, 0);
init(context);
}

public FlyBorderView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
init(context);
}

private void init(Context context)
{

}

public boolean isTvScreen()
{
return isTvScreen;
}

public void setTvScreen(boolean isTvScreen)
{
this.isTvScreen = isTvScreen;
invalidate();
}

/**
* 设置焦点框的移动.
*/
public void setFocusView(View view, float scale)
{
if (mFocusView != view)
{
mFocusView = view;
runTranslateAnimation(mFocusView, scale, scale);
}
}

public void setSelectView(View view)
{
if (mSelectView != view)
{
mSelectView = view;

runTranslateAnimation(mSelectView);
}
}

private void runTranslateAnimation(View toView)
{
Rect fromRect = findLocationWithView(this);
Rect toRect = findLocationWithView(toView);
int x = toRect.left - fromRect.left;
int y = toRect.top - fromRect.top;

int deltaX = (toView.getWidth() - this.getWidth()) / 2;
int deltaY = (toView.getHeight() - this.getHeight()) / 2;
// tv
if (isTvScreen)
{
x = DensityUtil.dip2px(this.getContext(), x + deltaX);
y = DensityUtil.dip2px(this.getContext(), y + deltaY);
}
else
{
x = x + deltaX;
y = y + deltaY;
}
flyWhiteBorder(x, y);

}

private void flyWhiteBorder(float x, float y)
{

animate().translationX(x).translationY(y).setDuration(Constant.TRAN_DUR_ANIM).setInterpolator(new DecelerateInterpolator()).start();

}

public void runTranslateAnimation(View toView, float scaleX, float scaleY)
{
Rect fromRect = findLocationWithView(this);
Rect toRect = findLocationWithView(toView);

int x = toRect.left - fromRect.left;
int y = toRect.top - fromRect.top;

int deltaX = (toView.getWidth() - this.getWidth()) / 2;
int deltaY = (toView.getHeight() - this.getHeight()) / 2;
// tv
if (isTvScreen)
{
x = DensityUtil.dip2px(this.getContext(), x + deltaX);
y = DensityUtil.dip2px(this.getContext(), y + deltaY);
}
else
{
x = x + deltaX;
y = y + deltaY;
}
float toWidth = toView.getWidth() * scaleX;
float toHeight = toView.getHeight() * scaleY;
int width = (int) (toWidth);
int height = (int) (toHeight);

flyWhiteBorder(width, height, x, y);
}

private void flyWhiteBorder(int width, int height, float x, float y)
{
int mWidth = this.getWidth();
int mHeight = this.getHeight();

float scaleX = (float) width / (float) mWidth;
float scaleY = (float) height / (float) mHeight;

animate().translationX(x).translationY(y).setDuration(Constant.TRAN_DUR_ANIM).scaleX(scaleX).scaleY(scaleY).setInterpolator(new DecelerateInterpolator()).start();
}

public Rect findLocationWithView(View view)
{
ViewGroup root = (ViewGroup) this.getParent();
Rect rect = new Rect();
root.offsetDescendantRectToMyCoords(view, rect);
return rect;
}

}
XML布局文件

<?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">

...
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/homeTitle_bar">

<com.gotech.tv.launcher.view.MainLayout
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">

...
</com.gotech.tv.launcher.view.MainLayout>

<com.gotech.tv.launcher.view.FlyBorderView
android:id="@+id/flyBorder_view"
android:layout_width="@dimen/border_width"
android:layout_height="@dimen/border_height"
android:background="@drawable/item_highlight"
android:visibility="invisible"/>
</FrameLayout>
控件事件监听

private void initView()
{
mFlyBorderView = (FlyBorderView) getParentView().findViewById(R.id.flyBorder_view);
mMainLayout = (MainLayout) getParentView().findViewById(R.id.main_layout);
for (int index = 0; index < mMainLayout.getChildCount(); index++)
{
mMainLayout.getChildAt(index).setOnFocusChangeListener(this);
mMainLayout.getChildAt(index).setOnClickListener(this);
}

reflectImageView();

}
控件事件处理

@Override
public void onFocusChange(View v, boolean hasFocus)
{
if (hasFocus)
{

mFlyBorderView.setVisibility(View.VISIBLE);
mFlyBorderView.setTvScreen(true);
if (v.getId() == R.id.frame_tv)
{
mFlyBorderView.setFocusView(v, 1.15f);
showOnFocusAnimation(v, 1.15f);
}
else
{
mFlyBorderView.setFocusView(v, 1.20f);
showOnFocusAnimation(v, 1.20f);
}

}
else
{
mFlyBorderView.setVisibility(View.INVISIBLE);
if (v.getId() == R.id.frame_tv)
{
showLoseFocusAnimation(v, 1.15f);
}
else
{
showLoseFocusAnimation(v, 1.20f);
}

}

}

最终效果

http://v.youku.com/v_show/id_XMTQyMTc0ODgxNg==.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: