Android从零开搞系列:自定义View(15)仿天天美剧拖动卡片的效果(下)
来源:互联网 发布:小米净水器怎么样 知乎 编辑:程序博客网 时间:2024/06/09 15:15
转载请注意:http://blog.csdn.net/wjzj000/article/details/73441173
本菜开源的一个自己写的Demo,希望能给Androider们有所帮助,水平有限,见谅见谅…
https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的优秀框架于一体,全部拆离不含任何额外的库导入)
https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)
写在前面
本来想昨天就把这个效果分析完毕。然后事与愿违,随便玩了玩时间就没了。没办法只有今天补上….最难逃儿女情长书接上回楚霸王,让我们续上仿天天美剧拖动卡片的效果(上):
http://blog.csdn.net/wjzj000/article/details/73432852
开始
我们上部分分析完了setAdapter的一系列的过程,这部分我们要通过
FlingCardListener
来近距离的看一看我们拖动Card的实现效果。
在开始FlingCardListener
之前,让我们回过来上看一个类LinearRegression
。可能各位看官已经把这个类给忘了,所以我们在分析的一开始就看一看它。
LinearRegression
public LinearRegression(float[] x, float[] y) { if (x.length != y.length) { throw new IllegalArgumentException("array lengths are not equal"); } N = x.length; //求平均值 double sumx = 0.0, sumy = 0.0, sumx2 = 0.0; for (int i = 0; i < N; i++) sumx += x[i]; for (int i = 0; i < N; i++) sumx2 += x[i]*x[i]; for (int i = 0; i < N; i++) sumy += y[i]; double xbar = sumx / N; double ybar = sumy / N; //求方差 double xxbar = 0.0, yybar = 0.0, xybar = 0.0; for (int i = 0; i < N; i++) { xxbar += (x[i] - xbar) * (x[i] - xbar); yybar += (y[i] - ybar) * (y[i] - ybar); xybar += (x[i] - xbar) * (y[i] - ybar); } beta = xybar / xxbar; alpha = ybar - beta * xbar; //一些计算公式 double rss = 0.0; double ssr = 0.0; for (int i = 0; i < N; i++) { double fit = beta*x[i] + alpha; rss += (fit - y[i]) * (fit - y[i]); ssr += (fit - ybar) * (fit - ybar); } int degreesOfFreedom = N-2; R2 = ssr / yybar; svar = rss / degreesOfFreedom; svar1 = svar / xxbar; svar0 = svar/N + xbar*xbar*svar1; }
FlingCardListener
这里比较重要的方法是
onTouch()
方法,构造方法传参非常多,各个变量的使用将逐一在onTouch()
方法这种展开。因此接下来让我们看一看onTouch()
方法。
@Override public boolean onTouch(View view, MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: //确定唯一的坐标点,避免多指操作带来的问题 mActivePointerId = event.getPointerId(0); float x = 0; float y = 0; boolean success = false; try { x = event.getX(mActivePointerId); y = event.getY(mActivePointerId); success = true; } catch (IllegalArgumentException e) { Log.w(TAG, "Exception in onTouch(view, event) : " + mActivePointerId, e); } if (success) { //记住我们按下时的坐标 aDownTouchX = x; aDownTouchY = y; //设置View的初始坐标是传入frame的X、Y值 if (aPosX == 0) { aPosX = frame.getX(); } if (aPosY == 0) { aPosY = frame.getY(); } //记录按压位置状态 if (y < objectH / 2) { touchPosition = TOUCH_ABOVE; } else { touchPosition = TOUCH_BELOW; } } view.getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_UP: mActivePointerId = INVALID_POINTER_ID; //将在下文展开 resetCardViewOnStack(); view.getParent().requestDisallowInterceptTouchEvent(false); break; case MotionEvent.ACTION_MOVE: final int pointerIndexMove = event.findPointerIndex(mActivePointerId); final float xMove = event.getX(pointerIndexMove); final float yMove = event.getY(pointerIndexMove); final float dx = xMove - aDownTouchX; final float dy = yMove - aDownTouchY; aPosX += dx; aPosY += dy; /** * objectX= frame.getX() * 此处为计算移动距离 */ float distobjectX = aPosX - objectX; /** * 如果我们仔细看Card的拖动效果可以发现, * 有一个小小的旋转效果如果我们仔细看Card的拖动效果可以发现,有一个小小的旋转效果 * * 此处是计算旋转角度自定义的公式,当拖动X轴距离和parentWidth相等时, * 那么最大旋转的效果就是:BASE_ROTATION_DEGREES * 2.f */ float rotation = BASE_ROTATION_DEGREES * 2.f * distobjectX / parentWidth; if (touchPosition == TOUCH_BELOW) { rotation = -rotation; } //设置Card的位置,旋转等 frame.setX(aPosX); frame.setY(aPosY); frame.setRotation(rotation); //onScroll方法调用 mFlingListener.onScroll(getScrollProgressPercent()); break; } return true; }
这里的思路是通过setListener的时候,
new FlingCardListener
把每一个Card的View对象和Adapter
中对应的数据,FlingCardListener
将通过onTouch()
方法中的坐标变换进行对Card
位置进行设置,并且完成回调。
resetCardViewOnStack()
private boolean resetCardViewOnStack() { //如果超出左边界限 if (movedBeyondLeftBorder()) { // 执行左划出效果,相关执行代码在这个方法中( onSelected()在下文展开 ) // getExitPoint()得到离开时的坐标Y的方法 onSelected(true, getExitPoint(-objectW), 100); mFlingListener.onScroll(-1.0f); } else if (movedBeyondRightBorder()) { // 执行右划出效果 onSelected(false, getExitPoint(parentWidth), 100); mFlingListener.onScroll(1.0f); } else { //如果不从左或右划出,那么便是Card回弹 float abslMoveDistance = Math.abs(aPosX - objectX); aPosX = 0; aPosY = 0; aDownTouchX = 0; aDownTouchY = 0; frame.animate() .setDuration(200) .setInterpolator(new OvershootInterpolator(1.5f)) .x(objectX) .y(objectY) .rotation(0); mFlingListener.onScroll(0.0f); if (abslMoveDistance < 4.0) { mFlingListener.onClick(dataObject); } } return false; }
onSelected()
Card划出的代码实现,需要接受exitY,离开的坐标Y
public void onSelected(final boolean isLeft,float exitY, long duration) { isAnimationRunning = true; float exitX; if (isLeft) { /** * 计算离开点的X坐标(左移动为减,右移动为加) * getRotationWidthOffset(): * objectW / MAX_COS - objectW(计算旋转的宽度偏移量) * MAX_COS=cos45 */ exitX = -objectW - getRotationWidthOffset(); } else { exitX = parentWidth + getRotationWidthOffset(); } //弹出Card的代码实现 this.frame.animate() .setDuration(duration) .setInterpolator(new AccelerateInterpolator()) .x(exitX) .y(exitY) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (isLeft) { //左边移出回调 mFlingListener.onCardExited(); mFlingListener.leftExit(dataObject); } else { //右边移出回调 mFlingListener.onCardExited(); mFlingListener.rightExit(dataObject); } isAnimationRunning = false; } }) .rotation(getExitRotation(isLeft)); }
getExitPoint()
得到离开时的坐标Y的方法
private float getExitPoint(int exitXPoint) { float[] x = new float[2]; x[0] = objectX; x[1] = aPosX; float[] y = new float[2]; y[0] = objectY; y[1] = aPosY; LinearRegression regression = new LinearRegression(x, y); //这个返回值是一个线性方程: y = ax+b return (float) regression.slope() * exitXPoint + (float) regression.intercept(); }
梳理
走到这里,整体的流程其实还是比较明确的:我们折叠的多个Card的效果是通过类似ListView的实现就行完成的。每一个Card的View在通过设置Listener进行完成Card的左右拖动的效果。而实现弹动的效果是用属性动画完成的。
尾声
希望各位看官可以star我的GitHub,三叩九拜,满地打滚求star:
https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp
- Android从零开搞系列:自定义View(15)仿天天美剧拖动卡片的效果(下)
- Android从零开搞系列:自定义View(14)仿天天美剧拖动卡片的效果(上)
- 高仿launcher和墨迹左右拖动效果 (自定义的View)
- Android从零开搞系列:自定义View(16)自定义验证码输入框效果
- Android从零开搞系列:自定义View(13)新消息小圆点效果
- Android从零开搞系列:自定义View(5)基本的自定义ViewPager指示器+开源项目分析(下)
- Android从零开搞系列:自定义View(7)ScrollTo+ScrollBy+Scroller+NestedScrolling机制(下)
- Android从零开搞系列:自定义View(1)setContent()台前幕后
- Android从零开搞系列:自定义View(8)Canvas解析
- Android从零开搞系列:自定义View(12)贝塞尔曲线的应用
- wing带你玩转自定义view系列(1) 仿360内存清理效果
- Android 自定义实现翻转卡片的View
- 自定义View从入门到懵逼系列(下)
- Android 仿 窗帘效果 和 登录界面拖动效果 (Scroller类的应用)
- Android view的随手指拖动 仿IOS的悬浮圆圈效果
- Android从零开搞系列:自定义View(4)基本的自定义ViewPager指示器+开源项目分析(上)
- 仿IOS特效(一)——Android 自定义View实现3D滚轮效果的城市联动选择器
- 【自定义View】5.仿探探的卡片滑动效果
- nginx+keepalived搭建高可用负载均衡(双主模式)
- Centos7下的Jmeter环境搭建
- BZOJ 4079 [Wf2014] Pachinko
- 如何让你的阅读更高效
- IOS RunLoop浅析 三
- Android从零开搞系列:自定义View(15)仿天天美剧拖动卡片的效果(下)
- C++字符串转化为数字的库函数
- [Docker]3.Docker 容器使用
- MyBatis学习之一
- IOS RunLoop浅析 二
- ANDROID STUDIO系列教程四--GRADLE基础
- 微信小程序开发实践点滴——接入Bmob后端云
- 块模型、内联模型、内联块模型
- sqlite3数据库