TV Metro介面(仿泰捷視訊TV版)原始碼解析
阿新 • • 發佈:2019-02-03
先看下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 List<Animator> mAnimatorList = new ArrayList<Animator>();
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 List<FocusListener> mFocusListener = new ArrayList<FocusListener>(1);
protected List<Animator.AnimatorListener> mAnimatorListener = new ArrayList<Animator.AnimatorListener>(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;
}
List<Animator> animatorList = new ArrayList<Animator>(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;
}
List<Animator> animatorList = new ArrayList<Animator>(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 List<Animator> getScaleAnimator(View view, boolean isScale) {
List<Animator> animatorList = new ArrayList<Animator>(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 List<Animator> getMoveAnimator(View newFocus, int factorX, int factorY) {
List<Animator> animatorList = new ArrayList<Animator>();
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 List<View> attacheViews = new ArrayList<>();
protected Map<View, AdapterView.OnItemSelectedListener> 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;