├── README.md
├── res
├── raw
│ └── himi_ogg.ogg
├── drawable
│ ├── bound.png
│ ├── bound2.png
│ ├── focus_bound.png
│ ├── white_border.png
│ └── white_border1.9.png
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ └── ic_launcher.png
├── drawable-xxhdpi
│ └── ic_launcher.png
├── values-sw600dp
│ └── dimens.xml
├── values
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── menu
│ └── main.xml
├── values-sw720dp-land
│ └── dimens.xml
├── values-v11
│ └── styles.xml
├── anim
│ ├── box_normal.xml
│ └── box_alpha.xml
├── values-v14
│ └── styles.xml
└── layout
│ ├── grid_item.xml
│ └── activity_main.xml
├── libs
├── android-support-v4.jar
└── nineoldandroids-2.4.0.jar
├── project.properties
├── AndroidManifest.xml
└── src
└── com
└── example
└── borderviewdemo
├── Utils
├── AnimUtils.java
└── DensityUtil.java
├── View
├── VerticalSmoothGridView.java
├── FocusBorderView.java
├── BorderView.java
└── CopyOfCopyOfFocusBorderView.java
└── MainActivity.java
/README.md:
--------------------------------------------------------------------------------
1 | BorderViewDemo
2 | ==============
3 |
--------------------------------------------------------------------------------
/res/raw/himi_ogg.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lf8289/BorderViewDemo/HEAD/res/raw/himi_ogg.ogg
--------------------------------------------------------------------------------
/res/drawable/bound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lf8289/BorderViewDemo/HEAD/res/drawable/bound.png
--------------------------------------------------------------------------------
/res/drawable/bound2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lf8289/BorderViewDemo/HEAD/res/drawable/bound2.png
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lf8289/BorderViewDemo/HEAD/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/res/drawable/focus_bound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lf8289/BorderViewDemo/HEAD/res/drawable/focus_bound.png
--------------------------------------------------------------------------------
/libs/nineoldandroids-2.4.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lf8289/BorderViewDemo/HEAD/libs/nineoldandroids-2.4.0.jar
--------------------------------------------------------------------------------
/res/drawable/white_border.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lf8289/BorderViewDemo/HEAD/res/drawable/white_border.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lf8289/BorderViewDemo/HEAD/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lf8289/BorderViewDemo/HEAD/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable/white_border1.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lf8289/BorderViewDemo/HEAD/res/drawable/white_border1.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lf8289/BorderViewDemo/HEAD/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lf8289/BorderViewDemo/HEAD/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TvBorderViewDemo
5 | Settings
6 | Hello world!
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/res/values-sw720dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 128dp
8 |
9 |
10 |
--------------------------------------------------------------------------------
/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/res/anim/box_normal.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/res/anim/box_alpha.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
16 |
17 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/res/layout/grid_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
15 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/com/example/borderviewdemo/Utils/AnimUtils.java:
--------------------------------------------------------------------------------
1 | package com.example.borderviewdemo.Utils;
2 |
3 | import com.example.tvborderviewdemo.R;
4 |
5 | import android.content.Context;
6 | //import android.util.Log;
7 | import android.view.animation.AlphaAnimation;
8 | import android.view.animation.Animation;
9 | import android.view.animation.AnimationSet;
10 | import android.view.animation.ScaleAnimation;
11 |
12 | public class AnimUtils {
13 |
14 | private static final String TAG = "AnimUtils";
15 | private static Animation mBoxAnimNormal;
16 |
17 | public static Animation buildAnimBoxNormal(Context context) {
18 | if (mBoxAnimNormal != null) {
19 | return mBoxAnimNormal;
20 | }
21 | mBoxAnimNormal = android.view.animation.AnimationUtils.loadAnimation(
22 | context, R.anim.box_alpha);
23 | return mBoxAnimNormal;
24 | }
25 |
26 | public static AnimationSet buildAnimBoxClick(Context context) {
27 | final ScaleAnimation scale = new ScaleAnimation(0.5f, 1.3f, 0.5f, 1.3f,
28 | Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
29 | 0.5f);
30 | AlphaAnimation alpha = new AlphaAnimation(0.5f, 1.0f);
31 | AnimationSet mBoxAnimClick = new AnimationSet(true);
32 | mBoxAnimClick.addAnimation(scale);
33 | mBoxAnimClick.addAnimation(alpha);
34 | mBoxAnimClick.setDuration(100);
35 | return mBoxAnimClick;
36 | }
37 | }
--------------------------------------------------------------------------------
/src/com/example/borderviewdemo/Utils/DensityUtil.java:
--------------------------------------------------------------------------------
1 | package com.example.borderviewdemo.Utils;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 |
6 | /**
7 | * 分辨率转换类
8 | *
9 | * @author 李小斌 364643658@qq.com
10 | * */
11 | public class DensityUtil {
12 |
13 | // int screenWidth = getWindowManager().getDefaultDisplay().getWidth();
14 | // // 屏幕宽(像素,如:480px)
15 | // int screenHeight =
16 | // getWindowManager().getDefaultDisplay().getHeight(); // 屏幕高(像素,如:800p)
17 | // int xDip = DensityUtil.px2dip(SettingActivity.this, (float)
18 | // (screenWidth * 1.0));
19 | // int yDip = DensityUtil.px2dip(SettingActivity.this, (float)
20 | // (screenHeight * 1.0));
21 |
22 | public static int getScreenHeight(Activity activity) {
23 | return activity.getWindowManager().getDefaultDisplay().getHeight();
24 | }
25 |
26 | public static int getScreenWidth(Activity activity) {
27 | return activity.getWindowManager().getDefaultDisplay().getWidth();
28 | }
29 |
30 | /**
31 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
32 | */
33 | public static int dip2px(Context context, float dpValue) {
34 | final float scale = context.getResources().getDisplayMetrics().density;
35 | return (int) (dpValue * scale + 0.5f);
36 | }
37 |
38 | /**
39 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
40 | */
41 | public static int px2dip(Context context, float pxValue) {
42 | final float scale = context.getResources().getDisplayMetrics().density;
43 | return (int) (pxValue / scale + 0.5f);
44 | }
45 | }
--------------------------------------------------------------------------------
/src/com/example/borderviewdemo/View/VerticalSmoothGridView.java:
--------------------------------------------------------------------------------
1 | package com.example.borderviewdemo.View;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.util.AttributeSet;
6 | import android.util.Log;
7 | import android.view.KeyEvent;
8 | import android.widget.GridView;
9 |
10 | public class VerticalSmoothGridView extends GridView {
11 |
12 | private final static int SCROLL_ITEM_TIME = 1500;
13 | private int eventCount = 0;
14 | private final static int DOUBLE_ROW = 2; // "2"在双数行
15 | private final static int SINGLE_ROW = 1; // "1"在单行
16 |
17 | /**
18 | * <默认构造函数>
19 | */
20 | public VerticalSmoothGridView(Context context) {
21 | super(context);
22 | }
23 |
24 | public VerticalSmoothGridView(Context context, AttributeSet attrs,
25 | int defStyle) {
26 | super(context, attrs, defStyle);
27 | }
28 |
29 | public VerticalSmoothGridView(Context context, AttributeSet attrs) {
30 | super(context, attrs);
31 | }
32 |
33 | @Override
34 | public boolean dispatchKeyEvent(KeyEvent event) {
35 |
36 | int height = this.getChildAt(1).getHeight();
37 | eventCount++;
38 | // 该eventCount%2 是为了取的按键的第一次,因为对不同的item,它会执行两次
39 | if (eventCount % 2 != 0) {
40 | int row = 0;
41 | row = getItemCurrentRow();
42 | if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN
43 | && row == DOUBLE_ROW) {
44 | this.smoothScrollBy(height, SCROLL_ITEM_TIME);
45 | Log.d("", "向下..滑动执行了");
46 | } else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP
47 | && row == SINGLE_ROW) {
48 | this.smoothScrollBy(-height, SCROLL_ITEM_TIME);
49 | Log.d("", "向上..滑动执行了");
50 | }
51 | }
52 | return super.dispatchKeyEvent(event);
53 | }
54 |
55 | /**
56 | *
57 | * 获取Gridview中当前item所在的行
58 | *
59 | * @return
60 | */
61 | @SuppressLint("NewApi")
62 | public int getItemCurrentRow() {
63 | int row = 0;
64 | int position = 0;
65 | position = this.getSelectedItemPosition();
66 | Log.d("", "dispatchKeyEvent..position = " + position);
67 | Log.d("", "this.getNumColumns() = " + this.getNumColumns());
68 | int temp = (position / this.getNumColumns() + 1) % 2;
69 | if (temp == 0) {
70 | row = DOUBLE_ROW;
71 | Log.d("", "在双数行");
72 | } else {
73 | row = SINGLE_ROW;
74 | Log.d("", "在单行");
75 | }
76 | return row;
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
33 |
34 |
38 |
39 |
46 |
47 |
56 |
57 |
65 |
66 |
74 |
75 |
--------------------------------------------------------------------------------
/src/com/example/borderviewdemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.borderviewdemo;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 |
6 | import com.example.borderviewdemo.View.FocusBorderView;
7 | import com.example.borderviewdemo.View.VerticalSmoothGridView;
8 | import com.example.tvborderviewdemo.R;
9 |
10 | import android.app.Activity;
11 | import android.os.Bundle;
12 | import android.os.Handler;
13 | import android.util.Log;
14 | import android.view.View;
15 | import android.view.View.OnClickListener;
16 | import android.view.View.OnFocusChangeListener;
17 | import android.widget.ArrayAdapter;
18 | import android.widget.Button;
19 | import android.widget.GridView;
20 | import android.widget.SimpleAdapter;
21 |
22 | /* import com.bestv.setting.utils.BoxNotFoundException;
23 | import com.bestv.setting.views.FocusBorderView; */
24 |
25 | public class MainActivity extends Activity implements OnFocusChangeListener {
26 |
27 | private static final String TAG_base = "BaseActivity";
28 | FocusBorderView mBorderView;
29 | Handler mHandler = new Handler();
30 |
31 | Button[] btns = new Button[3];
32 |
33 | @Override
34 | protected void onCreate(Bundle savedInstanceState) {
35 | super.onCreate(savedInstanceState);
36 | setContentView(R.layout.activity_main);
37 |
38 | btns[0] = (Button) findViewById(R.id.button1);
39 | btns[1] = (Button) findViewById(R.id.button2);
40 | btns[2] = (Button) findViewById(R.id.button3);
41 | }
42 |
43 | @Override
44 | protected void onStart() {
45 | super.onStart();
46 | FocusBorderView view = (FocusBorderView) findViewById(R.id.box);
47 | if (view == null) {
48 | // throw new BoxNotFoundException();// 必须在父布局中焦点框控件,否则抛出异常
49 | }
50 | mBorderView = view;
51 |
52 | for (Button btn : btns) {
53 | btn.setOnFocusChangeListener(this);
54 | }
55 | }
56 |
57 | public FocusBorderView getBorderView() {
58 | return mBorderView;
59 | }
60 |
61 | public void setBorderView(FocusBorderView box) {
62 | this.mBorderView = box;
63 | }
64 |
65 | public void setFocusedView(final View view, int delay) {
66 | if (mBorderView == null) {
67 | mBorderView = (FocusBorderView) findViewById(R.id.box);
68 | }
69 | //// mBorderView.runBorderAnimation();
70 | // mHandler.postDelayed(new Runnable() {
71 | //
72 | // @Override
73 | // public void run() {
74 | // if (view == null) {
75 | // return;
76 | // }
77 | // view.requestFocus();
78 | // mBorderView.setLocation(view);
79 | // }
80 | // }, delay);
81 | }
82 |
83 | public void runClickAnim() {
84 | // this.getBorderView().notifyClickBoxAnim();
85 | }
86 |
87 | @Override
88 | protected void onResume() {
89 | super.onResume();
90 | }
91 |
92 | @Override
93 | public void onFocusChange(View v, boolean hasFocus) {
94 | if (hasFocus) {
95 | mBorderView.runTranslateAnimation(v);
96 | }
97 | }
98 |
99 | public void setClickListener(View v, OnClickListener listener) {
100 | v.setOnClickListener(listener);
101 | v.setOnFocusChangeListener(this);
102 | }
103 |
104 | }
105 |
106 | //public class MainActivity extends Activity {
107 | // private String[] item = { "唐僧", "孙悟空 ", "猪八戒", "沙和尚", "唐僧", "孙悟空 ", "猪八戒",
108 | // "沙和尚", "唐僧", "孙悟空 ", "猪八戒", "沙和尚", "唐僧", "孙悟空 ", "猪八戒", "沙和尚",
109 | // "唐僧", "孙悟空 ", "猪八戒", "沙和尚", "唐僧", "孙悟空 ", "猪八戒", "沙和尚", "唐僧",
110 | // "孙悟空 ", "猪八戒", "沙和尚", "唐僧", "孙悟空 ", "猪八戒", "沙和尚", "唐僧", "孙悟空 ",
111 | // "猪八戒", "沙和尚", "唐僧", "孙悟空 ", "猪八戒", "沙和尚", "唐僧", "孙悟空 ", "猪八戒",
112 | // "沙和尚", "唐僧", "孙悟空 ", "猪八戒", "沙和尚", "唐僧", "孙悟空 ", "猪八戒", "沙和尚",
113 | // "唐僧", "孙悟空 ", "猪八戒", "沙和尚", "唐僧", "孙悟空 ", "猪八戒", "沙和尚", "唐僧",
114 | // "孙悟空 ", "猪八戒", "沙和尚" };
115 | // private SimpleAdapter adapterSimple;
116 | // private VerticalSmoothGridView gridView;
117 | //
118 | // @Override
119 | // public void onCreate(Bundle savedInstanceState) {
120 | // super.onCreate(savedInstanceState);
121 | // setContentView(R.layout.activity_main);
122 | // gridView = (VerticalSmoothGridView) findViewById(R.id.gridView1);
123 | // // 创建一个ArrayList列表,内部存的是HashMap列表
124 | // ArrayList> listItems = new ArrayList>();
125 | // // 将数组信息分别存入ArrayList中
126 | // int len = item.length;
127 | // for (int i = 0; i < len; i++) {
128 | // HashMap map = new HashMap();
129 | // map.put("image", R.drawable.ic_launcher);
130 | // map.put("item", item[i]);
131 | // listItems.add(map);
132 | // }
133 | //
134 | // // HashMap中的Key信息,要与grid_item.xml中的信息作对应
135 | // String[] from = { "image", "item" };
136 | // // grid_item.xml中对应的ImageView控件和TextView控件
137 | // int[] to = { R.id.item_imageView, R.id.item_textView };
138 | // // 设定一个适配器
139 | // adapterSimple = new SimpleAdapter(this, listItems, R.layout.grid_item,
140 | // from, to);
141 | //
142 | // // 对GridView进行适配
143 | // gridView.setAdapter(adapterSimple);
144 | // }
145 | //}
146 |
--------------------------------------------------------------------------------
/src/com/example/borderviewdemo/View/FocusBorderView.java:
--------------------------------------------------------------------------------
1 | package com.example.borderviewdemo.View;
2 |
3 | import com.example.borderviewdemo.Utils.DensityUtil;
4 | import com.example.tvborderviewdemo.R;
5 |
6 | import android.animation.Animator;
7 | import android.annotation.SuppressLint;
8 | import android.content.Context;
9 | import android.graphics.Rect;
10 | import android.util.AttributeSet;
11 | import android.util.Log;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.view.animation.DecelerateInterpolator;
15 | import android.widget.ImageView;
16 |
17 | public class FocusBorderView extends ImageView {
18 | private static int X_BORDER_SIZE = 0;
19 | private static int Y_BORDER_SIZE = 0;
20 | private static int TRAN_DUR_ANIM = 250;
21 |
22 | // private AnimationDrawable mBoxBgAnim;
23 | // private static Animation mBoxAnimNormal;
24 |
25 | public FocusBorderView(Context context, AttributeSet attrs) {
26 | super(context, attrs);
27 |
28 | this.setBackgroundResource(R.drawable.focus_bound);
29 | this.setVisibility(View.INVISIBLE);
30 | // this.setBackgroundResource(R.anim.box_normal);
31 | // mBoxAnimNormal = android.view.animation.AnimationUtils.loadAnimation(
32 | // context, R.anim.box_alpha);
33 | // mBoxBgAnim = (AnimationDrawable) this.getBackground();
34 | }
35 |
36 | public void runTranslateAnimation(View toView) {
37 | runTranslateAnimation(toView, 1.0F, 1.0F);
38 | }
39 |
40 | public void runTranslateAnimation(View toView, float scaleX, float scaleY) {
41 | /* ViewGroup root = (ViewGroup) this.getParent();
42 | Rect fromRect = new Rect();
43 | Rect toRect = new Rect();
44 |
45 | root.offsetDescendantRectToMyCoords(this, fromRect);
46 | root.offsetDescendantRectToMyCoords(toView, toRect);
47 | int x = toRect.left - fromRect.left;
48 | int y = toRect.top - fromRect.top;*/
49 |
50 | /*
51 | * int x = toView.getLeft() - this.getLeft(); int y = toView.getTop() -
52 | * this.getTop();
53 | */
54 |
55 | Rect fromRect = findLocationWithView(this);
56 | Rect toRect = findLocationWithView(toView);
57 |
58 | int x = toRect.left - fromRect.left;
59 | int y = toRect.top - fromRect.top;
60 |
61 | int deltaX = (toView.getWidth() - this.getWidth()) / 2;
62 | int deltaY = (toView.getHeight() - this.getHeight()) / 2;
63 | x = DensityUtil.dip2px(this.getContext(), x + deltaX);
64 | y = DensityUtil.dip2px(this.getContext(), y + deltaY);
65 |
66 | float toWidth = toView.getWidth() * scaleX;
67 | float toHeight = toView.getHeight() * scaleY;
68 | Log.d("LIF",
69 | "width = " + toView.getWidth() + ", height = "
70 | + toView.getHeight());
71 | float targetScaleX = (float) toWidth
72 | / (float) (this.getWidth() - 2 * X_BORDER_SIZE);
73 | float targetScaleY = (float) toHeight
74 | / (float) (this.getHeight() - 2 * Y_BORDER_SIZE);
75 | int width = (int) (toWidth + 2 * X_BORDER_SIZE * targetScaleX);
76 | int height = (int) (toHeight + 2 * Y_BORDER_SIZE * targetScaleY);
77 |
78 | flyWhiteBorder(width, height, x, y);
79 | }
80 |
81 | /**
82 | * 白色焦点框飞动、移动、变大
83 | *
84 | * @param width
85 | * 白色框的宽(非放大后的)
86 | * @param height
87 | * 白色框的高(非放大后的)
88 | * @param paramFloat1
89 | * x坐标偏移量,相对于初始的白色框的中心点
90 | * @param paramFloat2
91 | * y坐标偏移量,相对于初始的白色框的中心点
92 | * */
93 | @SuppressLint("NewApi")
94 | private void flyWhiteBorder(int width, int height, float x, float y) {
95 | int mWidth = this.getWidth();
96 | int mHeight = this.getHeight();
97 |
98 | float scaleX = (float) width / (float) mWidth;
99 | float scaleY = (float) height / (float) mHeight;
100 |
101 | Log.d("LIF", "mWidth = " + mWidth + ", mHeight = " + mHeight);
102 | Log.d("LIF", "width = " + width + ", height = " + height);
103 | Log.d("LIF", "x = " + x + ", y = " + y);
104 | Log.d("LIF", "scaleX = " + scaleX + ", scaleY = " + scaleY);
105 |
106 | animate().translationX(x).translationY(y).setDuration(TRAN_DUR_ANIM)
107 | .scaleX(scaleX).scaleY(scaleY)
108 | .setInterpolator(new DecelerateInterpolator())
109 | .setListener(flyListener).start();
110 | }
111 |
112 | /**
113 | * 获取View的位置
114 | *
115 | * @param view
116 | * 获取的控件
117 | * @return 位置
118 | */
119 | public Rect findLocationWithView(View view) {
120 | /*
121 | * int[] location = new int[2]; view.getLocationOnScreen(location);
122 | * return new ViewLocation(location[0], location[1]);
123 | */
124 |
125 | ViewGroup root = (ViewGroup) this.getParent();
126 | Rect rect = new Rect();
127 |
128 | root.offsetDescendantRectToMyCoords(view, rect);
129 | return rect;
130 | }
131 |
132 | @SuppressLint("NewApi")
133 | private Animator.AnimatorListener flyListener = new Animator.AnimatorListener() {
134 |
135 | @Override
136 | public void onAnimationCancel(Animator arg0) {
137 | }
138 |
139 | @Override
140 | public void onAnimationEnd(Animator arg0) {
141 | FocusBorderView.this.setVisibility(View.VISIBLE);
142 | }
143 |
144 | @Override
145 | public void onAnimationRepeat(Animator arg0) {
146 | }
147 |
148 | @Override
149 | public void onAnimationStart(Animator arg0) {
150 | }
151 |
152 | };
153 | }
154 |
--------------------------------------------------------------------------------
/src/com/example/borderviewdemo/View/BorderView.java:
--------------------------------------------------------------------------------
1 | package com.example.borderviewdemo.View;
2 |
3 | import com.example.borderviewdemo.Utils.AnimUtils;
4 | import com.example.tvborderviewdemo.R;
5 |
6 | import android.content.Context;
7 | import android.graphics.drawable.AnimationDrawable;
8 | import android.media.AudioManager;
9 | import android.media.SoundPool;
10 | import android.os.Handler;
11 | import android.util.AttributeSet;
12 | import android.util.Log;
13 | import android.view.View;
14 | import android.view.animation.Animation;
15 | import android.view.animation.Animation.AnimationListener;
16 | import android.view.animation.AnimationSet;
17 | import android.view.animation.ScaleAnimation;
18 | import android.view.animation.TranslateAnimation;
19 | import android.widget.ImageView;
20 |
21 | public class BorderView extends ImageView implements AnimationListener {
22 |
23 | private static final String TAG = "BorderView";
24 | private static int BORDER_SIZE = 30;
25 | private static int TRAN_DUR_ANIM = 250;
26 |
27 | private SoundPool sp;
28 | private Context mContext;
29 |
30 | private AnimationDrawable mBoxBgAnim;
31 | private int mLeft, mTop, mRight, mBottom;
32 |
33 | public BorderView(Context context, AttributeSet attrs) {
34 | super(context, attrs);
35 |
36 | mContext = context;
37 | }
38 |
39 | @Override
40 | protected void onLayout(boolean changed, int left, int top, int right,
41 | int bottom) {
42 | super.onLayout(changed, left, top, right, bottom);
43 | if (this.mLeft != left || mTop != top || mRight != right
44 | || mBottom != bottom) {
45 | this.layout(this.mLeft, this.mTop, this.mRight, this.mBottom);
46 | }
47 | }
48 |
49 | /**
50 | * 设置边界框的外框大小
51 | *
52 | * @param size
53 | */
54 | public void setBorderSize(int size) {
55 | BORDER_SIZE = size;
56 | }
57 |
58 | /**
59 | * 设置位移动画时间
60 | *
61 | * @param dur
62 | */
63 | public void setTranslateAnimtionDuration(int dur) {
64 | TRAN_DUR_ANIM = dur;
65 | }
66 |
67 | private class ViewLocation {
68 | private int x;
69 | private int y;
70 | public ViewLocation(int x, int y) {
71 | this.x = x;
72 | this.y = y;
73 | }
74 | }
75 |
76 | public void setLocation(View view) {
77 | ViewLocation location = findLocationWithView(view);
78 | // Log.v(TAG, "setLocation X:"+location.x+" Y:"+location.y);
79 | mLeft = location.x - (int) BORDER_SIZE;
80 | mTop = location.y - (int) BORDER_SIZE;
81 | mRight = location.x + (int) BORDER_SIZE + view.getWidth();
82 | mBottom = location.y + (int) BORDER_SIZE + view.getHeight();
83 | this.layout(mLeft, mTop, mRight, mBottom);
84 | this.clearAnimation();
85 | BorderView.this.setVisibility(View.VISIBLE);
86 | }
87 |
88 | /**
89 | * 初始化焦点框动画
90 | */
91 | public void runBorderAnimation() {
92 | this.setBackgroundResource(R.anim.box_normal);
93 | restartBoxAnim();
94 | }
95 |
96 | /**
97 | * 获取View的位置
98 | *
99 | * @param view
100 | * 获取的控件
101 | * @return 位置
102 | */
103 | public ViewLocation findLocationWithView(View view) {
104 | int[] location = new int[2];
105 | view.getLocationOnScreen(location);
106 | return new ViewLocation(location[0], location[1]);
107 | }
108 |
109 | /**
110 | * 重启闪烁动画
111 | *
112 | * @param context
113 | */
114 | public void restartBoxAnim() {
115 | BorderView.this.setVisibility(View.VISIBLE);
116 | this.clearAnimation();
117 | if (mBoxBgAnim == null) {
118 | mBoxBgAnim = (AnimationDrawable) this.getBackground();
119 | }
120 | if (mBoxBgAnim.isRunning()) {
121 | mBoxBgAnim.stop();
122 | }
123 | mBoxBgAnim.start();
124 | this.startAnimation(AnimUtils.buildAnimBoxNormal(mContext));
125 | }
126 |
127 | @Override
128 | public void onAnimationEnd(Animation arg0) {
129 | notifyRestartBoxAnim(0);
130 | }
131 |
132 | @Override
133 | public void onAnimationRepeat(Animation arg0) {
134 | // TODO Auto-generated method stub
135 |
136 | }
137 |
138 | @Override
139 | public void onAnimationStart(Animation arg0) {
140 | // TODO Auto-generated method stub
141 |
142 | }
143 |
144 | /**
145 | * 记录上一次的焦点组件,用于判断是否未移动控件的焦点,相同则不重新加载动画
146 | */
147 | private View mLastFocusView;
148 |
149 | /**
150 | * 启动焦点框位移动画
151 | */
152 | public void runTranslateAnimation(View toView) {
153 | runBorderAnimation();
154 | if (toView == null || mLastFocusView == toView) {
155 | return;
156 | }
157 | // 缩放比例
158 | float scaleWValue = (float) this.getWidth()
159 | / ((float) toView.getWidth() + 2 * BORDER_SIZE);
160 | float scaleHValue = (float) this.getHeight()
161 | / ((float) toView.getHeight() + 2 * BORDER_SIZE);
162 | ScaleAnimation scale = new ScaleAnimation(scaleWValue, 1.0f,
163 | scaleHValue, 1.0f);
164 | // 记录位置信息,以为启动动画前box已经设置到目标位置了。
165 | ViewLocation fromLocation = findLocationWithView(this);
166 | ViewLocation toLocation = findLocationWithView(toView);
167 | TranslateAnimation tran = new TranslateAnimation(0,
168 | 100,
169 | 0,
170 | 0);
171 | /*TranslateAnimation tran = new TranslateAnimation(-toLocation.x
172 | + (float) BORDER_SIZE + fromLocation.x, 0, -toLocation.y
173 | + (float) BORDER_SIZE + fromLocation.y, 0);*/
174 | // Log.v("TAG","fromX:"+(-toLocation.x+(float)BORDER_SIZE+fromLocation.x)+" fromY:"+(-toLocation.y+(float)BORDER_SIZE+fromLocation.y));
175 | // Log.v("TAG","fromX:"+fromLocation.x+ " toX:"
176 | // +toLocation.x+" fromY:"+fromLocation.y+" toY:"+toLocation.x);
177 | // TranslateAnimation tran = new TranslateAnimation(0,
178 | // toLocation.x-(float)BORDER_SIZE-fromLocation.x,
179 | // 0, toLocation.y-(float)BORDER_SIZE-fromLocation.y);
180 | AnimationSet boxAnimaSet = new AnimationSet(true);
181 | boxAnimaSet.setAnimationListener(this);
182 | boxAnimaSet.addAnimation(scale);
183 | boxAnimaSet.addAnimation(tran);
184 | boxAnimaSet.setDuration(TRAN_DUR_ANIM);
185 | BorderView.this.setVisibility(View.INVISIBLE);
186 | // setLocation(toView);// 先位移到目标位置再启动动画
187 | Log.v(TAG, "setLocation runTranslateAnimation");
188 | BorderView.this.startAnimation(boxAnimaSet);
189 | mLastFocusView = toView;
190 | }
191 |
192 | public void playClickOgg() {
193 | if (sp == null) {
194 | sp = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
195 | sp.load(mContext, R.raw.himi_ogg, 0);
196 | }
197 | sp.play(1, 1, 1, 0, 0, 1);
198 | }
199 |
200 | private static AnimationSet mBoxAnimClick;
201 |
202 | private void runClickAnimtion() {
203 | playClickOgg();
204 | if (mBoxAnimClick == null) {
205 | mBoxAnimClick = AnimUtils.buildAnimBoxClick(mContext);
206 | }
207 | BorderView.this.startAnimation(mBoxAnimClick);
208 | notifyRestartBoxAnim(500);
209 | }
210 |
211 | public static final int MSG_BOX_BG_ANIM = 10;
212 | public static final int MSG_BOX_CLICK_ANIM = 11;
213 |
214 | /**
215 | * 重启背景动画
216 | *
217 | * @param delay
218 | * 延迟时间毫秒
219 | */
220 | void notifyRestartBoxAnim(int delay) {
221 | mBoxHandler.sendEmptyMessageDelayed(MSG_BOX_BG_ANIM, delay);
222 | }
223 |
224 | /**
225 | * 点击动画
226 | */
227 | public void notifyClickBoxAnim() {
228 | mBoxHandler.sendEmptyMessageDelayed(MSG_BOX_CLICK_ANIM, 10);
229 | }
230 |
231 | Handler mBoxHandler = new Handler() {
232 | public void handleMessage(android.os.Message msg) {
233 | switch (msg.what) {
234 | case MSG_BOX_BG_ANIM:
235 | restartBoxAnim();
236 | break;
237 | case MSG_BOX_CLICK_ANIM:
238 | runClickAnimtion();
239 | break;
240 | }
241 | };
242 | };
243 |
244 | }
245 |
--------------------------------------------------------------------------------
/src/com/example/borderviewdemo/View/CopyOfCopyOfFocusBorderView.java:
--------------------------------------------------------------------------------
1 | package com.example.borderviewdemo.View;
2 |
3 | import com.example.borderviewdemo.Utils.DensityUtil;
4 | import com.example.tvborderviewdemo.R;
5 | import com.nineoldandroids.animation.Animator;
6 | import com.nineoldandroids.animation.AnimatorSet;
7 | import com.nineoldandroids.animation.ObjectAnimator;
8 | import com.nineoldandroids.animation.Animator.AnimatorListener;
9 |
10 | //import android.animation.Animator;
11 | import android.annotation.SuppressLint;
12 | import android.content.Context;
13 | import android.util.AttributeSet;
14 | import android.util.Log;
15 | import android.view.View;
16 | import android.view.animation.DecelerateInterpolator;
17 | import android.widget.ImageView;
18 |
19 | public class CopyOfCopyOfFocusBorderView extends ImageView {
20 | private static int X_BORDER_SIZE = 0;
21 | private static int Y_BORDER_SIZE = 0;
22 | private static int TRAN_DUR_ANIM = 250;
23 |
24 | public CopyOfCopyOfFocusBorderView(Context context, AttributeSet attrs) {
25 | super(context, attrs);
26 |
27 | this.setBackgroundResource(R.drawable.focus_bound);
28 | this.setVisibility(View.INVISIBLE);
29 | }
30 |
31 | @Override
32 | protected void onLayout(boolean changed, int left, int top, int right,
33 | int bottom) {
34 | super.onLayout(changed, left, top, right, bottom);
35 | }
36 |
37 | @SuppressLint("NewApi")
38 | @Override
39 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
40 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
41 | }
42 |
43 | /**
44 | * 设置边界框的外框大小
45 | *
46 | * @param size
47 | */
48 | public void setBorderSize(int w, int h) {
49 | X_BORDER_SIZE = w;
50 | Y_BORDER_SIZE = h;
51 | }
52 |
53 | /**
54 | * 设置位移动画时间
55 | *
56 | * @param dur
57 | */
58 | public void setTranslateAnimtionDuration(int dur) {
59 | TRAN_DUR_ANIM = dur;
60 | }
61 |
62 | private int mLeft, mTop, mRight, mBottom;
63 |
64 | public void setLocation(View view) {
65 | ViewLocation location = findLocationWithView(view);
66 | mLeft = location.x - (int) X_BORDER_SIZE;
67 | mTop = location.y - (int) Y_BORDER_SIZE;
68 | mRight = location.x + (int) X_BORDER_SIZE + view.getWidth();
69 | mBottom = location.y + (int) Y_BORDER_SIZE + view.getHeight();
70 | this.layout(mLeft, mTop, mRight, mBottom);
71 | this.clearAnimation();
72 | this.setVisibility(View.VISIBLE);
73 | }
74 |
75 | /**
76 | * 记录上一次的焦点组件,用于判断是否未移动控件的焦点,相同则不重新加载动画
77 | */
78 | private View mLastFocusView;
79 |
80 | /**
81 | * 启动焦点框位移动画
82 | */
83 | public void runTranslateAnimation(View toView) {
84 | runTranslateAnimation(toView, 1.0F, 1.0F);
85 | }
86 |
87 | /**
88 | * 启动焦点框位移及缩放动画
89 | */
90 | public void runTranslateAnimation(View toView, float scaleX, float scaleY) {
91 | if (toView == null || mLastFocusView == toView) {
92 | return;
93 | }
94 |
95 | int x = toView.getLeft() - this.getLeft();
96 | int y = toView.getTop() - this.getTop();
97 |
98 | Log.d("LIF", "this.getLeft() = " + this.getLeft());
99 | Log.d("LIF", "this.getTop() = " + this.getTop());
100 | Log.d("LIF", "toView.getLeft() = " + toView.getLeft());
101 | Log.d("LIF", "toView.getTop() = " + toView.getTop());
102 |
103 | ViewLocation locationTo = findLocationWithView(toView);
104 | ViewLocation locationFrom = findLocationWithView(this);
105 |
106 | /*
107 | * int x = locationTo.x - locationFrom.x; int y = locationTo.y -
108 | * locationFrom.y;
109 | */
110 |
111 |
112 | int deltaX = (toView.getWidth() - this.getWidth()) / 2;
113 | int deltaY = (toView.getHeight() - this.getHeight()) / 2;
114 | x = DensityUtil.dip2px(this.getContext(), x + deltaX);
115 | y = DensityUtil.dip2px(this.getContext(), y + deltaY);
116 |
117 | float toWidth = toView.getWidth() * scaleX;
118 | float toHeight = toView.getHeight() * scaleY;
119 | float targetScaleX = (float) toWidth
120 | / (float) (this.getWidth() - 2 * X_BORDER_SIZE);
121 | float targetScaleY = (float) toHeight
122 | / (float) (this.getHeight() - 2 * Y_BORDER_SIZE);
123 | int width = (int) (toWidth + 2 * X_BORDER_SIZE * targetScaleX);
124 | int height = (int) (toHeight + 2 * Y_BORDER_SIZE * targetScaleY);
125 |
126 | flyWhiteBorder(width, height, x, y);
127 |
128 | mLastFocusView = toView;
129 | }
130 |
131 | private float mLastXPos = 0F;
132 | private float mLastYPos = 0F;
133 | private float mLastXScale = 1F;
134 | private float mLastYScale = 1F;
135 |
136 | /**
137 | * 白色焦点框飞动、移动、变大
138 | *
139 | * @param width
140 | * 白色框的宽(非放大后的)
141 | * @param height
142 | * 白色框的高(非放大后的)
143 | * @param paramFloat1
144 | * x坐标偏移量,相对于初始的白色框的中心点
145 | * @param paramFloat2
146 | * y坐标偏移量,相对于初始的白色框的中心点
147 | * */
148 | @SuppressLint("NewApi")
149 | private void flyWhiteBorder(int width, int height, float x, float y) {
150 | // this.setVisibility(View.VISIBLE);
151 | int mWidth = this.getWidth();
152 | int mHeight = this.getHeight();
153 |
154 | if (mWidth == 0 || mHeight == 0) {
155 | mWidth = 10;
156 | mHeight = 10;
157 | }
158 |
159 | float scaleX = (float) width / (float) mWidth;
160 | float scaleY = (float) height / (float) mHeight;
161 |
162 | /*
163 | * Log.d("LIF", "mWidth = " + mWidth + ", mHeight = " + mHeight);
164 | * Log.d("LIF", "width = " + width + ", height = " + height);
165 | */
166 | Log.d("LIF", "x = " + x + ", y = " + y);
167 | Log.d("LIF", "scaleX = " + scaleX + ", scaleY = " + scaleY);
168 |
169 | /*
170 | * animate().translationX(x).translationY(y).setDuration(TRAN_DUR_ANIM)
171 | * .scaleX(scaleX).scaleY(scaleY) .setInterpolator(new
172 | * DecelerateInterpolator()) .setListener(flyListener).start();
173 | */
174 |
175 | float scaleFromX = mLastXScale;
176 | float scaleFromY = mLastYScale;
177 | float scaleToX = scaleX;
178 | float scaleToY = scaleY;
179 |
180 | float translateFromY = mLastYPos;
181 | float translateToY = y;
182 | float translateFromX = mLastXPos;
183 | float translateToX = x;
184 |
185 | AnimatorSet set = new AnimatorSet();
186 | set.playTogether(ObjectAnimator.ofFloat(this, "scaleX", scaleFromX,
187 | scaleToX), ObjectAnimator.ofFloat(this, "scaleY", scaleFromY,
188 | scaleToY), ObjectAnimator.ofFloat(this, "translationX",
189 | translateFromX, translateToX),
190 |
191 | ObjectAnimator.ofFloat(this, "translationY", translateFromY,
192 | translateToY));
193 | set.addListener(new AnimatorListener() {
194 |
195 | @Override
196 | public void onAnimationCancel(Animator arg0) {
197 | }
198 |
199 | @Override
200 | public void onAnimationEnd(Animator arg0) {
201 | if (CopyOfCopyOfFocusBorderView.this.getVisibility() != View.VISIBLE) {
202 | CopyOfCopyOfFocusBorderView.this.setVisibility(View.VISIBLE);
203 | }
204 | }
205 |
206 | @Override
207 | public void onAnimationRepeat(Animator arg0) {
208 | }
209 |
210 | @Override
211 | public void onAnimationStart(Animator arg0) {
212 | }
213 |
214 | });
215 | set.setDuration(250).start();
216 |
217 | mLastXPos = x;
218 | mLastYPos = y;
219 | mLastXScale = scaleX;
220 | mLastYScale = scaleY;
221 | }
222 |
223 | private class ViewLocation {
224 | private int x;
225 | private int y;
226 |
227 | public ViewLocation(int x, int y) {
228 | this.x = x;
229 | this.y = y;
230 | }
231 | }
232 |
233 | /**
234 | * 获取View的位置
235 | *
236 | * @param view
237 | * 获取的控件
238 | * @return 位置
239 | */
240 | public ViewLocation findLocationWithView(View view) {
241 | int[] location = new int[2];
242 | view.getLocationOnScreen(location);
243 | // view.getLocationInWindow(location);
244 | Log.d("LIF", "location[0] = " + location[0]);
245 | Log.d("LIF", "location[1] = " + location[1]);
246 | return new ViewLocation(location[0], location[1]);
247 | }
248 |
249 | /*
250 | * @SuppressLint("NewApi") private Animator.AnimatorListener flyListener =
251 | * new Animator.AnimatorListener() {
252 | *
253 | * @Override public void onAnimationCancel(Animator arg0) { }
254 | *
255 | * @Override public void onAnimationEnd(Animator arg0) { if
256 | * (FocusBorderView.this.getVisibility() != View.VISIBLE) {
257 | * FocusBorderView.this.setVisibility(View.VISIBLE); } }
258 | *
259 | * @Override public void onAnimationRepeat(Animator arg0) { }
260 | *
261 | * @Override public void onAnimationStart(Animator arg0) { }
262 | *
263 | * };
264 | */
265 | }
266 |
--------------------------------------------------------------------------------