您当前的位置: 首页 > 

暂无认证

  • 3浏览

    0关注

    95281博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

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

发布时间:2016-10-15 14:10:06 ,浏览量:3

转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼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
public class CornerVew extends View { private String mTextContent;//显示字体内容 private int mTextColor;//字体颜色 private float mTextSize;//字体大小 private boolean mTextBold;//是否字体加粗 private boolean mFillTriangle;//是否三角形 private boolean mTextAllCaps;// private int mBackgroundColor;//背景颜色 private float mMinSize;//最小值 private float mPadding;//padding值 private int mGravity;// private static final int DEFAULT_DEGREES = 45;//切角 private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿 private Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private Path mPath = new Path(); public CornerVew(Context context) { this(context, null);
    } public CornerVew(Context context, AttributeSet attrs) { super(context, attrs);

        obtainAttributes(context, attrs);
        mTextPaint.setTextAlign(Paint.Align.CENTER);//从中间开始画 } private void obtainAttributes(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CornerVew);
        mTextContent = ta.getString(R.styleable.CornerVew_cv_text);
        mTextColor = ta.getColor(R.styleable.CornerVew_cv_text_color, Color.parseColor("#ffffff"));
        mTextSize = ta.getDimension(R.styleable.CornerVew_cv_text_size, sp2px(11));
        mTextBold = ta.getBoolean(R.styleable.CornerVew_cv_text_bold, true);//加粗 mTextAllCaps = ta.getBoolean(R.styleable.CornerVew_cv_text_all_caps, true);
        mFillTriangle = ta.getBoolean(R.styleable.CornerVew_cv_fill_triangle, false);
        mBackgroundColor = ta.getColor(R.styleable.CornerVew_cv_background_color, Color.parseColor("#FF4081"));
        mMinSize = ta.getDimension(R.styleable.CornerVew_cv_min_size, mFillTriangle ? dp2px(35) : dp2px(50));
        mPadding = ta.getDimension(R.styleable.CornerVew_cv_padding, dp2px(3.5f));
        mGravity = ta.getInt(R.styleable.CornerVew_cv_gravity, Gravity.TOP | Gravity.LEFT);
        ta.recycle();
    } @Override protected void onDraw(Canvas canvas) { int size = getHeight();

        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setFakeBoldText(mTextBold);
        mBackgroundPaint.setColor(mBackgroundColor); float textHeight = mTextPaint.descent() - mTextPaint.ascent(); if (mFillTriangle) { if (mGravity == (Gravity.TOP | Gravity.LEFT)) {//左上角 mPath.reset();
                mPath.moveTo(0, 0);
                mPath.lineTo(0, size);
                mPath.lineTo(size, 0);
                mPath.close();
                canvas.drawPath(mPath, mBackgroundPaint);

                drawTextWhenFill(size, -DEFAULT_DEGREES, canvas, true);
            } else if (mGravity == (Gravity.TOP | Gravity.RIGHT)) {//右上角 mPath.reset();
                mPath.moveTo(size, 0);
                mPath.lineTo(0, 0);
                mPath.lineTo(size, size);
                mPath.close();
                canvas.drawPath(mPath, mBackgroundPaint);

                drawTextWhenFill(size, DEFAULT_DEGREES, canvas, true);
            } else if (mGravity == (Gravity.BOTTOM | Gravity.LEFT)) {//左下角 mPath.reset();
                mPath.moveTo(0, size);
                mPath.lineTo(0, 0);
                mPath.lineTo(size, size);
                mPath.close();
                canvas.drawPath(mPath, mBackgroundPaint);

                drawTextWhenFill(size, DEFAULT_DEGREES, canvas, false);
            } else if (mGravity == (Gravity.BOTTOM | Gravity.RIGHT)) {//右下角 mPath.reset();
                mPath.moveTo(size, size);
                mPath.lineTo(0, size);
                mPath.lineTo(size, 0);
                mPath.close();
                canvas.drawPath(mPath, mBackgroundPaint);

                drawTextWhenFill(size, -DEFAULT_DEGREES, canvas, false);
            }
        } else { double delta = (textHeight + mPadding * 2) * Math.sqrt(2); if (mGravity == (Gravity.TOP | Gravity.LEFT)) {
                mPath.reset();
                mPath.moveTo(0, (float) (size - delta));
                mPath.lineTo(0, size);
                mPath.lineTo(size, 0);
                mPath.lineTo((float) (size - delta), 0);
                mPath.close();
                canvas.drawPath(mPath, mBackgroundPaint);

                drawText(size, -DEFAULT_DEGREES, canvas, textHeight, true);
            } else if (mGravity == (Gravity.TOP | Gravity.RIGHT)) {
                mPath.reset();
                mPath.moveTo(0, 0);
                mPath.lineTo((float) delta, 0);
                mPath.lineTo(size, (float) (size - delta));
                mPath.lineTo(size, size);
                mPath.close();
                canvas.drawPath(mPath, mBackgroundPaint);

                drawText(size, DEFAULT_DEGREES, canvas, textHeight, true);
            } else if (mGravity == (Gravity.BOTTOM | Gravity.LEFT)) {
                mPath.reset();
                mPath.moveTo(0, 0);
                mPath.lineTo(0, (float) delta);
                mPath.lineTo((float) (size - delta), size);
                mPath.lineTo(size, size);
                mPath.close();
                canvas.drawPath(mPath, mBackgroundPaint);

                drawText(size, DEFAULT_DEGREES, canvas, textHeight, false);
            } else if (mGravity == (Gravity.BOTTOM | Gravity.RIGHT)) {
                mPath.reset();
                mPath.moveTo(0, size);
                mPath.lineTo((float) delta, size);
                mPath.lineTo(size, (float) delta);
                mPath.lineTo(size, 0);
                mPath.close();
                canvas.drawPath(mPath, mBackgroundPaint);

                drawText(size, -DEFAULT_DEGREES, canvas, textHeight, false);
            }
        }
    } private void drawText(int size, float degrees, Canvas canvas, float textHeight, boolean isTop) {
        canvas.save();
        canvas.rotate(degrees, size / 2f, size / 2f); float delta = isTop ? -(textHeight + mPadding * 2) / 2 : (textHeight + mPadding * 2) / 2; float textBaseY = size / 2 - (mTextPaint.descent() + mTextPaint.ascent()) / 2 + delta;
        canvas.drawText(mTextAllCaps ? mTextContent.toUpperCase() : mTextContent,
                getPaddingLeft() + (size - getPaddingLeft() - getPaddingRight()) / 2, textBaseY, mTextPaint);
        canvas.restore();
    } private void drawTextWhenFill(int size, float degrees, Canvas canvas, boolean isTop) {
        canvas.save();
        canvas.rotate(degrees, size / 2f, size / 2f); float delta = isTop ? -size / 4 : size / 4; float textBaseY = size / 2 - (mTextPaint.descent() + mTextPaint.ascent()) / 2 + delta;
        canvas.drawText(mTextAllCaps ? mTextContent.toUpperCase() : mTextContent,
                getPaddingLeft() + (size - getPaddingLeft() - getPaddingRight()) / 2, textBaseY, mTextPaint);
        canvas.restore();
    } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int measuredWidth = measureWidth(widthMeasureSpec);
        setMeasuredDimension(measuredWidth, measuredWidth);
    } private int measureWidth(int widthMeasureSpec) { int result; int specMode = MeasureSpec.getMode(widthMeasureSpec);//获取测量模式 int specSize = MeasureSpec.getSize(widthMeasureSpec);//获取测量大小 if (specMode == MeasureSpec.EXACTLY) {//精确模式 result = specSize;
        } else { int padding = getPaddingLeft() + getPaddingRight();
            mTextPaint.setColor(mTextColor);
            mTextPaint.setTextSize(mTextSize); float textWidth = mTextPaint.measureText(mTextContent + "");
            result = (int) ((padding + (int) textWidth) * Math.sqrt(2)); if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
            result = Math.max((int) mMinSize, result);
        } return result;
    } protected int dp2px(float dp) { final float scale = getResources().getDisplayMetrics().density; return (int) (dp * scale + 0.5f);
    } protected int sp2px(float sp) { final float scale = getResources().getDisplayMetrics().scaledDensity; return (int) (sp * scale + 0.5f);
    }
}
绘制子视图顺序的Layout
public class DrawingOrderRelativeLayout extends RelativeLayout { private int position = 0; public DrawingOrderRelativeLayout(Context context) { super(context);
    } public DrawingOrderRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); this.setChildrenDrawingOrderEnabled(true);
    } public void setCurrentPosition(int pos) { this.position = pos;
    } @Override public boolean dispatchKeyEvent(KeyEvent event) {
            View focused = findFocus();//找到焦点 int pos = indexOfChild(focused); if (pos >= 0 && pos < getChildCount()) {
                setCurrentPosition(pos);
                postInvalidate();
            } return super.dispatchKeyEvent(event);
    } @Override protected int getChildDrawingOrder(int childCount, int i) {//改变ViewGroup的子视图绘制顺序 View v = getFocusedChild(); int pos = indexOfChild(v); if (pos >= 0 && pos < childCount)
            setCurrentPosition(pos); if (i == childCount - 1) {//从最后一个view开始绘制 return position;
        } if (i == position) { return childCount - 1;
        } return i;
    }

}
MetroItem
public class MetroItemFrameLayout extends FrameLayout implements IMetroItemRound { private MetroItemRound mMetroItemRound; public MetroItemFrameLayout(Context context) { super(context);
        init(context, null, 0);
    } public MetroItemFrameLayout(Context context, AttributeSet attrs) { super(context, attrs);
        init(context, attrs, 0);
    } public MetroItemFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    } private void init(Context context, AttributeSet attrs, int defStyle) {
        mMetroItemRound = new MetroItemRound(this, context, attrs, defStyle);
        setWillNotDraw(false);//设置为false时,就会调用自定义的布局 } @Override public void draw(Canvas canvas) {
        mMetroItemRound.draw(canvas);
    } @Override public MetroItemRound getRoundImpl() { return mMetroItemRound;
    } @Override public void drawRadious(Canvas canvas) { super.draw(canvas);
    }
}
MetroItem的圆角
public class MetroItemRound { private float mRadius;//圆角 private float mTopLeftRadius;//左上角的圆角 private float mTopRightRadius;//右上角的圆角 private float mBottomLeftRadius;//左下角的圆角 private float mBottomRightRadius;//右下角的圆角 private Paint mPaint;//画笔 private Path mPath;//画布 private IMetroItemRound mView; public MetroItemRound(IMetroItemRound view, Context context, AttributeSet attrs, int defStyle) {
        init(context, attrs, defStyle);
        mView = view;
    } private void init(Context context, AttributeSet attrs, int defStyle) {
        mPaint = new Paint();//实例化画笔 mPaint.setColor(Color.WHITE);//设置画笔颜色为白色 mPaint.setAntiAlias(true);//设置抗锯齿 mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));//取下层绘制非交集部分 mPath = new Path();//设置路径 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MetroItemRound, defStyle, 0);
        mBottomLeftRadius = a.getDimension(R.styleable.MetroItemRound_bottomLeftRadius, -1);
        mBottomRightRadius = a.getDimension(R.styleable.MetroItemRound_bottomRightRadius, -1);
        mTopLeftRadius = a.getDimension(R.styleable.MetroItemRound_topLeftRadius, -1);
        mTopRightRadius = a.getDimension(R.styleable.MetroItemRound_topRightRadius, -1);
        mRadius = a.getDimension(R.styleable.MetroItemRound_radius, -1); if (mRadius > 0) { if (mBottomLeftRadius < 0)
                mBottomLeftRadius = mRadius; if (mBottomRightRadius < 0)
                mBottomRightRadius = mRadius; if (mTopLeftRadius < 0)
                mTopLeftRadius = mRadius; if (mTopRightRadius < 0)
                mTopRightRadius = mRadius;
        }
        a.recycle();

    } public void draw(Canvas canvas) { if (mBottomLeftRadius <= 0 && mBottomRightRadius <= 0 && mTopRightRadius <= 0 && mTopLeftRadius <= 0) {
            mView.drawRadious(canvas); return;
        } int width = mView.getWidth(); int height = mView.getHeight(); int count = canvas.save(); int count2 = canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG);

        addRoundPath(width, height);
        mView.drawRadious(canvas);//画一个圆角 canvas.drawPath(mPath, mPaint);
        canvas.restoreToCount(count2);
        canvas.restoreToCount(count);

    } private void addRoundPath(int width, int height) { //topleft path if (mTopLeftRadius > 0) {
            Path topLeftPath = new Path();
            topLeftPath.moveTo(0, mTopLeftRadius);//移动画笔到最左边x为0,y为mTopLeftRadius topLeftPath.lineTo(0, 0);//绘制直线,从坐标0,0开始 topLeftPath.lineTo(mTopLeftRadius, 0);// RectF arc = new RectF(0, 0, mTopLeftRadius * 2, mTopLeftRadius * 2);//矩形区域 //和Rect的区别是RectF表示精确到浮点型 topLeftPath.arcTo(arc, -90, -90);//画弧线的路径,从-90到-90,逆时针画 topLeftPath.close();// mPath.addPath(topLeftPath);
        } //topRight path if (mTopRightRadius > 0) {
            Path topRightPath = new Path();
            topRightPath.moveTo(width, mTopRightRadius);//移动画笔到最右边x为width,y为mTopRightRadius topRightPath.lineTo(width, 0);
            topRightPath.lineTo(width - mTopRightRadius, 0);
            RectF arc = new RectF(width - mTopRightRadius * 2, 0, width, mTopRightRadius * 2);

            topRightPath.arcTo(arc, -90, 90);//这里是画180的椭圆 topRightPath.close();

            mPath.addPath(topRightPath);

        } //bottomLeft path if (mBottomLeftRadius > 0) {
            Path bottomLeftPath = new Path();
            bottomLeftPath.moveTo(0, height - mBottomLeftRadius);
            bottomLeftPath.lineTo(0, height);
            bottomLeftPath.lineTo(mBottomLeftRadius, height);
            RectF arc = new RectF(0, height - mBottomLeftRadius * 2, mBottomLeftRadius * 2, height);

            bottomLeftPath.arcTo(arc, 90, 90);
            bottomLeftPath.close();
            mPath.addPath(bottomLeftPath);
        } //bottomRight path if (mBottomRightRadius > 0) {
            Path bottomRightPath = new Path();
            bottomRightPath.moveTo(width - mBottomRightRadius, height);
            bottomRightPath.lineTo(width, height);
            bottomRightPath.lineTo(width, height - mBottomRightRadius);
            RectF arc = new RectF(width - mBottomRightRadius * 2, height - mBottomRightRadius * 2, width, height);
            bottomRightPath.arcTo(arc, 0, 90);
            bottomRightPath.close();
            mPath.addPath(bottomRightPath);
        }

    }
}
MetroItem中外边框处理类
public class MetroViewBorderHandler implements IMetroViewBorder { private String TAG = MetroViewBorderHandler.class.getSimpleName(); protected boolean mScalable = true;//是否缩小 protected float mScale = 1.1f;//设置放大比例 protected long mDurationTraslate = 200;//焦点移动的动画时间 protected int mMargin = 0; protected View lastFocus, oldLastFocus;//上一个焦点,新的位置的焦点 protected AnimatorSet mAnimatorSet;//动画集合,可组合多个动画 protected ListmAnimatorList = new ArrayList(); protected View mTarget; protected boolean mEnableTouch = true; public MetroViewBorderHandler() {
        mFocusListener.add(mFocusMoveListener);//设置焦点移动时监听 mFocusListener.add(mFocusScaleListener);//设置焦点放大缩小监听 mFocusListener.add(mFocusPlayListener);
        mFocusListener.add(mAbsListViewFocusListener);//用作listview,gridview相关 } public interface FocusListener { void onFocusChanged(View oldFocus, View newFocus);
    } protected ListmFocusListener = new ArrayList(1); protected List mAnimatorListener = new ArrayList(1); public FocusListener mFocusScaleListener = new FocusListener() { @Override public void onFocusChanged(View oldFocus, View newFocus) { //焦点变化,设置新焦点移动的位置变成放大状态 mAnimatorList.addAll(getScaleAnimator(newFocus, true)); if (oldFocus != null) { //上一个view(之前是焦点态,onFocusChanged变成非焦点态)不为null,设置上一个为缩小状态 mAnimatorList.addAll(getScaleAnimator(oldFocus, false));
            }
        }
    }; public FocusListener mFocusPlayListener = new FocusListener() { @Override public void onFocusChanged(View oldFocus, View newFocus) { try { if (newFocus instanceof AbsListView) {//如果新的view是AbsListView的实例,直接return return;
                }
                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.setInterpolator(new DecelerateInterpolator(1));//设置插值器 animatorSet.setDuration(mDurationTraslate);//设置动画时间 animatorSet.playTogether(mAnimatorList);//表示两个动画同进执行 for (Animator.AnimatorListener listener : mAnimatorListener) {
                    animatorSet.addListener(listener);
                }
                mAnimatorSet = animatorSet; if (oldFocus == null) {//之前view为null,表示首次状态时 animatorSet.setDuration(0);//无动画时长 mTarget.setVisibility(View.VISIBLE);
                }
                animatorSet.start();//开启动画 } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }; public FocusListener mFocusMoveListener = new FocusListener() { @Override public void onFocusChanged(View oldFocus, View newFocus) { if (newFocus == null) return;//下一个view不存在时,直接return try {
                mAnimatorList.addAll(getMoveAnimator(newFocus, 0, 0));//添加 } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }; public FocusListener mAbsListViewFocusListener = new FocusListener() { @Override public void onFocusChanged(View oldFocus, View newFocus) { try { if (oldFocus == null) { for (int i = 0; i < attacheViews.size(); i++) {
                        View view = attacheViews.get(i); if (view instanceof AbsListView) { final AbsListView absListView = (AbsListView) view;
                            mTarget.setVisibility(View.INVISIBLE); if (mFirstFocus) {
                                absListView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { try {
                                            absListView.removeOnLayoutChangeListener(this); int factorX = 0, factorY = 0;
                                            Rect rect = new Rect();
                                            View firstView = (View) absListView.getSelectedView();
                                            firstView.getLocalVisibleRect(rect); if (Math.abs(rect.left - rect.right) > firstView.getMeasuredWidth()) {
                                                factorX = (Math.abs(rect.left - rect.right) - firstView.getMeasuredWidth()) / 2 - 1;
                                                factorY = (Math.abs(rect.top - rect.bottom) - firstView.getMeasuredHeight()) / 2;
                                            }
                                            ListanimatorList = new ArrayList(3);
                                            animatorList.addAll(getScaleAnimator(firstView, true));
                                            animatorList.addAll(getMoveAnimator(firstView, factorX, factorY));
                                            mTarget.setVisibility(View.VISIBLE);
                                            AnimatorSet animatorSet = new AnimatorSet();
                                            animatorSet.setDuration(0);
                                            animatorSet.playTogether(animatorList);
                                            animatorSet.start();
                                        } catch (Exception ex) {
                                            ex.printStackTrace();
                                        }
                                    }
                                });


                            } break;
                        }
                    }
                } else if (oldFocus instanceof AbsListView && newFocus instanceof AbsListView) { if (attacheViews.indexOf(oldFocus) >= 0 && attacheViews.indexOf(newFocus) >= 0) {

                        AbsListView a = (AbsListView) oldFocus;
                        AbsListView b = (AbsListView) newFocus;

                        MyOnItemSelectedListener oldOn = (MyOnItemSelectedListener) onItemSelectedListenerList.get(oldFocus);
                        MyOnItemSelectedListener newOn = (MyOnItemSelectedListener) onItemSelectedListenerList.get(newFocus); int factorX = 0, factorY = 0;
                        Rect rect = new Rect();
                        View firstView = (View) b.getSelectedView();
                        firstView.getLocalVisibleRect(rect); if (Math.abs(rect.left - rect.right) > firstView.getMeasuredWidth()) {
                            factorX = (Math.abs(rect.left - rect.right) - firstView.getMeasuredWidth()) / 2 - 1;
                            factorY = (Math.abs(rect.top - rect.bottom) - firstView.getMeasuredHeight()) / 2;
                        }

                        ListanimatorList = new ArrayList(3);
                        animatorList.addAll(getScaleAnimator(firstView, true));
                        animatorList.addAll(getScaleAnimator(a.getSelectedView(), false));

                        animatorList.addAll(getMoveAnimator(firstView, factorX, factorY));
                        mTarget.setVisibility(View.VISIBLE);

                        mAnimatorSet = new AnimatorSet();


                        mAnimatorSet.setDuration(mDurationTraslate);
                        mAnimatorSet.playTogether(animatorList);
                        mAnimatorSet.start();

                        oldOn.oldFocus = null;
                        oldOn.newFocus = null;

                        newOn.oldFocus = null; if (newOn.newFocus != null && newOn.oldFocus != null) {
                            newOn.newFocus = null;
                        } else {
                            newOn.newFocus = b.getSelectedView();
                        }

                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }; protected ListgetScaleAnimator(View view, boolean isScale) {

        ListanimatorList = new ArrayList(2); if (!mScalable) return animatorList;//如果没有放大,直接返回 try { float scaleBefore = 1.0f;//放大前比例 float scaleAfter = mScale;//放大后比例 if (!isScale) {// scaleBefore = mScale;
                scaleAfter = 1.0f;
            }
            ObjectAnimator scaleX = new ObjectAnimator().ofFloat(view, "scaleX", scaleBefore, scaleAfter);
            ObjectAnimator scaleY = new ObjectAnimator().ofFloat(view, "scaleY", scaleBefore, scaleAfter);
            animatorList.add(scaleX);
            animatorList.add(scaleY);
        } catch (Exception ex) {
            ex.printStackTrace();
        } return animatorList;
    } protected ListgetMoveAnimator(View newFocus, int factorX, int factorY) {

        ListanimatorList = new ArrayList(); int newXY[]; int oldXY[]; try {

            newXY = getLocation(newFocus);//新的 oldXY = getLocation(mTarget); int newWidth; int newHeight; int oldWidth = mTarget.getMeasuredWidth(); int oldHeight = mTarget.getMeasuredHeight(); if (mScalable) { float scaleWidth = newFocus.getMeasuredWidth() * mScale; float scaleHeight = newFocus.getMeasuredHeight() * mScale;
                newWidth = (int) (scaleWidth + mMargin * 2 + 0.5);
                newHeight = (int) (scaleHeight + mMargin * 2 + 0.5);
                newXY[0] = (int) (newXY[0] - (newWidth - newFocus.getMeasuredWidth()) / 2.0f) + factorX;
                newXY[1] = (int) (newXY[1] - (newHeight - newFocus.getMeasuredHeight()) / 2.0f + 0.5 + factorY);

            } else {
                newWidth = newFocus.getWidth();
                newHeight = newFocus.getHeight();
            } if (oldHeight == 0 && oldWidth == 0) {
                oldHeight = newHeight;
                oldWidth = newWidth;
            }

            PropertyValuesHolder valuesWithdHolder = PropertyValuesHolder.ofInt("width", oldWidth, newWidth);
            PropertyValuesHolder valuesHeightHolder = PropertyValuesHolder.ofInt("height", oldHeight, newHeight);
            PropertyValuesHolder valuesXHolder = PropertyValuesHolder.ofFloat("translationX", oldXY[0], newXY[0]);
            PropertyValuesHolder valuesYHolder = PropertyValuesHolder.ofFloat("translationY", oldXY[1], newXY[1]); final ObjectAnimator scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(mTarget, valuesWithdHolder, valuesHeightHolder, valuesYHolder, valuesXHolder);

            scaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public synchronized void onAnimationUpdate(ValueAnimator animation) { int width = (int) animation.getAnimatedValue("width"); int height = (int) animation.getAnimatedValue("height"); float translationX = (float) animation.getAnimatedValue("translationX"); float translationY = (float) animation.getAnimatedValue("translationY");
                    View view = (View) scaleAnimator.getTarget(); assert view != null; int w = view.getLayoutParams().width;
                    view.getLayoutParams().width = width;
                    view.getLayoutParams().height = height; if (width > 0) {
                        view.requestLayout();
                        view.postInvalidate();

                    }
                }
            });
            animatorList.add(scaleAnimator);
        } catch (Exception ex) {
            ex.printStackTrace();
        } return animatorList;
    } protected int[] getLocation(View view) { int[] location = new int[2];//location[0]代表x坐标,location [1] 代表y坐标。 try { //获取在整个屏幕内的绝对坐标,注意这个值是要从屏幕顶端算起,也就是包括了通知栏的高度。 view.getLocationOnScreen(location);
        } catch (Exception ex) {
            ex.printStackTrace();
        } return location;
    } public void addOnFocusChanged(FocusListener focusListener) { this.mFocusListener.add(focusListener);
    } public void removeOnFocusChanged(FocusListener focusListener) { this.mFocusListener.remove(focusListener);
    } public void addAnimatorListener(Animator.AnimatorListener animatorListener) { this.mAnimatorListener.add(animatorListener);
    } public void removeAnimatorListener(Animator.AnimatorListener animatorListener) { this.mAnimatorListener.remove(animatorListener);
    } private class VisibleScope { public boolean isVisible; public View oldFocus; public View newFocus;
    } protected VisibleScope checkVisibleScope(View oldFocus, View newFocus) {
        VisibleScope scope = new VisibleScope(); try {
            scope.oldFocus = oldFocus;
            scope.newFocus = newFocus;
            scope.isVisible = true; if (attacheViews.indexOf(oldFocus) >= 0 && attacheViews.indexOf(newFocus) >= 0) { return scope;
            } if (oldFocus != null && newFocus != null) { if (oldFocus.getParent() != newFocus.getParent()) { if ((attacheViews.indexOf(newFocus.getParent()) < 0) || (attacheViews.indexOf(oldFocus.getParent()) < 0 && attacheViews.indexOf(newFocus.getParent()) > 0)) {
                        mTarget.setVisibility(View.INVISIBLE);
                        AnimatorSet animatorSet = new AnimatorSet();
                        animatorSet.playTogether(getScaleAnimator(oldFocus, false));
                        animatorSet.setDuration(0).start();
                        scope.isVisible = false; return scope;
                    } else {
                        mTarget.setVisibility(View.VISIBLE);
                    } if (attacheViews.indexOf(oldFocus.getParent()) < 0) {
                        scope.oldFocus = null;
                    }

                } else { if (attacheViews.indexOf(newFocus.getParent()) < 0) {
                        mTarget.setVisibility(View.INVISIBLE);
                        scope.isVisible = false; return scope;
                    }
                }
            }
            mTarget.setVisibility(View.VISIBLE);
        } catch (Exception ex) {
            ex.printStackTrace();
        } return scope;
    } @Override public void onFocusChanged(View target, View oldFocus, View newFocus) { try {
            Log.d(TAG, "onFocusChanged:" + oldFocus + "=" + newFocus); if (newFocus == null && attacheViews.indexOf(newFocus) >= 0) { return;
            } if (oldFocus == newFocus) return; if (mAnimatorSet != null && mAnimatorSet.isRunning()) {//如果动画正在运行时 mAnimatorSet.end();
            }

            lastFocus = newFocus;
            oldLastFocus = oldFocus;
            mTarget = target;

            VisibleScope scope = checkVisibleScope(oldFocus, newFocus); if (!scope.isVisible) { return;
            } else {
                oldFocus = scope.oldFocus;
                newFocus = scope.newFocus;
                oldLastFocus = scope.oldFocus;
            } if (isScrolling || newFocus == null || newFocus.getWidth() <= 0 || newFocus.getHeight() <= 0) return;

            mAnimatorList.clear();//清除动画 for (FocusListener f : this.mFocusListener) {
                f.onFocusChanged(oldFocus, newFocus);
            }

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    } @Override public void onScrollChanged(View target, View attachView) { try {
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    } @Override public void onLayout(View target, View attachView) { try {
            ViewGroup viewGroup = (ViewGroup) attachView.getRootView(); if (target.getParent() != null && target.getParent() != viewGroup) {
                target.setVisibility(View.GONE); if (mFirstFocus)
                    viewGroup.requestFocus();//如果是首次获取焦点,强制变成焦点态 }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    } protected boolean mFirstFocus = true; public void setFirstFocus(boolean b) { this.mFirstFocus = b;
    } @Override public void onTouchModeChanged(View target, View attachView, boolean isInTouchMode) { try { if (mEnableTouch && isInTouchMode) {
                target.setVisibility(View.INVISIBLE); if (lastFocus != null) {
                    AnimatorSet animatorSet = new AnimatorSet();
                    animatorSet.playTogether(getScaleAnimator(lastFocus, false));
                    animatorSet.setDuration(0).start();
                }
            }

        } catch (Exception ex) {
            ex.printStackTrace();
        }

    } protected boolean isScrolling = false; protected ListattacheViews = new ArrayList<>(); protected Map onItemSelectedListenerList = new HashMap<>(); @Override public void onAttach(View target, View attachView) { try {
            mTarget = target; if (target.getParent() != null && (target.getParent() instanceof ViewGroup)) {
                ViewGroup vg = (ViewGroup) target.getParent();
                vg.removeView(target);
            }

            ViewGroup vg = (ViewGroup) attachView.getRootView();
            vg.addView(target);

            target.setVisibility(View.GONE); if (attachView instanceof RecyclerView) {
                RecyclerView recyclerView = (RecyclerView) attachView;
                RecyclerView.OnScrollListener recyclerViewOnScrollListener = null;
                recyclerViewOnScrollListener = new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { try { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                                isScrolling = false;
                                View oldFocus = oldLastFocus;
                                View newFocus = lastFocus;
                                VisibleScope scope = checkVisibleScope(oldFocus, newFocus); if (!scope.isVisible) { return;
                                } else {
                                    oldFocus = scope.oldFocus;
                                    newFocus = scope.newFocus;
                                }
                                AnimatorSet animatorSet = new AnimatorSet();
                                Listlist = new ArrayList<>();
                                list.addAll(getScaleAnimator(newFocus, true));
                                list.addAll(getMoveAnimator(newFocus, 0, 0));
                                animatorSet.setDuration(mDurationTraslate);
                                animatorSet.playTogether(list);
                                animatorSet.start();


                            } else if (newState == RecyclerView.SCROLL_STATE_SETTLING) {
                                isScrolling = true; if (lastFocus != null) {
                                    Listlist = getScaleAnimator(lastFocus, false);
                                    AnimatorSet animatorSet = new AnimatorSet();
                                    animatorSet.setDuration(150);
                                    animatorSet.playTogether(list);
                                    animatorSet.start();
                                }
                            }
                        } catch (Exception ex) {

                        }
                    }
                };
                recyclerView.addOnScrollListener(recyclerViewOnScrollListener);
            } else if (attachView instanceof AbsListView) { final AbsListView absListView = (AbsListView) attachView; final AdapterView.OnItemSelectedListener onItemSelectedListener = absListView.getOnItemSelectedListener();

                View temp = null; if (absListView.getChildCount() > 0) {
                    temp = absListView.getChildAt(0);
                } final View tempFocus = temp;
                MyOnItemSelectedListener myOnItemSelectedListener = new MyOnItemSelectedListener();
                myOnItemSelectedListener.onItemSelectedListener = onItemSelectedListener;
                myOnItemSelectedListener.oldFocus = temp;
                absListView.setOnItemSelectedListener(myOnItemSelectedListener);
                onItemSelectedListenerList.put(attachView, myOnItemSelectedListener);

            }

            attacheViews.add(attachView);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    } protected class MyOnItemSelectedListener implements AdapterView.OnItemSelectedListener { public View oldFocus = null; public View newFocus = null; public AnimatorSet animatorSet; public AdapterView.OnItemSelectedListener onItemSelectedListener; @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { try { if (onItemSelectedListener != null && parent != null) {
                    onItemSelectedListener.onItemSelected(parent, view, position, id);
                } if (newFocus == null) return;
                newFocus = view;

                Rect rect = new Rect();
                view.getLocalVisibleRect(rect);
                ViewGroup vg = (ViewGroup) newFocus.getParent(); int factorX = 0, factorY = 0; if (Math.abs(rect.left - rect.right) > newFocus.getMeasuredWidth()) {
                    factorX = (Math.abs(rect.left - rect.right) - newFocus.getMeasuredWidth()) / 2 - 1;
                    factorY = (Math.abs(rect.top - rect.bottom) - newFocus.getMeasuredHeight()) / 2;

                }


                ListanimatorList = new ArrayList(3);
                animatorList.addAll(getScaleAnimator(newFocus, true)); if (oldFocus != null)
                    animatorList.addAll(getScaleAnimator(oldFocus, false));
                animatorList.addAll(getMoveAnimator(newFocus, factorX, factorY));
                mTarget.setVisibility(View.VISIBLE); if (animatorSet != null && animatorSet.isRunning())
                    animatorSet.end();
                animatorSet = new AnimatorSet();
                animatorSet.setDuration(mDurationTraslate);
                animatorSet.playTogether(animatorList);
                animatorSet.start();


                oldFocus = newFocus;
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        } @Override public void onNothingSelected(AdapterView parent) { if (onItemSelectedListener != null) {
                onItemSelectedListener.onNothingSelected(parent);
            }
        }
    } @Override public void OnDetach(View targe, View view) { if (targe.getParent() == view) {
            ((ViewGroup) view).removeView(targe);
        }

        attacheViews.remove(view);
    } public void setEnableTouch(boolean enableTouch) { this.mEnableTouch = enableTouch;
    } public boolean isScalable() { return mScalable;
    } public void setScalable(boolean scalable) { this.mScalable = scalable;
    } public float getScale() { return mScale;
    } public void setScale(float scale) { this.mScale = scale;
    } public int getMargin() { return mMargin;
    } public void setMargin(int mMargin) { this.mMargin = mMargin;
    }

}
MetroViewBorder用于对焦点变化处理及滚动处理
public class MetroViewBorderImplimplements ViewTreeObserver.OnGlobalFocusChangeListener, ViewTreeObserver.OnScrollChangedListener, ViewTreeObserver.OnGlobalLayoutListener, ViewTreeObserver.OnTouchModeChangeListener { private static final String TAG = MetroViewBorderImpl.class.getSimpleName(); private ViewGroup mViewGroup; private IMetroViewBorder mMetroViewBorder; private X mView; private View mLastView; public MetroViewBorderImpl(Context context) { this(context, null, 0);
    } public MetroViewBorderImpl(Context context, AttributeSet attrs) { this(context, attrs, 0);
    } public MetroViewBorderImpl(Context context, AttributeSet attrs, int defStyleAttr) {
        init(context, attrs, defStyleAttr);
    } private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        mMetroViewBorder = new MetroViewBorderHandler();
        mView = (X) new View(context, attrs, defStyleAttr);
    } public MetroViewBorderImpl(X view) { this.mView = view;
        mMetroViewBorder = new MetroViewBorderHandler();
    } public MetroViewBorderImpl(X view, IMetroViewBorder border) { this.mView = view;
        mMetroViewBorder = border;
    } public MetroViewBorderImpl(Context context, int resId) { this((X) LayoutInflater.from(context).inflate(resId, null, false));
    } public X getView() { return mView;
    } public void setBackgroundResource(int resId) { if (mView != null)
            mView.setBackgroundResource(resId);
    }

    @Override public void onScrollChanged() {
        mMetroViewBorder.onScrollChanged(mView, mViewGroup);
    }

    @Override public void onGlobalLayout() {
        mMetroViewBorder.onLayout(mView, mViewGroup);
    }

    @Override public void onTouchModeChanged(boolean isInTouchMode) {
        mMetroViewBorder.onTouchModeChanged(mView, mViewGroup, isInTouchMode);
    }

    @Override public void onGlobalFocusChanged(View oldFocus, View newFocus) { try { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2) {// 4.3 if (oldFocus == null && mLastView != null) {
                    oldFocus = mLastView;
                }
            } if (mMetroViewBorder != null)
                mMetroViewBorder.onFocusChanged(mView, oldFocus, newFocus);
            mLastView = newFocus;

        } catch (Exception ex) {
            ex.printStackTrace();
        }

    } public T getViewBorder() { return (T) mMetroViewBorder;
    } public void setBorder(IMetroViewBorder border) { this.mMetroViewBorder = border;
    } public void attachTo(ViewGroup viewGroup) { try { if (viewGroup == null) { if (mView.getContext() instanceof Activity) {
                    Activity activity = (Activity) mView.getContext();
                    viewGroup = (ViewGroup) activity.getWindow().getDecorView().getRootView();//获取顶层view }
            } if (mViewGroup != viewGroup) {

                ViewTreeObserver viewTreeObserver = viewGroup.getViewTreeObserver(); if (viewTreeObserver.isAlive() && mViewGroup == null) {
                    viewTreeObserver.addOnGlobalFocusChangeListener(this);
                    viewTreeObserver.addOnScrollChangedListener(this);
                    viewTreeObserver.addOnGlobalLayoutListener(this);
                    viewTreeObserver.addOnTouchModeChangeListener(this);
                }
                mViewGroup = viewGroup;
            }
            mMetroViewBorder.onAttach(mView, viewGroup);


        } catch (Exception ex) {
            ex.printStackTrace();
        }
    } public void detach() {
        detachFrom(mViewGroup);
    } public void detachFrom(ViewGroup viewGroup) { try { if (viewGroup == mViewGroup) {
                ViewTreeObserver viewTreeObserver = mViewGroup.getViewTreeObserver();//获取view树的观察者 viewTreeObserver.removeOnGlobalFocusChangeListener(this);//通知全局性移除相应的listener viewTreeObserver.removeOnScrollChangedListener(this);
                viewTreeObserver.removeOnGlobalLayoutListener(this);
                viewTreeObserver.removeOnTouchModeChangeListener(this);
                mMetroViewBorder.OnDetach(mView, viewGroup);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

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

这里写图片描述 如果你觉得好,随手点赞,也是对笔者的肯定,也可以分享此公众号给你更多的人,原创不易
关注
打赏
1655516835
查看更多评论
立即登录/注册

微信扫码登录

0.0565s