Android仿苹果版QQ下拉刷新实现(二) ——贝塞尔曲线开发"鼻涕"下拉粘连效果
前言
接着上一期Android仿苹果版QQ下拉刷新实现(一) ——打造简单平滑的通用下拉刷新控件 的博客开始,同样,在开始前我们先来看一下目标效果:
下面上一下本章需要实现的效果图:
大家看到这个效果肯定不会觉得陌生,QQ已经把粘滞效果做的满大街都是,相信不少读者或多或少对于贝塞尔曲线有所了解,不了解的朋友们也没有关系,在这里我会带领读者领略一下贝塞尔的魅力!
一、关于贝塞尔曲线
我们知道,任何一条线段是由起始点和终止点的连线组成,两点组成一条直线,这就是最简单的一阶公式(就是线段):
一阶贝塞尔曲线表达公式(图略):
B(t) = P0 + ( P1 - P0 ) t = ( 1 - t ) P0 + t P1 , t∈[0,1]
很显然,一阶的贝塞尔只是用于一条线段,其中t的变化率代表着线性插值大小.所以我们的效果用于一阶贝塞尔曲线公式肯定不行,下面我们来着重介绍一下二阶(次)贝塞尔曲线变化率和公式:
(图片来自于网络)
公式:
B(t) = ( 1 - t )² P0 + 2 t ( 1 - t ) P1 + t² P2 , t∈[0,1]
其实公式对于我们的开发者来说并没有太大的意义,因为主要的算法我们的API都已经包含,不过我们需要了解的是,我们的辅助点的查找.首先,我们需要了解曲线是如何画出来的?从图中我们可以看出我们的辅助点是p1点,由p0和p1组成的线段加上p1和p2组成的线段一共是有两条线段,我们需要一个变化率t,t从p0走到p1和从p1走到p2的时间是一样的,这样我们连接两点,就产生了第三条直线(图中绿色的线),这条直线其实就是我们的贝塞尔曲线的切线,只要有了这条直线,我们就可以确定我们的贝塞尔曲线轨迹(这一点至关重要).
当然,有一阶二阶,肯定也会有三阶、四阶等等.因为辅助点的增加,曲线也会发生各种变化,在这里,博主就不介绍了,想了解更深入的读者,可以在很多关于贝塞尔的博客中去了解.
介绍完了贝塞尔曲线,接下来我们就要开始着手打造QQ的粘滞效果了.在开始编写代码前我们先分析一下,我们要实现这个效果所需要的准备工作:
- 自定义View先绘制两个同样大小并重叠的圆形
- 按照小圆的大小我们设置圆形上刷新图标
- 重写触摸事件,绘制我们的贝塞尔曲线
- 动画收回
二、自定义View绘制圆形
/**
* 圆的画笔
*/
private Paint circlePaint;
/**
* 画笔的路径
*/
private Path circlePath; /**
* 可拖动的最远距离
*/
private int maxHeight; /**
* 刷新图标
*/
private Bitmap bt; private float topCircleRadius;//默认上面圆形半径
private float topCircleX;//默认上面圆形x
private float topCircleY;//默认上面圆形y private float bottomCircleRadius;//默认上面圆形半径
private float bottomCircleX;//默认下面圆形x
private float bottomCircleY;//默认下面圆形y private float defaultRadius;//默认上面圆形半径 float offset=1.0f; float lastY; OnAnimResetListener listener; ObjectAnimator anim;
public YPXBezierView(Context context) {
this(context, null);
}
public YPXBezierView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public YPXBezierView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
protected void init() {
maxHeight=dp(60);
topCircleX=ScreenUtils.getScreenWidth(getContext())/2;
topCircleY=dp(100);
topCircleRadius=dp(15);
bottomCircleX=topCircleX;
bottomCircleY=topCircleY;
bottomCircleRadius=topCircleRadius;
defaultRadius=topCircleRadius;
circlePath = new Path();
circlePaint = new Paint();
circlePaint.setAntiAlias(true);
circlePaint.setStyle(Paint.Style.FILL_AND_STROKE);
circlePaint.setStrokeWidth(1);
circlePaint.setColor(Color.parseColor("#999999"));
}
@Override
protected void onDraw(Canvas canvas) {
drawPath();
float left=topCircleX-topCircleRadius;
float top=topCircleY-topCircleRadius; canvas.drawPath(circlePath, circlePaint);
canvas.drawCircle(bottomCircleX, bottomCircleY, bottomCircleRadius, circlePaint);
canvas.drawCircle(topCircleX, topCircleY, topCircleRadius, circlePaint); int btWidth=(int) topCircleRadius* 2-dp(6);
if ((btWidth) > 0) {
bt = BitmapFactory.decodeResource(getResources(), R.mipmap.refresh);
bt = Bitmap.createScaledBitmap(bt,btWidth, btWidth, true);
canvas.drawBitmap(bt, left+dp(3), top+dp(2) , null);
bt.recycle();
}
super.onDraw(canvas); }
这个方法了,画圆形的代码不用多说,直接drawCircle就好,关于刷新图标,我们需要说一下,因为我们的刷新图标是需要跟随大圆的大小变化而变化的,所以它自身的大小一定是可变的,我查阅了关于修改bitmap大小的方法,发现只有在创建的时候使用createScaledBitmap方法,该方法支持bitmap的缩放,但是美中不足的是,它的效果是叠加的,如果把bitmap只创建一次并且不去释放,那么每次刷新的时候会发现我们的刷新图标越来越模糊,目前博主没有什么好的解决方案,只能在绘制的时候重新生成bitmap,如果有了解更优化的方案的话,欢迎大神联系交流~我们的边距是3dp,所以我们的位置需要减去6dp,这样看起来效果更好一点!
三、绘制贝塞尔曲线
图中有六个重要的点,p1、p2、p3、p4、anchor1、anchor2,因为我们的粘滞小球尽量需要平滑一点,所以博主选择了最简单的四个交叉点(p1~p4),这四个点不涉及到三角函数的处理,所以坐标很容易的就可以得到:
private void drawPath() {
float p1X = topCircleX - topCircleRadius ;
float p1Y = topCircleY ;
float p2X = topCircleX + topCircleRadius;
float p2Y = topCircleY ;
float p3X = bottomCircleX - bottomCircleRadius ;
float p3Y = bottomCircleY ;
float p4X = bottomCircleX + bottomCircleRadius ;
float p4Y = bottomCircleY ;
float anchorX = (p1X+ p4X) / 2-topCircleRadius*offset;
float anchorY = (p1Y + p4Y) / 2;
float anchorX2 = (p2X +p3X) / 2+topCircleRadius*offset;
float anchorY2 = (p2Y + p3Y) / 2;
/* 画粘连体 */
circlePath.reset();
circlePath.moveTo(p1X, p1Y);
circlePath.quadTo(anchorX, anchorY, p3X, p3Y);
circlePath.lineTo(p4X, p4Y);
circlePath.quadTo(anchorX2, anchorY2, p2X, p2Y);
circlePath.lineTo(p1X, p1Y);
}
三、触摸事件监听以及收回
private void drawPath() {
float p1X = topCircleX - topCircleRadius ;
float p1Y = topCircleY ;
float p2X = topCircleX + topCircleRadius;
float p2Y = topCircleY ;
float p3X = bottomCircleX - bottomCircleRadius ;
float p3Y = bottomCircleY ;
float p4X = bottomCircleX + bottomCircleRadius ;
float p4Y = bottomCircleY ;
float anchorX = (p1X+ p4X) / 2-topCircleRadius*offset;
float anchorY = (p1Y + p4Y) / 2;
float anchorX2 = (p2X +p3X) / 2+topCircleRadius*offset;
float anchorY2 = (p2Y + p3Y) / 2;
/* 画粘连体 */
circlePath.reset();
circlePath.moveTo(p1X, p1Y);
circlePath.quadTo(anchorX, anchorY, p3X, p3Y);
circlePath.lineTo(p4X, p4Y);
circlePath.quadTo(anchorX2, anchorY2, p2X, p2Y);
circlePath.lineTo(p1X, p1Y);
}
offset public void animToReset(boolean lock){
if(!lock) {
Log.e("onAnimationEnd", "动画开始");
anim= ObjectAnimator.ofFloat(offset, "ypx", 0.0F, 1.0F).setDuration(200);
//使用反弹算法插值器,貌似没有什么太大的效果 - -!
anim.setInterpolator(new BounceInterpolator());
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float cVal = (Float) animation.getAnimatedValue();
offset = cVal;
bottomCircleX=bottomCircleX+(topCircleX-bottomCircleX)*offset;
bottomCircleY=bottomCircleY+(topCircleY-bottomCircleY)*offset;
bottomCircleRadius=bottomCircleRadius+(topCircleRadius-bottomCircleRadius)*offset;
topCircleRadius=topCircleRadius+(defaultRadius-topCircleRadius)*offset;
postInvalidate();
}
});
anim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
Log.e("onAnimationEnd", "动画结束");
if (listener != null) {
listener.onReset();
}
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
anim.start();
}
}
四、使用和总结
感谢大家的支持,谢谢!
作者:yangpeixing
QQ:313930500
下载地址:http://download.csdn.net/detail/qq_16674697/9741375
转载请注明出处~谢谢~
Android仿苹果版QQ下拉刷新实现(二) ——贝塞尔曲线开发"鼻涕"下拉粘连效果的更多相关文章
- Android仿苹果版QQ下拉刷新实现(一) ——打造简单平滑的通用下拉刷新控件
前言: 忙完了结婚乐APP的开发,终于可以花一定的时间放在博客上了.好了,废话不多说,今天我们要带来的效果是苹果版本的QQ下拉刷新.首先看一下目标效果以及demo效果: 因为此效果实现的步骤 ...
- [Swift通天遁地]二、表格表单-(4)使用系统自带的下拉刷新控件,制作表格的下拉刷新效果
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...
- Android平台下利用zxing实现二维码开发
Android平台下利用zxing实现二维码开发 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平 ...
- (转载)Android平台下利用zxing实现二维码开发
Android平台下利用zxing实现二维码开发 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平 ...
- [Android]仿新版QQ的tab下面拖拽标记为已读的效果
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4182929.html 可拖拽的红点,(仿新版QQ,tab下面拖 ...
- ListView实现下拉刷新(二)隐藏头布局
一.问题分析 在上一篇中,我们将头布局加到了ListView上.但是没有隐藏他.你可能会想,隐藏还不简单,直接给它设置为GONE属性不就可以了吗,在需要的时候再设定为可见.没错,这正是ListView ...
- Android仿微信QQ等实现锁屏消息提醒
demo代码如下: import android.content.Intent; import android.os.Bundle; import android.support.v7.app.App ...
- mui实现分页上拉加载更多 下拉刷新数据的简单实现 移动端下拉上拉
空下来把mui上拉加载更多,下拉刷新数据做了一个简单的实现,希望可以帮助到需要的朋友 demo项目的结构 <!DOCTYPE html> <html> <head> ...
- 【转】Android平台下利用zxing实现二维码开发
http://www.cnblogs.com/dolphin0520/p/3355728.html 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描 ...
随机推荐
- linux 命令大全,我去
系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS ...
- BZOJ4654 NOI2016国王饮水记(动态规划+三分)
有很多比较显然的性质.首先每个城市(除1外)至多被连通一次,否则没有意义.其次将城市按水位从大到小排序后,用以连通的城市集合是一段前缀,并且不应存在比1城市还小的.然后如果确定了选取的城市集合,每次选 ...
- BZOJ2756 SCOI2012奇怪的游戏(二分答案+最大流)
由数据范围容易想到网络流.由于操作只是对于棋盘上相邻两格,容易想到给其黑白染色. 假设已经知道最后要变成什么数.那么给黑白点之间连边,其流量则表示同时增加的次数,再用源汇给其限流为需要增加的数即可. ...
- C++11 初始化
C++11 初始化 统一初始化语法 C++11新添加初始化列表 std::initializer_list<>类型,可以通过{}语法来构造初始化列表 .初始化列表是常数:一旦 ...
- 【刷题】BZOJ 2724 [Violet 6]蒲公英
Description Input 修正一下 l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n + 1 Output Sample Input ...
- 【刷题】LOJ 6121 「网络流 24 题」孤岛营救问题
题目描述 1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩.瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图.迷宫的外形是一个长方形 ...
- 今天GG
刚开考: 这里锅了,那里锅了,还被D了QAQ. 然后\(YL\)说,\(T2\)不是傻逼题吗. 于是萝卜秒掉了\(T1\). 于是\(gsy\)秒掉了\(T3\). \(lalaxu,FlashHu\ ...
- luogu1373 小a和uim之大逃离 (dp)
直接设f[i][j][k][l][2]是在(i,j)时两人分数是k,l,复杂度会爆掉 但其实只需要知道两人分数只差就行了 所以设f[i][j][k][2],k是分数之差%(K+1),最后一位表示该谁走 ...
- 【POJ2796】Feel Good 单调栈
题目大意:给定一个长度为 N 的序列,求任意区间 [ l , r ] 中最小的\(min\{v[i],i\in[l,r] \}*\Sigma_{i=l}^rv[i]\). 题解:这是一道具有标准单调栈 ...
- 解决 django 博客归档 “Are time zone definitions for your database and pytz installed?”的错误
修改 project 中的settings 文件,问题解决! # USE_TZ = True USE_TZ = False # LANGUAGE_CODE = 'en-us' LANGUAGE_COD ...