TV Metro界面(仿泰捷视频TV版)源码解析

原文: TV Metro界面(仿泰捷视频TV版)源码解析

版权声明:我已委托“维权骑士”(rightknights.com)为我的文章进行维权行动.转载务必转载所有,且须注明出处。否则保留追究法律责任 https://blog.csdn.net/hejjunlin/article/details/52822499

转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52822499

前言:上一篇介绍了仿泰捷视频TV版的效果,对应github:https://github.com/hejunlin2013/TVSample,今天就介绍下对应的源码部分

先看下View的层级结构图:

这里写图片描述

在SmoothHorizonalScrollView(继承HorizonalScrollView)下挂上DrawingOrderRelativeLayout(继承自RelativeLayout),接下来就是一个个的MetroItem,每个Item下包含ImageView(海报图),CornerView(VIP角标),TextView(海报图简介),像第一张和第二张,以及最后两张,显示是频道入口,只有一个ImageView,无CornerView及TextView。

角标View

1public class CornerVew extends View { 2 3 private String mTextContent;//显示字体内容 4 private int mTextColor;//字体颜色 5 private float mTextSize;//字体大小 6 private boolean mTextBold;//是否字体加粗 7 private boolean mFillTriangle;//是否三角形 8 private boolean mTextAllCaps;// 9 private int mBackgroundColor;//背景颜色 10 private float mMinSize;//最小值 11 private float mPadding;//padding值 12 private int mGravity;// 13 private static final int DEFAULT_DEGREES = 45;//切角 14 15 private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿 16 private Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 17 private Path mPath = new Path(); 18 19 public CornerVew(Context context) { 20 this(context, null); 21 } 22 23 public CornerVew(Context context, AttributeSet attrs) { 24 super(context, attrs); 25 26 obtainAttributes(context, attrs); 27 mTextPaint.setTextAlign(Paint.Align.CENTER);//从中间开始画 28 } 29 30 private void obtainAttributes(Context context, AttributeSet attrs) { 31 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CornerVew); 32 mTextContent = ta.getString(R.styleable.CornerVew_cv_text); 33 mTextColor = ta.getColor(R.styleable.CornerVew_cv_text_color, Color.parseColor("#ffffff")); 34 mTextSize = ta.getDimension(R.styleable.CornerVew_cv_text_size, sp2px(11)); 35 mTextBold = ta.getBoolean(R.styleable.CornerVew_cv_text_bold, true);//加粗 36 mTextAllCaps = ta.getBoolean(R.styleable.CornerVew_cv_text_all_caps, true); 37 mFillTriangle = ta.getBoolean(R.styleable.CornerVew_cv_fill_triangle, false); 38 mBackgroundColor = ta.getColor(R.styleable.CornerVew_cv_background_color, Color.parseColor("#FF4081")); 39 mMinSize = ta.getDimension(R.styleable.CornerVew_cv_min_size, mFillTriangle ? dp2px(35) : dp2px(50)); 40 mPadding = ta.getDimension(R.styleable.CornerVew_cv_padding, dp2px(3.5f)); 41 mGravity = ta.getInt(R.styleable.CornerVew_cv_gravity, Gravity.TOP | Gravity.LEFT); 42 ta.recycle(); 43 } 44 45 @Override 46 protected void onDraw(Canvas canvas) { 47 int size = getHeight(); 48 49 mTextPaint.setColor(mTextColor); 50 mTextPaint.setTextSize(mTextSize); 51 mTextPaint.setFakeBoldText(mTextBold); 52 mBackgroundPaint.setColor(mBackgroundColor); 53 54 float textHeight = mTextPaint.descent() - mTextPaint.ascent(); 55 if (mFillTriangle) { 56 if (mGravity == (Gravity.TOP | Gravity.LEFT)) {//左上角 57 mPath.reset(); 58 mPath.moveTo(0, 0); 59 mPath.lineTo(0, size); 60 mPath.lineTo(size, 0); 61 mPath.close(); 62 canvas.drawPath(mPath, mBackgroundPaint); 63 64 drawTextWhenFill(size, -DEFAULT_DEGREES, canvas, true); 65 } else if (mGravity == (Gravity.TOP | Gravity.RIGHT)) {//右上角 66 mPath.reset(); 67 mPath.moveTo(size, 0); 68 mPath.lineTo(0, 0); 69 mPath.lineTo(size, size); 70 mPath.close(); 71 canvas.drawPath(mPath, mBackgroundPaint); 72 73 drawTextWhenFill(size, DEFAULT_DEGREES, canvas, true); 74 } else if (mGravity == (Gravity.BOTTOM | Gravity.LEFT)) {//左下角 75 mPath.reset(); 76 mPath.moveTo(0, size); 77 mPath.lineTo(0, 0); 78 mPath.lineTo(size, size); 79 mPath.close(); 80 canvas.drawPath(mPath, mBackgroundPaint); 81 82 drawTextWhenFill(size, DEFAULT_DEGREES, canvas, false); 83 } else if (mGravity == (Gravity.BOTTOM | Gravity.RIGHT)) {//右下角 84 mPath.reset(); 85 mPath.moveTo(size, size); 86 mPath.lineTo(0, size); 87 mPath.lineTo(size, 0); 88 mPath.close(); 89 canvas.drawPath(mPath, mBackgroundPaint); 90 91 drawTextWhenFill(size, -DEFAULT_DEGREES, canvas, false); 92 } 93 } else { 94 double delta = (textHeight + mPadding * 2) * Math.sqrt(2); 95 if (mGravity == (Gravity.TOP | Gravity.LEFT)) { 96 mPath.reset(); 97 mPath.moveTo(0, (float) (size - delta)); 98 mPath.lineTo(0, size); 99 mPath.lineTo(size, 0); 100 mPath.lineTo((float) (size - delta), 0); 101 mPath.close(); 102 canvas.drawPath(mPath, mBackgroundPaint); 103 104 drawText(size, -DEFAULT_DEGREES, canvas, textHeight, true); 105 } else if (mGravity == (Gravity.TOP | Gravity.RIGHT)) { 106 mPath.reset(); 107 mPath.moveTo(0, 0); 108 mPath.lineTo((float) delta, 0); 109 mPath.lineTo(size, (float) (size - delta)); 110 mPath.lineTo(size, size); 111 mPath.close(); 112 canvas.drawPath(mPath, mBackgroundPaint); 113 114 drawText(size, DEFAULT_DEGREES, canvas, textHeight, true); 115 } else if (mGravity == (Gravity.BOTTOM | Gravity.LEFT)) { 116 mPath.reset(); 117 mPath.moveTo(0, 0); 118 mPath.lineTo(0, (float) delta); 119 mPath.lineTo((float) (size - delta), size); 120 mPath.lineTo(size, size); 121 mPath.close(); 122 canvas.drawPath(mPath, mBackgroundPaint); 123 124 drawText(size, DEFAULT_DEGREES, canvas, textHeight, false); 125 } else if (mGravity == (Gravity.BOTTOM | Gravity.RIGHT)) { 126 mPath.reset(); 127 mPath.moveTo(0, size); 128 mPath.lineTo((float) delta, size); 129 mPath.lineTo(size, (float) delta); 130 mPath.lineTo(size, 0); 131 mPath.close(); 132 canvas.drawPath(mPath, mBackgroundPaint); 133 134 drawText(size, -DEFAULT_DEGREES, canvas, textHeight, false); 135 } 136 } 137 } 138 139 private void drawText(int size, float degrees, Canvas canvas, float textHeight, boolean isTop) { 140 canvas.save(); 141 canvas.rotate(degrees, size / 2f, size / 2f); 142 float delta = isTop ? -(textHeight + mPadding * 2) / 2 : (textHeight + mPadding * 2) / 2; 143 float textBaseY = size / 2 - (mTextPaint.descent() + mTextPaint.ascent()) / 2 + delta; 144 canvas.drawText(mTextAllCaps ? mTextContent.toUpperCase() : mTextContent, 145 getPaddingLeft() + (size - getPaddingLeft() - getPaddingRight()) / 2, textBaseY, mTextPaint); 146 canvas.restore(); 147 } 148 149 private void drawTextWhenFill(int size, float degrees, Canvas canvas, boolean isTop) { 150 canvas.save(); 151 canvas.rotate(degrees, size / 2f, size / 2f); 152 float delta = isTop ? -size / 4 : size / 4; 153 float textBaseY = size / 2 - (mTextPaint.descent() + mTextPaint.ascent()) / 2 + delta; 154 canvas.drawText(mTextAllCaps ? mTextContent.toUpperCase() : mTextContent, 155 getPaddingLeft() + (size - getPaddingLeft() - getPaddingRight()) / 2, textBaseY, mTextPaint); 156 canvas.restore(); 157 } 158 159 @Override 160 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 161 int measuredWidth = measureWidth(widthMeasureSpec); 162 setMeasuredDimension(measuredWidth, measuredWidth); 163 } 164 165 private int measureWidth(int widthMeasureSpec) { 166 int result; 167 int specMode = MeasureSpec.getMode(widthMeasureSpec);//获取测量模式 168 int specSize = MeasureSpec.getSize(widthMeasureSpec);//获取测量大小 169 if (specMode == MeasureSpec.EXACTLY) {//精确模式 170 result = specSize; 171 } else { 172 int padding = getPaddingLeft() + getPaddingRight(); 173 mTextPaint.setColor(mTextColor); 174 mTextPaint.setTextSize(mTextSize); 175 float textWidth = mTextPaint.measureText(mTextContent + ""); 176 result = (int) ((padding + (int) textWidth) * Math.sqrt(2)); 177 if (specMode == MeasureSpec.AT_MOST) { 178 result = Math.min(result, specSize); 179 } 180 result = Math.max((int) mMinSize, result); 181 } 182 183 return result; 184 } 185 186 protected int dp2px(float dp) { 187 final float scale = getResources().getDisplayMetrics().density; 188 return (int) (dp * scale + 0.5f); 189 } 190 191 protected int sp2px(float sp) { 192 final float scale = getResources().getDisplayMetrics().scaledDensity; 193 return (int) (sp * scale + 0.5f); 194 } 195}

绘制子视图顺序的Layout

1public class DrawingOrderRelativeLayout extends RelativeLayout { 2 private int position = 0; 3 4 public DrawingOrderRelativeLayout(Context context) { 5 super(context); 6 } 7 8 public DrawingOrderRelativeLayout(Context context, AttributeSet attrs) { 9 super(context, attrs); 10 this.setChildrenDrawingOrderEnabled(true); 11 } 12 13 public void setCurrentPosition(int pos) { 14 this.position = pos; 15 } 16 17 @Override 18 public boolean dispatchKeyEvent(KeyEvent event) { 19 View focused = findFocus();//找到焦点 20 int pos = indexOfChild(focused); 21 if (pos >= 0 && pos < getChildCount()) { 22 setCurrentPosition(pos); 23 postInvalidate(); 24 } 25 26 return super.dispatchKeyEvent(event); 27 } 28 29 @Override 30 protected int getChildDrawingOrder(int childCount, int i) {//改变ViewGroup的子视图绘制顺序 31 View v = getFocusedChild(); 32 int pos = indexOfChild(v); 33 if (pos >= 0 && pos < childCount) 34 setCurrentPosition(pos); 35 if (i == childCount - 1) {//从最后一个view开始绘制 36 return position; 37 } 38 if (i == position) { 39 return childCount - 1; 40 } 41 return i; 42 } 43 44}

MetroItem

1public class MetroItemFrameLayout extends FrameLayout implements IMetroItemRound { 2 3 private MetroItemRound mMetroItemRound; 4 public MetroItemFrameLayout(Context context) { 5 super(context); 6 init(context, null, 0); 7 } 8 9 public MetroItemFrameLayout(Context context, AttributeSet attrs) { 10 super(context, attrs); 11 init(context, attrs, 0); 12 } 13 14 public MetroItemFrameLayout(Context context, AttributeSet attrs, int defStyle) { 15 super(context, attrs, defStyle); 16 init(context, attrs, defStyle); 17 } 18 19 private void init(Context context, AttributeSet attrs, int defStyle) { 20 mMetroItemRound = new MetroItemRound(this, context, attrs, defStyle); 21 setWillNotDraw(false);//设置为false时,就会调用自定义的布局 22 } 23 24 @Override 25 public void draw(Canvas canvas) { 26 mMetroItemRound.draw(canvas); 27 } 28 29 @Override 30 public MetroItemRound getRoundImpl() { 31 return mMetroItemRound; 32 } 33 34 @Override 35 public void drawRadious(Canvas canvas) { 36 super.draw(canvas); 37 } 38}

MetroItem的圆角

1public class MetroItemRound { 2 3 private float mRadius;//圆角 4 private float mTopLeftRadius;//左上角的圆角 5 private float mTopRightRadius;//右上角的圆角 6 private float mBottomLeftRadius;//左下角的圆角 7 private float mBottomRightRadius;//右下角的圆角 8 private Paint mPaint;//画笔 9 private Path mPath;//画布 10 private IMetroItemRound mView; 11 12 public MetroItemRound(IMetroItemRound view, Context context, AttributeSet attrs, int defStyle) { 13 init(context, attrs, defStyle); 14 mView = view; 15 } 16 17 private void init(Context context, AttributeSet attrs, int defStyle) { 18 mPaint = new Paint();//实例化画笔 19 mPaint.setColor(Color.WHITE);//设置画笔颜色为白色 20 mPaint.setAntiAlias(true);//设置抗锯齿 21 mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));//取下层绘制非交集部分 22 mPath = new Path();//设置路径 23 24 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MetroItemRound, defStyle, 0); 25 mBottomLeftRadius = a.getDimension(R.styleable.MetroItemRound_bottomLeftRadius, -1); 26 mBottomRightRadius = a.getDimension(R.styleable.MetroItemRound_bottomRightRadius, -1); 27 mTopLeftRadius = a.getDimension(R.styleable.MetroItemRound_topLeftRadius, -1); 28 mTopRightRadius = a.getDimension(R.styleable.MetroItemRound_topRightRadius, -1); 29 mRadius = a.getDimension(R.styleable.MetroItemRound_radius, -1); 30 31 if (mRadius > 0) { 32 if (mBottomLeftRadius < 0) 33 mBottomLeftRadius = mRadius; 34 if (mBottomRightRadius < 0) 35 mBottomRightRadius = mRadius; 36 if (mTopLeftRadius < 0) 37 mTopLeftRadius = mRadius; 38 if (mTopRightRadius < 0) 39 mTopRightRadius = mRadius; 40 } 41 a.recycle(); 42 43 } 44 45 public void draw(Canvas canvas) { 46 if (mBottomLeftRadius <= 0 && mBottomRightRadius <= 0 && mTopRightRadius <= 0 && mTopLeftRadius <= 0) { 47 mView.drawRadious(canvas); 48 return; 49 } 50 51 int width = mView.getWidth(); 52 int height = mView.getHeight(); 53 54 int count = canvas.save(); 55 int count2 = canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG); 56 57 addRoundPath(width, height); 58 mView.drawRadious(canvas);//画一个圆角 59 canvas.drawPath(mPath, mPaint); 60 canvas.restoreToCount(count2); 61 canvas.restoreToCount(count); 62 63 } 64 65 private void addRoundPath(int width, int height) { 66 //topleft path 67 if (mTopLeftRadius > 0) { 68 Path topLeftPath = new Path(); 69 topLeftPath.moveTo(0, mTopLeftRadius);//移动画笔到最左边x为0,y为mTopLeftRadius 70 topLeftPath.lineTo(0, 0);//绘制直线,从坐标0,0开始 71 topLeftPath.lineTo(mTopLeftRadius, 0);// 72 RectF arc = new RectF(0, 0, mTopLeftRadius * 2, mTopLeftRadius * 2);//矩形区域 73 //和Rect的区别是RectF表示精确到浮点型 74 topLeftPath.arcTo(arc, -90, -90);//画弧线的路径,从-90到-90,逆时针画 75 topLeftPath.close();// 76 mPath.addPath(topLeftPath); 77 } 78 79 //topRight path 80 if (mTopRightRadius > 0) { 81 Path topRightPath = new Path(); 82 topRightPath.moveTo(width, mTopRightRadius);//移动画笔到最右边x为width,y为mTopRightRadius 83 topRightPath.lineTo(width, 0); 84 topRightPath.lineTo(width - mTopRightRadius, 0); 85 RectF arc = new RectF(width - mTopRightRadius * 2, 0, width, mTopRightRadius * 2); 86 87 topRightPath.arcTo(arc, -90, 90);//这里是画180的椭圆 88 topRightPath.close(); 89 90 mPath.addPath(topRightPath); 91 92 } 93 94 //bottomLeft path 95 if (mBottomLeftRadius > 0) { 96 Path bottomLeftPath = new Path(); 97 bottomLeftPath.moveTo(0, height - mBottomLeftRadius); 98 bottomLeftPath.lineTo(0, height); 99 bottomLeftPath.lineTo(mBottomLeftRadius, height); 100 RectF arc = new RectF(0, height - mBottomLeftRadius * 2, mBottomLeftRadius * 2, height); 101 102 bottomLeftPath.arcTo(arc, 90, 90); 103 bottomLeftPath.close(); 104 mPath.addPath(bottomLeftPath); 105 } 106 107 //bottomRight path 108 if (mBottomRightRadius > 0) { 109 Path bottomRightPath = new Path(); 110 bottomRightPath.moveTo(width - mBottomRightRadius, height); 111 bottomRightPath.lineTo(width, height); 112 bottomRightPath.lineTo(width, height - mBottomRightRadius); 113 RectF arc = new RectF(width - mBottomRightRadius * 2, height - mBottomRightRadius * 2, width, height); 114 bottomRightPath.arcTo(arc, 0, 90); 115 bottomRightPath.close(); 116 mPath.addPath(bottomRightPath); 117 } 118 119 } 120}

MetroItem中外边框处理类

1public class MetroViewBorderHandler implements IMetroViewBorder { 2 3 private String TAG = MetroViewBorderHandler.class.getSimpleName(); 4 protected boolean mScalable = true;//是否缩小 5 protected float mScale = 1.1f;//设置放大比例 6 7 protected long mDurationTraslate = 200;//焦点移动的动画时间 8 protected int mMargin = 0; 9 protected View lastFocus, oldLastFocus;//上一个焦点,新的位置的焦点 10 protected AnimatorSet mAnimatorSet;//动画集合,可组合多个动画 11 protected List<Animator> mAnimatorList = new ArrayList<Animator>(); 12 protected View mTarget; 13 14 protected boolean mEnableTouch = true; 15 16 public MetroViewBorderHandler() { 17 mFocusListener.add(mFocusMoveListener);//设置焦点移动时监听 18 mFocusListener.add(mFocusScaleListener);//设置焦点放大缩小监听 19 mFocusListener.add(mFocusPlayListener); 20 mFocusListener.add(mAbsListViewFocusListener);//用作listview,gridview相关 21 } 22 23 public interface FocusListener { 24 void onFocusChanged(View oldFocus, View newFocus); 25 } 26 27 protected List<FocusListener> mFocusListener = new ArrayList<FocusListener>(1); 28 protected List<Animator.AnimatorListener> mAnimatorListener = new ArrayList<Animator.AnimatorListener>(1); 29 30 public FocusListener mFocusScaleListener = new FocusListener() { 31 @Override 32 public void onFocusChanged(View oldFocus, View newFocus) { 33 //焦点变化,设置新焦点移动的位置变成放大状态 34 mAnimatorList.addAll(getScaleAnimator(newFocus, true)); 35 if (oldFocus != null) { 36 //上一个view(之前是焦点态,onFocusChanged变成非焦点态)不为null,设置上一个为缩小状态 37 mAnimatorList.addAll(getScaleAnimator(oldFocus, false)); 38 } 39 } 40 }; 41 42 public FocusListener mFocusPlayListener = new FocusListener() { 43 @Override 44 public void onFocusChanged(View oldFocus, View newFocus) { 45 try { 46 if (newFocus instanceof AbsListView) {//如果新的view是AbsListView的实例,直接return 47 return; 48 } 49 AnimatorSet animatorSet = new AnimatorSet(); 50 animatorSet.setInterpolator(new DecelerateInterpolator(1));//设置插值器 51 animatorSet.setDuration(mDurationTraslate);//设置动画时间 52 animatorSet.playTogether(mAnimatorList);//表示两个动画同进执行 53 for (Animator.AnimatorListener listener : mAnimatorListener) { 54 animatorSet.addListener(listener); 55 } 56 mAnimatorSet = animatorSet; 57 if (oldFocus == null) {//之前view为null,表示首次状态时 58 animatorSet.setDuration(0);//无动画时长 59 mTarget.setVisibility(View.VISIBLE); 60 } 61 animatorSet.start();//开启动画 62 } catch (Exception ex) { 63 ex.printStackTrace(); 64 } 65 } 66 }; 67 68 public FocusListener mFocusMoveListener = new FocusListener() { 69 @Override 70 public void onFocusChanged(View oldFocus, View newFocus) { 71 if (newFocus == null) return;//下一个view不存在时,直接return 72 try { 73 mAnimatorList.addAll(getMoveAnimator(newFocus, 0, 0));//添加 74 } catch (Exception ex) { 75 ex.printStackTrace(); 76 } 77 } 78 }; 79 80 public FocusListener mAbsListViewFocusListener = new FocusListener() { 81 82 @Override 83 public void onFocusChanged(View oldFocus, View newFocus) { 84 try { 85 if (oldFocus == null) { 86 for (int i = 0; i < attacheViews.size(); i++) { 87 View view = attacheViews.get(i); 88 if (view instanceof AbsListView) { 89 final AbsListView absListView = (AbsListView) view; 90 mTarget.setVisibility(View.INVISIBLE); 91 if (mFirstFocus) { 92 absListView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 93 @Override 94 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 95 try { 96 absListView.removeOnLayoutChangeListener(this); 97 int factorX = 0, factorY = 0; 98 Rect rect = new Rect(); 99 View firstView = (View) absListView.getSelectedView(); 100 firstView.getLocalVisibleRect(rect); 101 if (Math.abs(rect.left - rect.right) > firstView.getMeasuredWidth()) { 102 factorX = (Math.abs(rect.left - rect.right) - firstView.getMeasuredWidth()) / 2 - 1; 103 factorY = (Math.abs(rect.top - rect.bottom) - firstView.getMeasuredHeight()) / 2; 104 } 105 List<Animator> animatorList = new ArrayList<Animator>(3); 106 animatorList.addAll(getScaleAnimator(firstView, true)); 107 animatorList.addAll(getMoveAnimator(firstView, factorX, factorY)); 108 mTarget.setVisibility(View.VISIBLE); 109 AnimatorSet animatorSet = new AnimatorSet(); 110 animatorSet.setDuration(0); 111 animatorSet.playTogether(animatorList); 112 animatorSet.start(); 113 } catch (Exception ex) { 114 ex.printStackTrace(); 115 } 116 } 117 }); 118 119 120 } 121 break; 122 } 123 } 124 } else if (oldFocus instanceof AbsListView && newFocus instanceof AbsListView) { 125 if (attacheViews.indexOf(oldFocus) >= 0 && attacheViews.indexOf(newFocus) >= 0) { 126 127 AbsListView a = (AbsListView) oldFocus; 128 AbsListView b = (AbsListView) newFocus; 129 130 MyOnItemSelectedListener oldOn = (MyOnItemSelectedListener) onItemSelectedListenerList.get(oldFocus); 131 MyOnItemSelectedListener newOn = (MyOnItemSelectedListener) onItemSelectedListenerList.get(newFocus); 132 133 134 int factorX = 0, factorY = 0; 135 Rect rect = new Rect(); 136 View firstView = (View) b.getSelectedView(); 137 firstView.getLocalVisibleRect(rect); 138 if (Math.abs(rect.left - rect.right) > firstView.getMeasuredWidth()) { 139 factorX = (Math.abs(rect.left - rect.right) - firstView.getMeasuredWidth()) / 2 - 1; 140 factorY = (Math.abs(rect.top - rect.bottom) - firstView.getMeasuredHeight()) / 2; 141 } 142 143 List<Animator> animatorList = new ArrayList<Animator>(3); 144 animatorList.addAll(getScaleAnimator(firstView, true)); 145 animatorList.addAll(getScaleAnimator(a.getSelectedView(), false)); 146 147 animatorList.addAll(getMoveAnimator(firstView, factorX, factorY)); 148 mTarget.setVisibility(View.VISIBLE); 149 150 mAnimatorSet = new AnimatorSet(); 151 152 153 mAnimatorSet.setDuration(mDurationTraslate); 154 mAnimatorSet.playTogether(animatorList); 155 mAnimatorSet.start(); 156 157 oldOn.oldFocus = null; 158 oldOn.newFocus = null; 159 160 newOn.oldFocus = null; 161 if (newOn.newFocus != null && newOn.oldFocus != null) { 162 newOn.newFocus = null; 163 } else { 164 newOn.newFocus = b.getSelectedView(); 165 } 166 167 } 168 } 169 } catch (Exception ex) { 170 ex.printStackTrace(); 171 } 172 } 173 }; 174 175 protected List<Animator> getScaleAnimator(View view, boolean isScale) { 176 177 List<Animator> animatorList = new ArrayList<Animator>(2); 178 if (!mScalable) return animatorList;//如果没有放大,直接返回 179 try { 180 float scaleBefore = 1.0f;//放大前比例 181 float scaleAfter = mScale;//放大后比例 182 if (!isScale) {// 183 scaleBefore = mScale; 184 scaleAfter = 1.0f; 185 } 186 ObjectAnimator scaleX = new ObjectAnimator().ofFloat(view, "scaleX", scaleBefore, scaleAfter); 187 ObjectAnimator scaleY = new ObjectAnimator().ofFloat(view, "scaleY", scaleBefore, scaleAfter); 188 animatorList.add(scaleX); 189 animatorList.add(scaleY); 190 } catch (Exception ex) { 191 ex.printStackTrace(); 192 } 193 return animatorList; 194 } 195 196 protected List<Animator> getMoveAnimator(View newFocus, int factorX, int factorY) { 197 198 List<Animator> animatorList = new ArrayList<Animator>(); 199 int newXY[]; 200 int oldXY[]; 201 202 try { 203 204 newXY = getLocation(newFocus);//新的 205 oldXY = getLocation(mTarget); 206 207 int newWidth; 208 int newHeight; 209 int oldWidth = mTarget.getMeasuredWidth(); 210 int oldHeight = mTarget.getMeasuredHeight(); 211 212 if (mScalable) { 213 float scaleWidth = newFocus.getMeasuredWidth() * mScale; 214 float scaleHeight = newFocus.getMeasuredHeight() * mScale; 215 newWidth = (int) (scaleWidth + mMargin * 2 + 0.5); 216 newHeight = (int) (scaleHeight + mMargin * 2 + 0.5); 217 newXY[0] = (int) (newXY[0] - (newWidth - newFocus.getMeasuredWidth()) / 2.0f) + factorX; 218 newXY[1] = (int) (newXY[1] - (newHeight - newFocus.getMeasuredHeight()) / 2.0f + 0.5 + factorY); 219 220 } else { 221 newWidth = newFocus.getWidth(); 222 newHeight = newFocus.getHeight(); 223 } 224 if (oldHeight == 0 && oldWidth == 0) { 225 oldHeight = newHeight; 226 oldWidth = newWidth; 227 } 228 229 PropertyValuesHolder valuesWithdHolder = PropertyValuesHolder.ofInt("width", oldWidth, newWidth); 230 PropertyValuesHolder valuesHeightHolder = PropertyValuesHolder.ofInt("height", oldHeight, newHeight); 231 PropertyValuesHolder valuesXHolder = PropertyValuesHolder.ofFloat("translationX", oldXY[0], newXY[0]); 232 PropertyValuesHolder valuesYHolder = PropertyValuesHolder.ofFloat("translationY", oldXY[1], newXY[1]); 233 final ObjectAnimator scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(mTarget, valuesWithdHolder, valuesHeightHolder, valuesYHolder, valuesXHolder); 234 235 scaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 236 @Override 237 public synchronized void onAnimationUpdate(ValueAnimator animation) { 238 int width = (int) animation.getAnimatedValue("width"); 239 int height = (int) animation.getAnimatedValue("height"); 240 float translationX = (float) animation.getAnimatedValue("translationX"); 241 float translationY = (float) animation.getAnimatedValue("translationY"); 242 View view = (View) scaleAnimator.getTarget(); 243 assert view != null; 244 int w = view.getLayoutParams().width; 245 view.getLayoutParams().width = width; 246 view.getLayoutParams().height = height; 247 if (width > 0) { 248 view.requestLayout(); 249 view.postInvalidate(); 250 251 } 252 } 253 }); 254 animatorList.add(scaleAnimator); 255 } catch (Exception ex) { 256 ex.printStackTrace(); 257 } 258 return animatorList; 259 } 260 261 protected int[] getLocation(View view) { 262 int[] location = new int[2];//location[0]代表x坐标,location [1] 代表y坐标。 263 try { 264 //获取在整个屏幕内的绝对坐标,注意这个值是要从屏幕顶端算起,也就是包括了通知栏的高度。 265 view.getLocationOnScreen(location); 266 } catch (Exception ex) { 267 ex.printStackTrace(); 268 } 269 return location; 270 } 271 272 public void addOnFocusChanged(FocusListener focusListener) { 273 this.mFocusListener.add(focusListener); 274 } 275 276 public void removeOnFocusChanged(FocusListener focusListener) { 277 this.mFocusListener.remove(focusListener); 278 } 279 280 public void addAnimatorListener(Animator.AnimatorListener animatorListener) { 281 this.mAnimatorListener.add(animatorListener); 282 } 283 284 public void removeAnimatorListener(Animator.AnimatorListener animatorListener) { 285 this.mAnimatorListener.remove(animatorListener); 286 } 287 288 private class VisibleScope { 289 public boolean isVisible; 290 public View oldFocus; 291 public View newFocus; 292 } 293 294 protected VisibleScope checkVisibleScope(View oldFocus, View newFocus) { 295 VisibleScope scope = new VisibleScope(); 296 try { 297 scope.oldFocus = oldFocus; 298 scope.newFocus = newFocus; 299 scope.isVisible = true; 300 if (attacheViews.indexOf(oldFocus) >= 0 && attacheViews.indexOf(newFocus) >= 0) { 301 return scope; 302 } 303 304 if (oldFocus != null && newFocus != null) { 305 if (oldFocus.getParent() != newFocus.getParent()) { 306 if ((attacheViews.indexOf(newFocus.getParent()) < 0) || (attacheViews.indexOf(oldFocus.getParent()) < 0 && attacheViews.indexOf(newFocus.getParent()) > 0)) { 307 mTarget.setVisibility(View.INVISIBLE); 308 AnimatorSet animatorSet = new AnimatorSet(); 309 animatorSet.playTogether(getScaleAnimator(oldFocus, false)); 310 animatorSet.setDuration(0).start(); 311 scope.isVisible = false; 312 return scope; 313 } else { 314 mTarget.setVisibility(View.VISIBLE); 315 } 316 if (attacheViews.indexOf(oldFocus.getParent()) < 0) { 317 scope.oldFocus = null; 318 } 319 320 } else { 321 if (attacheViews.indexOf(newFocus.getParent()) < 0) { 322 mTarget.setVisibility(View.INVISIBLE); 323 scope.isVisible = false; 324 return scope; 325 } 326 } 327 } 328 mTarget.setVisibility(View.VISIBLE); 329 } catch (Exception ex) { 330 ex.printStackTrace(); 331 } 332 return scope; 333 } 334 335 @Override 336 public void onFocusChanged(View target, View oldFocus, View newFocus) { 337 try { 338 Log.d(TAG, "onFocusChanged:" + oldFocus + "=" + newFocus); 339 340 if (newFocus == null && attacheViews.indexOf(newFocus) >= 0) { 341 return; 342 } 343 if (oldFocus == newFocus) 344 return; 345 346 if (mAnimatorSet != null && mAnimatorSet.isRunning()) {//如果动画正在运行时 347 mAnimatorSet.end(); 348 } 349 350 lastFocus = newFocus; 351 oldLastFocus = oldFocus; 352 mTarget = target; 353 354 VisibleScope scope = checkVisibleScope(oldFocus, newFocus); 355 if (!scope.isVisible) { 356 return; 357 } else { 358 oldFocus = scope.oldFocus; 359 newFocus = scope.newFocus; 360 oldLastFocus = scope.oldFocus; 361 } 362 363 if (isScrolling || newFocus == null || newFocus.getWidth() <= 0 || newFocus.getHeight() <= 0) 364 return; 365 366 mAnimatorList.clear();//清除动画 367 368 for (FocusListener f : this.mFocusListener) { 369 f.onFocusChanged(oldFocus, newFocus); 370 } 371 372 } catch (Exception ex) { 373 ex.printStackTrace(); 374 } 375 } 376 377 378 @Override 379 public void onScrollChanged(View target, View attachView) { 380 try { 381 } catch (Exception ex) { 382 ex.printStackTrace(); 383 } 384 } 385 386 @Override 387 public void onLayout(View target, View attachView) { 388 try { 389 ViewGroup viewGroup = (ViewGroup) attachView.getRootView(); 390 if (target.getParent() != null && target.getParent() != viewGroup) { 391 target.setVisibility(View.GONE); 392 if (mFirstFocus) 393 viewGroup.requestFocus();//如果是首次获取焦点,强制变成焦点态 394 } 395 } catch (Exception ex) { 396 ex.printStackTrace(); 397 } 398 } 399 400 protected boolean mFirstFocus = true; 401 402 public void setFirstFocus(boolean b) { 403 this.mFirstFocus = b; 404 } 405 406 @Override 407 public void onTouchModeChanged(View target, View attachView, boolean isInTouchMode) { 408 try { 409 if (mEnableTouch && isInTouchMode) { 410 target.setVisibility(View.INVISIBLE); 411 if (lastFocus != null) { 412 AnimatorSet animatorSet = new AnimatorSet(); 413 animatorSet.playTogether(getScaleAnimator(lastFocus, false)); 414 animatorSet.setDuration(0).start(); 415 } 416 } 417 418 } catch (Exception ex) { 419 ex.printStackTrace(); 420 } 421 422 } 423 424 protected boolean isScrolling = false; 425 426 protected List<View> attacheViews = new ArrayList<>(); 427 protected Map<View, AdapterView.OnItemSelectedListener> onItemSelectedListenerList = new HashMap<>(); 428 429 430 @Override 431 public void onAttach(View target, View attachView) { 432 try { 433 mTarget = target; 434 435 if (target.getParent() != null && (target.getParent() instanceof ViewGroup)) { 436 ViewGroup vg = (ViewGroup) target.getParent(); 437 vg.removeView(target); 438 } 439 440 ViewGroup vg = (ViewGroup) attachView.getRootView(); 441 vg.addView(target); 442 443 target.setVisibility(View.GONE); 444 if (attachView instanceof RecyclerView) { 445 RecyclerView recyclerView = (RecyclerView) attachView; 446 RecyclerView.OnScrollListener recyclerViewOnScrollListener = null; 447 recyclerViewOnScrollListener = new RecyclerView.OnScrollListener() { 448 @Override 449 public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 450 try { 451 super.onScrollStateChanged(recyclerView, newState); 452 453 if (newState == RecyclerView.SCROLL_STATE_IDLE) { 454 isScrolling = false; 455 View oldFocus = oldLastFocus; 456 View newFocus = lastFocus; 457 VisibleScope scope = checkVisibleScope(oldFocus, newFocus); 458 if (!scope.isVisible) { 459 return; 460 } else { 461 oldFocus = scope.oldFocus; 462 newFocus = scope.newFocus; 463 } 464 AnimatorSet animatorSet = new AnimatorSet(); 465 List<Animator> list = new ArrayList<>(); 466 list.addAll(getScaleAnimator(newFocus, true)); 467 list.addAll(getMoveAnimator(newFocus, 0, 0)); 468 animatorSet.setDuration(mDurationTraslate); 469 animatorSet.playTogether(list); 470 animatorSet.start(); 471 472 473 } else if (newState == RecyclerView.SCROLL_STATE_SETTLING) { 474 isScrolling = true; 475 if (lastFocus != null) { 476 List<Animator> list = getScaleAnimator(lastFocus, false); 477 AnimatorSet animatorSet = new AnimatorSet(); 478 animatorSet.setDuration(150); 479 animatorSet.playTogether(list); 480 animatorSet.start(); 481 } 482 } 483 } catch (Exception ex) { 484 485 } 486 } 487 }; 488 recyclerView.addOnScrollListener(recyclerViewOnScrollListener); 489 } else if (attachView instanceof AbsListView) { 490 491 final AbsListView absListView = (AbsListView) attachView; 492 final AdapterView.OnItemSelectedListener onItemSelectedListener = absListView.getOnItemSelectedListener(); 493 494 View temp = null; 495 if (absListView.getChildCount() > 0) { 496 temp = absListView.getChildAt(0); 497 } 498 final View tempFocus = temp; 499 MyOnItemSelectedListener myOnItemSelectedListener = new MyOnItemSelectedListener(); 500 myOnItemSelectedListener.onItemSelectedListener = onItemSelectedListener; 501 myOnItemSelectedListener.oldFocus = temp; 502 absListView.setOnItemSelectedListener(myOnItemSelectedListener); 503 onItemSelectedListenerList.put(attachView, myOnItemSelectedListener); 504 505 } 506 507 attacheViews.add(attachView); 508 } catch (Exception ex) { 509 ex.printStackTrace(); 510 } 511 512 } 513 514 protected class MyOnItemSelectedListener implements AdapterView.OnItemSelectedListener { 515 public View oldFocus = null; 516 public View newFocus = null; 517 public AnimatorSet animatorSet; 518 public AdapterView.OnItemSelectedListener onItemSelectedListener; 519 520 @Override 521 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 522 try { 523 if (onItemSelectedListener != null && parent != null) { 524 onItemSelectedListener.onItemSelected(parent, view, position, id); 525 } 526 if (newFocus == null) 527 return; 528 newFocus = view; 529 530 Rect rect = new Rect(); 531 view.getLocalVisibleRect(rect); 532 ViewGroup vg = (ViewGroup) newFocus.getParent(); 533 534 int factorX = 0, factorY = 0; 535 if (Math.abs(rect.left - rect.right) > newFocus.getMeasuredWidth()) { 536 factorX = (Math.abs(rect.left - rect.right) - newFocus.getMeasuredWidth()) / 2 - 1; 537 factorY = (Math.abs(rect.top - rect.bottom) - newFocus.getMeasuredHeight()) / 2; 538 539 } 540 541 542 List<Animator> animatorList = new ArrayList<Animator>(3); 543 animatorList.addAll(getScaleAnimator(newFocus, true)); 544 if (oldFocus != null) 545 animatorList.addAll(getScaleAnimator(oldFocus, false)); 546 animatorList.addAll(getMoveAnimator(newFocus, factorX, factorY)); 547 mTarget.setVisibility(View.VISIBLE); 548 549 if (animatorSet != null && animatorSet.isRunning()) 550 animatorSet.end(); 551 animatorSet = new AnimatorSet(); 552 animatorSet.setDuration(mDurationTraslate); 553 animatorSet.playTogether(animatorList); 554 animatorSet.start(); 555 556 557 oldFocus = newFocus; 558 } catch (Exception ex) { 559 ex.printStackTrace(); 560 } 561 562 } 563 564 @Override 565 public void onNothingSelected(AdapterView<?> parent) { 566 if (onItemSelectedListener != null) { 567 onItemSelectedListener.onNothingSelected(parent); 568 } 569 } 570 } 571 572 573 @Override 574 public void OnDetach(View targe, View view) { 575 if (targe.getParent() == view) { 576 ((ViewGroup) view).removeView(targe); 577 } 578 579 attacheViews.remove(view); 580 } 581 582 public void setEnableTouch(boolean enableTouch) { 583 this.mEnableTouch = enableTouch; 584 } 585 586 public boolean isScalable() { 587 return mScalable; 588 } 589 590 public void setScalable(boolean scalable) { 591 this.mScalable = scalable; 592 } 593 594 public float getScale() { 595 return mScale; 596 } 597 598 public void setScale(float scale) { 599 this.mScale = scale; 600 } 601 602 public int getMargin() { 603 return mMargin; 604 } 605 606 public void setMargin(int mMargin) { 607 this.mMargin = mMargin; 608 } 609 610}

MetroViewBorder用于对焦点变化处理及滚动处理

1public class MetroViewBorderImpl<X extends View> implements ViewTreeObserver.OnGlobalFocusChangeListener, ViewTreeObserver.OnScrollChangedListener, ViewTreeObserver.OnGlobalLayoutListener, ViewTreeObserver.OnTouchModeChangeListener { 2 3 private static final String TAG = MetroViewBorderImpl.class.getSimpleName(); 4 5 private ViewGroup mViewGroup; 6 private IMetroViewBorder mMetroViewBorder; 7 8 private X mView; 9 private View mLastView; 10 11 public MetroViewBorderImpl(Context context) { 12 this(context, null, 0); 13 } 14 15 public MetroViewBorderImpl(Context context, AttributeSet attrs) { 16 this(context, attrs, 0); 17 } 18 19 public MetroViewBorderImpl(Context context, AttributeSet attrs, int defStyleAttr) { 20 init(context, attrs, defStyleAttr); 21 } 22 23 private void init(Context context, AttributeSet attrs, int defStyleAttr) { 24 mMetroViewBorder = new MetroViewBorderHandler(); 25 mView = (X) new View(context, attrs, defStyleAttr); 26 } 27 28 public MetroViewBorderImpl(X view) { 29 this.mView = view; 30 mMetroViewBorder = new MetroViewBorderHandler(); 31 } 32 33 public MetroViewBorderImpl(X view, IMetroViewBorder border) { 34 this.mView = view; 35 mMetroViewBorder = border; 36 } 37 38 public MetroViewBorderImpl(Context context, int resId) { 39 this((X) LayoutInflater.from(context).inflate(resId, null, false)); 40 } 41 42 public X getView() { 43 return mView; 44 } 45 46 47 public void setBackgroundResource(int resId) { 48 if (mView != null) 49 mView.setBackgroundResource(resId); 50 } 51 52 @Override 53 public void onScrollChanged() { 54 mMetroViewBorder.onScrollChanged(mView, mViewGroup); 55 } 56 57 @Override 58 public void onGlobalLayout() { 59 mMetroViewBorder.onLayout(mView, mViewGroup); 60 } 61 62 @Override 63 public void onTouchModeChanged(boolean isInTouchMode) { 64 mMetroViewBorder.onTouchModeChanged(mView, mViewGroup, isInTouchMode); 65 } 66 67 @Override 68 public void onGlobalFocusChanged(View oldFocus, View newFocus) { 69 try { 70 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2) {// 4.3 71 if (oldFocus == null && mLastView != null) { 72 oldFocus = mLastView; 73 } 74 } 75 if (mMetroViewBorder != null) 76 mMetroViewBorder.onFocusChanged(mView, oldFocus, newFocus); 77 mLastView = newFocus; 78 79 } catch (Exception ex) { 80 ex.printStackTrace(); 81 } 82 83 } 84 85 86 public <T extends MetroViewBorderHandler> T getViewBorder() { 87 return (T) mMetroViewBorder; 88 } 89 90 public void setBorder(IMetroViewBorder border) { 91 this.mMetroViewBorder = border; 92 } 93 94 public void attachTo(ViewGroup viewGroup) { 95 try { 96 if (viewGroup == null) { 97 if (mView.getContext() instanceof Activity) { 98 Activity activity = (Activity) mView.getContext(); 99 viewGroup = (ViewGroup) activity.getWindow().getDecorView().getRootView();//获取顶层view 100 } 101 } 102 103 if (mViewGroup != viewGroup) { 104 105 ViewTreeObserver viewTreeObserver = viewGroup.getViewTreeObserver(); 106 if (viewTreeObserver.isAlive() && mViewGroup == null) { 107 viewTreeObserver.addOnGlobalFocusChangeListener(this); 108 viewTreeObserver.addOnScrollChangedListener(this); 109 viewTreeObserver.addOnGlobalLayoutListener(this); 110 viewTreeObserver.addOnTouchModeChangeListener(this); 111 } 112 mViewGroup = viewGroup; 113 } 114 mMetroViewBorder.onAttach(mView, viewGroup); 115 116 117 } catch (Exception ex) { 118 ex.printStackTrace(); 119 } 120 } 121 122 public void detach() { 123 detachFrom(mViewGroup); 124 } 125 126 public void detachFrom(ViewGroup viewGroup) { 127 try { 128 if (viewGroup == mViewGroup) { 129 ViewTreeObserver viewTreeObserver = mViewGroup.getViewTreeObserver();//获取view树的观察者 130 viewTreeObserver.removeOnGlobalFocusChangeListener(this);//通知全局性移除相应的listener 131 viewTreeObserver.removeOnScrollChangedListener(this); 132 viewTreeObserver.removeOnGlobalLayoutListener(this); 133 viewTreeObserver.removeOnTouchModeChangeListener(this); 134 mMetroViewBorder.OnDetach(mView, viewGroup); 135 } 136 } catch (Exception ex) { 137 ex.printStackTrace(); 138 } 139 } 140}

第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。

这里写图片描述

如果你觉得好,随手点赞,也是对笔者的肯定,也可以分享此公众号给你更多的人,原创不易

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )