博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从头开始敲代码之《从BaseApplication/Activity开始(五)》(自定义控件,实现点击/滑动翻页)...
阅读量:6440 次
发布时间:2019-06-23

本文共 11449 字,大约阅读时间需要 38 分钟。

转载请注明出处:

开场白惯用鼓励诗句:

黑发不知勤学早,白首方悔读书迟。 —— 颜真卿《劝学诗》

这一系列的博文这是第五篇了,感谢大家的支持以及陪伴,往后我也会继续努力写出高质量的内容,谢谢

今天上的是一个自定义View,新鲜出炉,先上下效果(是一张张截图拼接的Gif动画都看不出来了,大家理解就行可以下Demo跑)

这里写图片描述

样例分析(最简单的描述了)

这里写图片描述

黑色线条是我们的手机

红色是我们自定义的”TitleBar”

蓝色是我们的自定义布局

紫色是自定义布局填充的内容

我们只需要配置我们蓝色内容的参数就可以对动画效果以及大小等进行设置。

PS:因为 蓝色内容吃掉了所有蓝色区域的OnTouch,所以紫色就不要做用户交互内容了,纯粹做展示吧TOT(小的该死)

看下项目结构:

这里写图片描述

就比上次的代码多了一些资源文件和4个类,一个就是我们的麦麦Activity,另外3个解释下

DraggableFlipView我们的自定义控件

DragGestureDetector我们的触碰效果处理类

FlipListener动作展示以及处理结果

OK,开始分析

DraggableFlipView

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) { } }); }}

DragGestureDetector

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 HashMap
pointMap = 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; } }}

FlipListener

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

你可能感兴趣的文章
大学团队打造手语翻译机器人,完整安装下来需要149个小时
查看>>
Wireshark抓包分析/TCP/Http/Https及代理IP的识别
查看>>
不同包下,相同数据结构的两个类进行转换
查看>>
软件安装(linux)
查看>>
TeamPlain for VSTS - Web Access for Team System-TFS 跨平台的客户端
查看>>
面对前车之鉴的AR,现在的VR要做些什么?
查看>>
vscode 换行符\n 变成\r\n
查看>>
一个绘制虚线的非常规函数(常规方法,打印机上绘制不出虚线)
查看>>
获得本机的IP,掩码和网关
查看>>
大数据之 ZooKeeper原理及其在Hadoop和HBase中的应用
查看>>
Delphi中将XML文件数据装入DataSet
查看>>
你刚才在淘宝上买了一件东西
查看>>
C#正则表达式整理备忘
查看>>
发布一个 Linux 下的 C++ 多线程库
查看>>
大数据分布式架构单点故障详解(Hdfs+Yarn+HBase+Spark+Storm)构建HA高可用架构
查看>>
通过Stetho在Chrome上调试Android App
查看>>
presto集群安装&整合hive|mysql|jdbc
查看>>
工行企业网银“您的数据签名有误请联系当地工行”解决办法
查看>>
关于namespace的一点点心得体会(2017年8月3日14:55:37)
查看>>
使用Markdown记录文字
查看>>