Android发光特效焦点框-遥控器版本
16lz
2021-01-23
适用于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;@Overrideprotected 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;}};};@Overridepublic void onAnimationEnd(Animation arg0) {notifyRestartBoxAnim(0);}@Overridepublic void onAnimationRepeat(Animation arg0) {}@Overridepublic 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();@Overrideprotected 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() {@Overridepublic void run() {if(view == null){return;}view.requestFocus();mBorderView.setLocation(view);}}, delay);}public void runClickAnim(){this.getBorderView().notifyClickBoxAnim();}@Overrideprotected void onResume() {super.onResume();}@Overridepublic 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)
至此全部完成。
更多相关文章
- Activity 转场动画踩坑
- Android中的四种动画效果
- 两个Android开源项目:Android显示GIF动画
- Android动画之view动画
- 捋一捋Android的转场动画
- Android Activity 界面切换动画
- Android Studio 初步在代码中操作控件