適用於android智能電視的全局焦點框控件,可以省去爲每個按鈕設計focused的按鈕圖標。
效果描述:
1.完整適配各種尺寸的圖標按鈕
2.平滑的補件動畫切換焦點
3.點擊效果閃光+聲音
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
/**
* 焦點框控件
* @author jiangyuchen
* @date 2014-2-13
*/
public class BorderView extends ImageView implements AnimationListener {
protected static final String TAG = "BestSetting.BorderView";
private static int BORDER_SIZE = 20;
private static int TRAN_DUR_ANIM = 250;
private SoundPool sp;
private Context mContext;
public BorderView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
}
/**
* 設置邊界框的外框大小
* @param padding
*/
public void setBorderSize(int size){
BORDER_SIZE = size;
}
/**
* 設置位移動畫時間
* @param dur
*/
public void setTranslateAnimtionDuration(int dur){
TRAN_DUR_ANIM = dur;
}
public void setLocation(View view){
ViewLocation location = findLocationWithView(view);
// Log.v(TAG, "setLocation X:"+location.x+" Y:"+location.y);
mLeft = location.x-(int)BORDER_SIZE;
mTop = location.y-(int)BORDER_SIZE;
mRight = location.x+(int)BORDER_SIZE+view.getWidth();
mBottom = location.y+(int)BORDER_SIZE+view.getHeight();
this.layout(mLeft, mTop, mRight, mBottom);
this.clearAnimation();
BorderView.this.setVisibility(View.VISIBLE);
}
private int mLeft, mTop, mRight, mBottom;
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
if(this.mLeft != left || mTop != top || mRight != right || mBottom != bottom){
this.layout(this.mLeft, this.mTop, this.mRight, this.mBottom);
}
}
/**
* 獲取View的位置
* @param view 獲取的控件
* @return 位置
*/
public ViewLocation findLocationWithView(View view){
int[] location = new int[2];
view.getLocationOnScreen(location);
return new ViewLocation(location[0], location[1]);
}
private AnimationDrawable mBoxBgAnim;
/**
* 初始化焦點框動畫
*/
public void runBorderAnimation(){
this.setBackgroundResource(R.anim.box_normal);
restartBoxAnim();
}
/**
* 重啓閃爍動畫
* @param context
*/
public void restartBoxAnim(){
BorderView.this.setVisibility(View.VISIBLE);
this.clearAnimation();
if(mBoxBgAnim == null){
mBoxBgAnim = (AnimationDrawable) this.getBackground();
}
if(mBoxBgAnim.isRunning()){
mBoxBgAnim.stop();
}
mBoxBgAnim.start();
this.startAnimation(AnimUtils.buildAnimBoxNormal(mContext));
}
/**
* 記錄上一次的焦點組件,用於判斷是否未移動控件的焦點,相同則不重新加載動畫
*/
private View mLastFocusView;
/**
* 啓動焦點框位移動畫
*/
public void runTranslateAnimation(View toView) {
runBorderAnimation();
if(toView == null || mLastFocusView == toView){
return;
}
//縮放比例
float scaleWValue = (float) this.getWidth()/((float) toView.getWidth()+2*BORDER_SIZE);
float scaleHValue = (float) this.getHeight()/((float) toView.getHeight()+2*BORDER_SIZE);
ScaleAnimation scale = new ScaleAnimation( scaleWValue, 1.0f,
scaleHValue,1.0f);
//記錄位置信息,以爲啓動動畫前box已經設置到目標位置了。
ViewLocation fromLocation = findLocationWithView(this);
ViewLocation toLocation = findLocationWithView(toView);
TranslateAnimation tran = new TranslateAnimation(-toLocation.x+(float)BORDER_SIZE+fromLocation.x, 0,
-toLocation.y+(float)BORDER_SIZE+fromLocation.y, 0);
// Log.v("TAG","fromX:"+(-toLocation.x+(float)BORDER_SIZE+fromLocation.x)+" fromY:"+(-toLocation.y+(float)BORDER_SIZE+fromLocation.y));
// Log.v("TAG","fromX:"+fromLocation.x+ " toX:" +toLocation.x+" fromY:"+fromLocation.y+" toY:"+toLocation.x);
// TranslateAnimation tran = new TranslateAnimation(0, toLocation.x-(float)BORDER_SIZE-fromLocation.x,
// 0, toLocation.y-(float)BORDER_SIZE-fromLocation.y);
AnimationSet boxAnimaSet = new AnimationSet(true);
boxAnimaSet.setAnimationListener(this);
boxAnimaSet.addAnimation(scale);
boxAnimaSet.addAnimation(tran);
boxAnimaSet.setDuration(TRAN_DUR_ANIM);
BorderView.this.setVisibility(View.INVISIBLE);
setLocation(toView);//先位移到目標位置再啓動動畫
Log.v(TAG, "setLocation runTranslateAnimation");
BorderView.this.startAnimation(boxAnimaSet);
mLastFocusView = toView;
}
public void playClickOgg(){
if(sp == null){
sp =new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
sp.load(mContext, R.raw.djtx, 0);
}
sp.play(1, 1, 1, 0, 0, 1);
}
private static AnimationSet mBoxAnimClick;
private void runClickAnimtion(){
playClickOgg();
if(mBoxAnimClick == null){
mBoxAnimClick = AnimUtils.buildAnimBoxClick(mContext);
}
BorderView.this.startAnimation(mBoxAnimClick);
notifyRestartBoxAnim(500);
}
public static final int MSG_BOX_BG_ANIM = 10;
public static final int MSG_BOX_CLICK_ANIM = 11;
/**
* 重啓背景動畫
* @param delay 延遲時間毫秒
*/
void notifyRestartBoxAnim(int delay){
mBoxHandler.sendEmptyMessageDelayed(MSG_BOX_BG_ANIM, delay);
}
/**
* 點擊動畫
*/
public void notifyClickBoxAnim(){
mBoxHandler.sendEmptyMessageDelayed(MSG_BOX_CLICK_ANIM, 10);
}
Handler mBoxHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch(msg.what){
case MSG_BOX_BG_ANIM:
restartBoxAnim();
break;
case MSG_BOX_CLICK_ANIM:
runClickAnimtion();
break;
}
};
};
@Override
public void onAnimationEnd(Animation arg0) {
notifyRestartBoxAnim(0);
}
@Override
public void onAnimationRepeat(Animation arg0) {
}
@Override
public void onAnimationStart(Animation arg0) {
}
}
動畫類:
import android.content.Context;
import android.util.Log;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
public class AnimUtils {
private static final String TAG = "AnimUtils";
private static Animation mBoxAnimNormal;
public static Animation buildAnimBoxNormal(Context context) {
if (mBoxAnimNormal != null) {
return mBoxAnimNormal;
}
mBoxAnimNormal = android.view.animation.AnimationUtils.loadAnimation(
context, R.anim.box_alpha);
return mBoxAnimNormal;
}
public static AnimationSet buildAnimBoxClick(Context context) {
final ScaleAnimation scale = new ScaleAnimation(0.5f, 1.3f, 0.5f, 1.3f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
AlphaAnimation alpha = new AlphaAnimation(0.5f, 1.0f);
AnimationSet mBoxAnimClick = new AnimationSet(true);
mBoxAnimClick.addAnimation(scale);
mBoxAnimClick.addAnimation(alpha);
mBoxAnimClick.setDuration(100);
return mBoxAnimClick;
}
}
動畫xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
>
<alpha
android:repeatCount="infinite"
android:repeatMode="restart"
android:duration="1000"
android:fromAlpha="2.0"
android:toAlpha="0.1" />
<alpha
android:repeatCount="infinite"
android:repeatMode="restart"
android:duration="1000"
android:fromAlpha="0.1"
android:toAlpha="2.0" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false" >
<item
android:drawable="@drawable/bound"
android:duration="1000"/>
<item
android:drawable="@drawable/bound2"
android:duration="1000"/>
</animation-list>
bound:
bound2:
加入到佈局中方法:
父佈局使用相對佈局
<com.bestv.setting.views.BorderView
android:layout_width="0px"
android:layout_height="0px"
android:id="@+id/box"
/>
在Acitivity中的使用方法:
import android.app.Activity;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import com.bestv.setting.utils.BoxNotFoundException;
import com.bestv.setting.views.BorderView;
public class BaseActivity extends Activity implements OnFocusChangeListener {
private static final String TAG_base = "BaseActivity";
BorderView mBorderView;
Handler mHandler = new Handler();
@Override
protected void onStart() {
super.onStart();
View view = findViewById(R.id.box);
if(view == null){
throw new BoxNotFoundException();//必須在父佈局中焦點框控件,否則拋出異常
}
}
public BorderView getBorderView() {
return mBorderView;
}
public void setBorderView(BorderView box) {
this.mBorderView = box;
}
public void setFocusedView(final View view, int delay){
if(mBorderView == null){
mBorderView = (BorderView)findViewById(R.id.box);
}
mBorderView.runBorderAnimation();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if(view == null){
return;
}
view.requestFocus();
mBorderView.setLocation(view);
}
}, delay);
}
public void runClickAnim(){
this.getBorderView().notifyClickBoxAnim();
}
@Override
protected void onResume() {
super.onResume();
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus){
mBorderView.runTranslateAnimation(v);
}
}
public void setClickListener(View v, OnClickListener listener){
v.setOnClickListener(listener);
v.setOnFocusChangeListener(this);
}
}
爲每個能夠獲取到焦點的空間調用函數:
setClickListener(View v, OnClickListener listener)
至此全部完成。