一直很好奇蘋果版QQ的下拉重新整理頭,那種水滴狀的感覺,特别有彈性的感覺,于是趁着項目比較松的時候也來實作一下,這是實作後的圖
最主要的要知道這個圖形的畫法,我使用的是Path路徑來做的,然後使用填充畫筆,把他全部填充
主要使用兩個半圓和兩條二次曲線構成
于是引入關鍵代碼
補充一下,path中繪制圓弧用的是arcTo方法,不僅可以繪制圓弧也可以繪制橢圓圓弧,傳入矩形區域和角度變化即可,值得注意的是圓弧的方向,用法不當會導緻曲線無法閉合。至于二次曲線就不說了
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//路徑重置
mPath.reset();
//繪制大圓圓弧
mPath.arcTo(new RectF(viewwdith / 2 - GreatCircleRadius, GreatCircleY - GreatCircleRadius,
viewwdith / 2 + GreatCircleRadius, GreatCircleY + GreatCircleRadius), 0, -180);
//繪制左邊的二次曲線
mPath.quadTo(viewwdith / 2 - SmallCircleRadius, (GreatCircleY + SmallCircleY) / 2, viewwdith / 2 - SmallCircleRadius, SmallCircleY);
//把點移動到大半圓的右邊
mPath.moveTo(viewwdith / 2 + GreatCircleRadius, GreatCircleY);
//繪制右邊的二次曲線
mPath.quadTo(viewwdith / 2 + SmallCircleRadius, (GreatCircleY + SmallCircleY) / 2, viewwdith / 2 + SmallCircleRadius, SmallCircleY);
//繪制小圓圓弧
mPath.arcTo(new RectF(viewwdith / 2 - SmallCircleRadius, SmallCircleY - SmallCircleRadius,
viewwdith / 2 + SmallCircleRadius, SmallCircleY + SmallCircleRadius), 0, 180);
canvas.drawPath(mPath, mPaint);
}
這就是主要的繪制方法,剩下的就好辦啦,重寫觸摸事件,使他下拉時,移動小圓的圓心位置,并且根據兩個圓的圓心距,改變兩個圓的半徑,初始時,兩個園大小是一緻的,随着距離的增大,小圓半徑縮小的更快。當手擡起後,開啟線程重置重新整理頭。
下面是所有的代碼:
package com.example.kaifa.myapplication;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
* TODO: document your custom view class.
*/
public class MyView extends View {
private Paint mPaint;
/**
* 下拉進度
*/
private float progress = 0;
/**
* view的寬高
*/
private int viewheight, viewwdith;
/**
* 大圓半徑
*/
private float GreatCircleRadius = 50;
/**
* 小圓半徑
*/
private float SmallCircleRadius = 50;
/**
* 大圓和小圓分别Y軸的坐标
*/
private float GreatCircleY = 60, SmallCircleY = 60;
/**
* 繪制路徑
*/
private Path mPath;
/**
* 第一次按下的Y軸坐标
*/
float firstY = 0;
public MyView(Context context) {
super(context);
init(null, 0);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
// Load attributes
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(0xff0000ff);
mPaint.setStyle(Paint.Style.FILL);
mPath = new Path();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
viewwdith = w;
viewheight = h;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//路徑重置
mPath.reset();
//繪制大圓圓弧
mPath.arcTo(new RectF(viewwdith / 2 - GreatCircleRadius, GreatCircleY - GreatCircleRadius,
viewwdith / 2 + GreatCircleRadius, GreatCircleY + GreatCircleRadius), 0, -180);
//繪制左邊的二次曲線
mPath.quadTo(viewwdith / 2 - SmallCircleRadius, (GreatCircleY + SmallCircleY) / 2, viewwdith / 2 - SmallCircleRadius, SmallCircleY);
//把點移動到大半圓的右邊
mPath.moveTo(viewwdith / 2 + GreatCircleRadius, GreatCircleY);
//繪制右邊的二次曲線
mPath.quadTo(viewwdith / 2 + SmallCircleRadius, (GreatCircleY + SmallCircleY) / 2, viewwdith / 2 + SmallCircleRadius, SmallCircleY);
//繪制小圓圓弧
mPath.arcTo(new RectF(viewwdith / 2 - SmallCircleRadius, SmallCircleY - SmallCircleRadius,
viewwdith / 2 + SmallCircleRadius, SmallCircleY + SmallCircleRadius), 0, 180);
canvas.drawPath(mPath, mPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
firstY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
float dy = event.getY() - firstY;
if (Math.abs(dy) > 2 &&(SmallCircleY-GreatCircleY)<viewheight) {
if (dy<0&&SmallCircleY<GreatCircleY){
}else{
SmallCircleY = SmallCircleY + dy;
if (SmallCircleY<GreatCircleY)
SmallCircleY=GreatCircleY;
}
jisuanR();
}
firstY = event.getY();
Log.v("xingyun", "dy=" + dy);
invalidate();
break;
case MotionEvent.ACTION_UP:
//開啟線程勻速傳回
new MyTread().start();
break;
}
return true;
}
/**
* 根據下拉的距離來計算兩個圓的半徑
*/
private void jisuanR(){
float dy=SmallCircleY-GreatCircleY;
progress=dy/(viewheight);
SmallCircleRadius=(float)(50*(1-0.9*progress));
GreatCircleRadius= (float) (50*(1-0.5*progress));
}
/**
* 回彈的線程
*/
class MyTread extends Thread{
@Override
public void run() {
while (SmallCircleY-GreatCircleY>0){
SmallCircleY=SmallCircleY-10;
if (SmallCircleY<GreatCircleY){
SmallCircleY=GreatCircleY;
}
jisuanR();
postInvalidate();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
如想用用在下拉重新整理處也是非常簡單的,監聽到下拉的距離,用此來設定圓心距即可、~
如有問題,或者更好的實作方法也可以分享一下,謝謝~