Android TV焦点框动画效果

本文介绍了一种在TV端应用中实现具有动画效果的焦点框的方法。通过自定义控件和动画效果,使得焦点框在不同控件间移动时能够跟随控件的形状和大小变化,提供更佳的用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

[java]  view plain  copy
  1. private void showOnFocusAnimation(View v, float scale)  
  2.     {  
  3.         animEffect.setAttributs(1.0f, scale, 1.0f, scale, 100);  
  4.         Animation anim = animEffect.createAnimation();  
  5.         v.startAnimation(anim);  
  6.         v.bringToFront();  
  7.     }  
  8.   
  9. private void showLoseFocusAnimation(View v, float scale)  
  10.     {  
  11.         animEffect.setAttributs(scale, 1.0f, scale, 1.0f, 100);  
  12.         Animation anim = animEffect.createAnimation();  
  13.         v.startAnimation(anim);  
  14.     }  
自定义焦点框控件类

[java]  view plain  copy
  1. package com.gotech.tv.launcher.view;  
  2.   
  3. import com.gotech.tv.launcher.util.Constant;  
  4. import com.gotech.tv.launcher.util.DensityUtil;  
  5.   
  6. import android.content.Context;  
  7. import android.graphics.Rect;  
  8. import android.util.AttributeSet;  
  9. import android.view.View;  
  10. import android.view.ViewGroup;  
  11. import android.view.animation.DecelerateInterpolator;  
  12.   
  13. public class FlyBorderView extends View  
  14. {  
  15.   
  16.     private View mFocusView;  
  17.     private View mSelectView;  
  18.     private boolean isTvScreen = false;  
  19.   
  20.     public FlyBorderView(Context context)  
  21.     {  
  22.         super(context, null0);  
  23.         init(context);  
  24.     }  
  25.   
  26.     public FlyBorderView(Context context, AttributeSet attrs)  
  27.     {  
  28.         super(context, attrs, 0);  
  29.         init(context);  
  30.     }  
  31.   
  32.     public FlyBorderView(Context context, AttributeSet attrs, int defStyleAttr)  
  33.     {  
  34.         super(context, attrs, defStyleAttr);  
  35.         init(context);  
  36.     }  
  37.   
  38.     private void init(Context context)  
  39.     {  
  40.   
  41.     }  
  42.   
  43.     public boolean isTvScreen()  
  44.     {  
  45.         return isTvScreen;  
  46.     }  
  47.   
  48.     public void setTvScreen(boolean isTvScreen)  
  49.     {  
  50.         this.isTvScreen = isTvScreen;  
  51.         invalidate();  
  52.     }  
  53.   
  54.     /** 
  55.      * 设置焦点框的移动. 
  56.      */  
  57.     public void setFocusView(View view, float scale)  
  58.     {  
  59.         if (mFocusView != view)  
  60.         {  
  61.             mFocusView = view;  
  62.             runTranslateAnimation(mFocusView, scale, scale);  
  63.         }  
  64.     }  
  65.   
  66.     public void setSelectView(View view)  
  67.     {  
  68.         if (mSelectView != view)  
  69.         {  
  70.             mSelectView = view;  
  71.   
  72.             runTranslateAnimation(mSelectView);  
  73.         }  
  74.     }  
  75.   
  76.     private void runTranslateAnimation(View toView)  
  77.     {  
  78.         Rect fromRect = findLocationWithView(this);  
  79.         Rect toRect = findLocationWithView(toView);  
  80.         int x = toRect.left - fromRect.left;  
  81.         int y = toRect.top - fromRect.top;  
  82.   
  83.         int deltaX = (toView.getWidth() - this.getWidth()) / 2;  
  84.         int deltaY = (toView.getHeight() - this.getHeight()) / 2;  
  85.         // tv  
  86.         if (isTvScreen)  
  87.         {  
  88.             x = DensityUtil.dip2px(this.getContext(), x + deltaX);  
  89.             y = DensityUtil.dip2px(this.getContext(), y + deltaY);  
  90.         }  
  91.         else  
  92.         {  
  93.             x = x + deltaX;  
  94.             y = y + deltaY;  
  95.         }  
  96.         flyWhiteBorder(x, y);  
  97.   
  98.     }  
  99.   
  100.     private void flyWhiteBorder(float x, float y)  
  101.     {  
  102.   
  103.         animate().translationX(x).translationY(y).setDuration(Constant.TRAN_DUR_ANIM).setInterpolator(new DecelerateInterpolator()).start();  
  104.   
  105.     }  
  106.   
  107.     public void runTranslateAnimation(View toView, float scaleX, float scaleY)  
  108.     {  
  109.         Rect fromRect = findLocationWithView(this);  
  110.         Rect toRect = findLocationWithView(toView);  
  111.   
  112.         int x = toRect.left - fromRect.left;  
  113.         int y = toRect.top - fromRect.top;  
  114.   
  115.         int deltaX = (toView.getWidth() - this.getWidth()) / 2;  
  116.         int deltaY = (toView.getHeight() - this.getHeight()) / 2;  
  117.         // tv  
  118.         if (isTvScreen)  
  119.         {  
  120.             x = DensityUtil.dip2px(this.getContext(), x + deltaX);  
  121.             y = DensityUtil.dip2px(this.getContext(), y + deltaY);  
  122.         }  
  123.         else  
  124.         {  
  125.             x = x + deltaX;  
  126.             y = y + deltaY;  
  127.         }  
  128.         float toWidth = toView.getWidth() * scaleX;  
  129.         float toHeight = toView.getHeight() * scaleY;  
  130.         int width = (int) (toWidth);  
  131.         int height = (int) (toHeight);  
  132.   
  133.         flyWhiteBorder(width, height, x, y);  
  134.     }  
  135.   
  136.     private void flyWhiteBorder(int width, int height, float x, float y)  
  137.     {  
  138.         int mWidth = this.getWidth();  
  139.         int mHeight = this.getHeight();  
  140.   
  141.         float scaleX = (float) width / (float) mWidth;  
  142.         float scaleY = (float) height / (float) mHeight;  
  143.   
  144.         animate().translationX(x).translationY(y).setDuration(Constant.TRAN_DUR_ANIM).scaleX(scaleX).scaleY(scaleY).setInterpolator(new DecelerateInterpolator()).start();  
  145.     }  
  146.   
  147.     public Rect findLocationWithView(View view)  
  148.     {  
  149.         ViewGroup root = (ViewGroup) this.getParent();  
  150.         Rect rect = new Rect();  
  151.         root.offsetDescendantRectToMyCoords(view, rect);  
  152.         return rect;  
  153.     }  
  154.   
  155. }  
