背景意义
自定义焦点框控件类
XML布局文件
控件事件监听
控件事件处理
- 对于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 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
本文出自:http://blog.youkuaiyun.com/johnwcheung/article/details/50388645