本文共 11449 字,大约阅读时间需要 38 分钟。
转载请注明出处:
黑发不知勤学早,白首方悔读书迟。 —— 颜真卿《劝学诗》
这一系列的博文这是第五篇了,感谢大家的支持以及陪伴,往后我也会继续努力写出高质量的内容,谢谢
今天上的是一个自定义View,新鲜出炉,先上下效果(是一张张截图拼接的Gif动画都看不出来了,大家理解就行可以下Demo跑)
样例分析(最简单的描述了)
黑色线条是我们的手机
红色是我们自定义的”TitleBar”
蓝色是我们的自定义布局
紫色是自定义布局填充的内容
我们只需要配置我们蓝色内容的参数就可以对动画效果以及大小等进行设置。
PS:因为 蓝色内容吃掉了所有蓝色区域的OnTouch,所以紫色就不要做用户交互内容了,纯粹做展示吧TOT(小的该死)看下项目结构:
就比上次的代码多了一些资源文件和4个类,一个就是我们的麦麦Activity,另外3个解释下
DraggableFlipView我们的自定义控件
DragGestureDetector我们的触碰效果处理类
FlipListener动作展示以及处理结果
OK,开始分析
public class DraggableFlipView extends FrameLayout implements DragGestureDetector.DragGestureListener { //一系列的声明,不一一解释了,后面用到了会加以解释 private static final float DRAG_THRESHOLD_PARAM = 50.0f; private static final int DEFAULT_VALUE = 0; private static final int DEFAULT_DRAGGABLE_VALUE = 50; private static final int DEFAULT_DRAG_DETECT_VALUE = 7; private DragGestureDetector mDragGestureDetector; private boolean isAnimation; private boolean isDragging; private int mAngle; private int mDraggableAngle; private int mDragDetectAngle; private boolean mIsReverse; private FlipListener mFlipListener; private RelativeLayout mFrontLayout; private RelativeLayout mBackLayout; //声明左右状态的枚举 private enum RotateDirection { RIGHT(1), LEFT(-1); private int mValue; RotateDirection(int value) { this.mValue = value; } public int getValue() { return mValue; } } //构造函数 public DraggableFlipView(Context context) { this(context, null); } public DraggableFlipView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DraggableFlipView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } //初始化参数 private void init(Context context, AttributeSet attrs) { //获取布局对象并加以填充,默认显示mBackLayout这个布局 mFrontLayout = new RelativeLayout(context); RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); mFrontLayout.setLayoutParams(params1); mBackLayout = new RelativeLayout(context); RelativeLayout.LayoutParams params2 = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); mBackLayout.setLayoutParams(params2); this.addView(mFrontLayout); this.addView(mBackLayout); mBackLayout.setVisibility(View.INVISIBLE); //初始化FlipListener,传入2个布局,第二个参数为不显示的布局 mFlipListener = new FlipListener(mFrontLayout, mBackLayout, this); mDragGestureDetector = new DragGestureDetector(this); //获取 标签 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DraggableFlipView); LayoutInflater.from(context).inflate(a.getResourceId(R.styleable.DraggableFlipView_frontView, DEFAULT_VALUE), mFrontLayout); LayoutInflater.from(context).inflate(a.getResourceId(R.styleable.DraggableFlipView_backView, DEFAULT_VALUE), mBackLayout); //填充标签数据 mDraggableAngle = a.getInteger(R.styleable.DraggableFlipView_draggableAngle, DEFAULT_DRAGGABLE_VALUE); mDragDetectAngle = a.getInteger(R.styleable.DraggableFlipView_dragDetectAngle, DEFAULT_DRAG_DETECT_VALUE); } //onInterceptTouchEvent这个事件是从父控件开始往子控件传的,直到有拦截或者到没有这个事件的view,并且使用 mDragGestureDetector.setPointMap(ev);进行参数的传递 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mDragGestureDetector == null) return false; int action = ev.getAction() & MotionEvent.ACTION_MASK; switch (action) { case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_MOVE: if (Math.abs(ev.getX() - mDragGestureDetector.getTouchPoint().getX()) > DRAG_THRESHOLD_PARAM || Math.abs(ev.getY() - mDragGestureDetector.getTouchPoint().getY()) > DRAG_THRESHOLD_PARAM) { mDragGestureDetector.setPointMap(ev); return true; } break; case MotionEvent.ACTION_POINTER_DOWN: return true; } return false; } //onTouch这个事件是从子控件回传到父控件的,一层层向下传 //mDragGestureDetector.onTouchEvent(event)来实现onTouchEvent的操作 @Override public boolean onTouchEvent(MotionEvent event) { if (mDragGestureDetector != null) { mDragGestureDetector.onTouchEvent(event); } return true; } //计算,并判断以哪种方式来实现切换动画 @Override public void onDragGestureListener(DragGestureDetector dragGestureDetector, int action) { if (isAnimation) return; if (action == MotionEvent.ACTION_UP) { if (mAngle >= mDragDetectAngle) { startAutoRotateAnimation(RotateDirection.RIGHT); } else if (mAngle < -mDragDetectAngle) { startAutoRotateAnimation(RotateDirection.LEFT); } return; } mAngle = (dragGestureDetector.deltaX - dragGestureDetector.prevDeltaX) > 0 ? ++mAngle : --mAngle; if (Math.abs(mAngle) > mDragDetectAngle) isDragging = true; if(isDragging) this.setRotationY(mAngle); if (mAngle >= mDraggableAngle) { startAutoRotateAnimation(RotateDirection.RIGHT); } else if (mAngle < -mDraggableAngle) { startAutoRotateAnimation(RotateDirection.LEFT); } } private void startAutoRotateAnimation(RotateDirection rotateDirection) { isAnimation = true; if (mIsReverse) { mFlipListener.reverse(); } else { mIsReverse = true; } mFlipListener.setRotateDirection(rotateDirection.getValue()); //动画的平滑过渡 可以参照 http://blog.csdn.net/guolin_blog/article/details/43536355 //讲的很详细 ValueAnimator mFlipAnimator = ValueAnimator.ofFloat(0f, 1f); mFlipAnimator.addUpdateListener(mFlipListener); mFlipAnimator.start(); mFlipAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { mAngle = 0; isAnimation = false; isDragging = false; } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); }}
public class DragGestureDetector { public float deltaX; public float deltaY; public float prevDeltaX; public float prevDeltaY; public int originalIndex; public float velocityX; public float velocityY; //储存用户操作路径 private HashMappointMap = new HashMap<>(); private DragGestureListener dragGestureListener; //构造函数 public DragGestureDetector(DragGestureListener dragGestureListener) { this.dragGestureListener = dragGestureListener; //初始化坐标 pointMap.put(0, createPoint(0.f, 0.f)); } //储存坐标点 public void setPointMap(MotionEvent event) { float eventX = event.getX(); float eventY = event.getY(); TouchPoint downPoint = pointMap.get(0); if (downPoint != null) { downPoint.setXY(eventX, eventY); return; } downPoint = createPoint(eventX, eventY); pointMap.put(0, downPoint); } //获取坐标点 public TouchPoint getTouchPoint() { return pointMap.get(originalIndex); } //用户触控坐标点相应的计算 synchronized public boolean onTouchEvent(MotionEvent event) { float eventX = event.getX(originalIndex); float eventY = event.getY(originalIndex); int action = event.getAction() & MotionEvent.ACTION_MASK; switch (action) { case MotionEvent.ACTION_DOWN: { break; } case MotionEvent.ACTION_MOVE: { TouchPoint originalPoint = pointMap.get(originalIndex); if (originalPoint != null) { deltaX = eventX - originalPoint.x; deltaY = eventY - originalPoint.y; if (dragGestureListener != null) { dragGestureListener.onDragGestureListener(this, action); } velocityX = deltaX - prevDeltaX; velocityY = deltaY - prevDeltaY; prevDeltaX = deltaX; prevDeltaY = deltaY; } break; } case MotionEvent.ACTION_UP: { TouchPoint originalPoint = pointMap.get(originalIndex); if (originalPoint != null && dragGestureListener != null) { dragGestureListener.onDragGestureListener(this, action); } velocityX = velocityY = 0; prevDeltaX = prevDeltaY = 0; deltaX = deltaY = 0; break; } default: } return false; } private TouchPoint createPoint(float x, float y) { return new TouchPoint(x, y); } public interface DragGestureListener { void onDragGestureListener(DragGestureDetector dragGestureDetector, int action); } //坐标类 public class TouchPoint { private float x; private float y; public TouchPoint(float x, float y) { this.x = x; this.y = y; } public TouchPoint setXY(float x, float y) { this.x = x; this.y = y; return this; } public float getX() { return this.x; } public float getY() { return this.y; } }}
public class FlipListener implements ValueAnimator.AnimatorUpdateListener { private View mParentView; private View mFrontView; private View mBackView; private boolean mFlipped; private int mDirection; //构造函数 public FlipListener(final View front, final View back, final View parent) { this.mParentView = parent; this.mFrontView = front; this.mBackView = back; this.mBackView.setVisibility(View.GONE); } @Override public void onAnimationUpdate(final ValueAnimator animation) { final float value = animation.getAnimatedFraction(); final float scaleValue = 0.625f + (1.5f * (value - 0.5f) * (value - 0.5f)); //根据传入的mDirection(1或者-1)进行计算并且逻辑判断 if (value <= 0.5f) { this.mParentView.setRotationY(180 * value * mDirection); if (mFlipped) setStateFlipped(false); } else { this.mParentView.setRotationY(-180 * (1 - value) * mDirection); if (!mFlipped) setStateFlipped(true); } this.mParentView.setScaleX(scaleValue); this.mParentView.setScaleY(scaleValue); } //初始化自定义View时调用 public void reverse() { View temp = mBackView; mBackView = mFrontView; mFrontView = temp; } public void setRotateDirection(int direction) { mDirection = direction; } //具体切换试图 private void setStateFlipped(boolean flipped) { mFlipped = flipped; this.mFrontView.setVisibility(flipped ? View.GONE : View.VISIBLE); this.mBackView.setVisibility(flipped ? View.VISIBLE : View.GONE); }}
OK!!!!实现就这些啦
源码: 访问密码 d11f