XML布局文件

[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.   
  6.     ...  
  7.     <FrameLayout  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:layout_below="@+id/homeTitle_bar">  
  11.   
  12.         <com.gotech.tv.launcher.view.MainLayout  
  13.             android:id="@+id/main_layout"  
  14.             android:layout_width="match_parent"  
  15.             android:layout_height="wrap_content">  
  16.   
  17.           ...  
  18.         </com.gotech.tv.launcher.view.MainLayout>  
  19.   
  20.         <com.gotech.tv.launcher.view.FlyBorderView  
  21.             android:id="@+id/flyBorder_view"  
  22.             android:layout_width="@dimen/border_width"  
  23.             android:layout_height="@dimen/border_height"  
  24.             android:background="@drawable/item_highlight"  
  25.             android:visibility="invisible"/>  
  26.     </FrameLayout>  
控件事件监听
[java]  view plain  copy
  1. private void initView()  
  2.     {  
  3.         mFlyBorderView = (FlyBorderView) getParentView().findViewById(R.id.flyBorder_view);  
  4.         mMainLayout = (MainLayout) getParentView().findViewById(R.id.main_layout);  
  5.         for (int index = 0; index < mMainLayout.getChildCount(); index++)  
  6.         {  
  7.             mMainLayout.getChildAt(index).setOnFocusChangeListener(this);  
  8.             mMainLayout.getChildAt(index).setOnClickListener(this);  
  9.         }  
  10.   
  11.         reflectImageView();  
  12.   
  13.     }  
控件事件处理

[java]  view plain  copy
  1. @Override  
  2.     public void onFocusChange(View v, boolean hasFocus)  
  3.     {  
  4.         if (hasFocus)  
  5.         {  
  6.   
  7.             mFlyBorderView.setVisibility(View.VISIBLE);  
  8.             mFlyBorderView.setTvScreen(true);  
  9.             if (v.getId() == R.id.frame_tv)  
  10.             {  
  11.                 mFlyBorderView.setFocusView(v, 1.15f);  
  12.                 showOnFocusAnimation(v, 1.15f);  
  13.             }  
  14.             else  
  15.             {  
  16.                 mFlyBorderView.setFocusView(v, 1.20f);  
  17.                 showOnFocusAnimation(v, 1.20f);  
  18.             }  
  19.   
  20.         }  
  21.         else  
  22.         {  
  23.             mFlyBorderView.setVisibility(View.INVISIBLE);  
  24.             if (v.getId() == R.id.frame_tv)  
  25.             {  
  26.                 showLoseFocusAnimation(v, 1.15f);  
  27.             }  
  28.             else  
  29.             {  
  30.                 showLoseFocusAnimation(v, 1.20f);  
  31.             }  
  32.   
  33.         }  
  34.   
  35.     }  

最终效果

http://v.youku.com/v_show/id_XMTQyMTc0ODgxNg==.html

本文出自:http://blog.youkuaiyun.com/johnwcheung/article/details/50388645


项目源代码:https://github.com/coderJohnZhang/TvLauncher

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值