您的位置:首页 > 其它

QQ聊天列表粘性控件

2016-08-19 19:46 316 查看

QQ聊天列表粘性控件

应用场景:未读数据的清除等





实现步骤:

1. 画静态图

  先画个两个静态的圆圈,一个大的,一个小的 ,要画的这个图的坐标如下图,通过Path类将上图中的路径坐标一一填充进方法中即可画出下图形状,然后算出两个圆对应下图的坐标替换进去即可。



protected void onDraw(Canvas canvas) {
// 1. 画固定圆
canvas.drawCircle(200f, 200f, 10f, mPaint);

// 2. 画拖动圆
canvas.drawCircle(100f, 100f, 15f, mPaint);

// 3. 画中间连接线
Path path = new Path();
path.moveTo(300f, 300f);            // 跳转到A点,
path.quadTo(200f, 350f, 100f, 300f);// A到B 曲线,二次方贝塞尔曲线
path.lineTo(100f, 400f);            // B到C 直线
path.quadTo(200f, 350f, 300,400);   // C到D 曲线
path.close();                       // D到A 直线
canvas.drawPath(path, mPaint);
}


2. 把静止的值替换成变量

/** 固定圆的圆心 */
private PointF mStickCenter = new PointF(200f, 200f);
/** 固定圆的半径 */
private float mStickRadius = 10f;
/** 拖动圆的圆心 */
private PointF mDragCenter = new PointF(100f, 100f);
/** 拖动圆的半径 */
private float mDragRadius = 15f;

private PointF[] mStickPoints = new PointF[] {
new PointF(300, 300),  // A点
new PointF(300, 400)    // D点
};
private PointF[] mDragPoints = new PointF[] {
new PointF(100f, 300f), // B点
new PointF(100f, 400f)  // C点
};

/** 控制点坐标 */
private PointF mCtrlPoint = new PointF(200f, 350f);

protected void onDraw(Canvas canvas) {
// 1. 画固定圆
canvas.drawCircle(mStickCenter.x, mStickCenter.y, mStickRadius, mPaint);

// 2. 画拖动圆
canvas.drawCircle(mDragCenter.x, mDragCenter.y, mDragRadius, mPaint);

// 3. 画中间连接线
Path path = new Path();
// 移动到A点
path.moveTo(mStickPoints[0].x, mStickPoints[0].y);
// 曲线:A-->B
path.quadTo(mCtrlPoint.x, mCtrlPoint.y, mDragPoints[0].x, mDragPoints[0].y);
// 直线: B-->C
path.lineTo(mDragPoints[1].x, mDragPoints[1].y);
// 曲线:C-->d
path.quadTo(mCtrlPoint.x, mCtrlPoint.y, mStickPoints[1].x, mStickPoints[1].y);
// 直线:D-->A
path.close();
canvas.drawPath(path, mPaint);
}


3. 计算ABCD交叉点和控件点

通过圆心和半径算出四个附着点和控制点

protected void onDraw(Canvas canvas) {
// 计算直线的斜率
double dx = (mStickCenter.x - mDragCenter.x);
double dy = (mStickCenter.y - mDragCenter.y);
double lineK = 0;
if (dx != 0) { // 除数不能为0
lineK = dy / dx;
}
// 计算ABCD四个交叉点
mDragPoints = GeometryUtil.getIntersectionPoints(mDragCenter, mDragRadius, lineK);
mStickPoints = GeometryUtil.getIntersectionPoints(mStickCenter, mStickRadius, lineK);
// 计算控件点
mCtrlPoint = GeometryUtil.getMiddlePoint(mDragCenter, mStickCenter);

...
}


4. 响应按下和拖动事件

在onTouchEvent里面判断手指按下和抬起时将手指的坐标设置成拖拽圆的圆心并重绘界面。

public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
mDragCenter.set(event.getX(), event.getY());
invalidate();
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}


5. 绘制参考圆

protected void onDraw(Canvas canvas) {
...
// 绘制参考圆:断开的最大范围
mPaint.setStyle(Style.STROKE);
canvas.drawCircle(mStickCenter.x, mStickCenter.y, mMaxRange, mPaint);
mPaint.setStyle(Style.FILL);
}


6. 拖拽处理

思路分析:

a. 拖动超出最大范围,则断开; (Move事件)

b. 超出了最大范围松开手, 则消失; (Up事件)

c. 没超出了最大范围松开手: 断开后,又放回最大范围内,则恢复显示; (Up事件)

d. 没超出了最大范围松开手: 拖拽没有超出最大范围松开手,则弹回去; (Up事件)

6.1. 拖动超出最大范围,则断开

public boolean onTouchEvent(MotionEvent event) {
float radius;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mOutOfRange = false;
case MotionEvent.ACTION_MOVE:
...
// a. 拖动超出最大范围, 则断开;
radius = GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter);
if (radius > mMaxRange) {
mOutOfRange = true;
invalidate();
}
break;
}
protected void onDraw(Canvas canvas) {
...
// 2. 画拖动圆
canvas.drawCircle(mDragCenter.x, mDragCenter.y, mDragRadius, mPaint);
// 断开了则不绘制固定圆和连接部分
if (!mOutOfRange) {
// 1. 画固定圆
...
// 3. 画中间连接线
...
}
...
}


6.2 超出最大范围松开则消失

public boolean onTouchEvent(MotionEvent event) {
float radius;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDisappear = false;
case MotionEvent.ACTION_MOVE:
...
case MotionEvent.ACTION_UP:
// b. 在最大范围外松手,则消失;
radius = GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter);
if (radius > mMaxRange) { // 超出了最大范围
mDisappear = true;
invalidate();
} else {}
break;
}
// onDraw方法中:
// 消失了就三部分都不绘制了
if (!mDisappear) {
// 2、绘制拖动圆
canvas.drawCircle(mDragCenter.x, mDragCenter.y, mDragRadius, mPaint);
// 断开了则不绘制固定圆和连接部分
if (!mOutOfRange) {
// 1. 画固定圆
canvas.drawCircle(mStickCenter.x, mStickCenter.y, mStickRadius, mPaint);
// 3. 画中间连接线
...
}
}


6.3. 超出了最大范围松开手又放回去

public boolean onTouchEvent(MotionEvent event) {
...
case MotionEvent.ACTION_UP:
// b. 在最大范围外松手,则消失;
radius = GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter);
if (radius > mMaxRange) { // 超出最大范围
...
} else { // 没有超出了最大范围松开
if (mOutOfRange) { // 断开
// c. 断开后,又放回最大范围内松开,则恢复显示;
mDragCenter.set(mStickCenter.x, mStickCenter.y);
invalidate();
} else { // d. 拖拽没有超出最大范围松开手,则弹回去;
}
}
break;
}
return true;
}


6.4. 拖拽没有超出最大范围松开手则弹回去

public boolean onTouchEvent(MotionEvent event) {
...
case MotionEvent.ACTION_UP:
...
if (radius > mMaxRange) { // 超出了最大范围
...
} else { // 没有超出了最大范围松开
if (mOutOfRange) { //
// c. 断开后,又放回最大范围内松开,则恢复显示;
...
} else {
// d. 拖拽没有超出最大范围松开手,则弹回去;
final PointF start = new PointF(mDragCenter.x, mDragCenter.y);
final PointF end = mStickCenter;
ValueAnimator animator = ValueAnimator.ofFloat(1);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float percent = animation.getAnimatedFraction();
PointF pointByPercent = GeometryUtil.getPointByPercent(start, end, percent);
mDragCenter.set(pointByPercent);
invalidate();
}
});
animator.setInterpolator(new OvershootInterpolator(4));
animator.setDuration(300);
animator.start();
}
}
break;
}
return true;
}


7. 计算固定圆半径

protected void onDraw(Canvas canvas) {
// 计算固定圆半径: 在一定范围内,拖动圆和固定圆的距离越远,固定圆的半径越小
float radius = GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter);
float startRadius = mStickRadius;
float endRadius = mStickRadius * 0.2f;
if (radius > mMaxRange) {
radius = mMaxRange;
}
float percent = radius / mMaxRange;
float tempStickRadius = GeometryUtil.evaluateValue(percent, startRadius, endRadius);

// 下面要改两个地方,替换为tempStickRadius
...
}


以上是实现的主要代码:

完整源码:点击下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  聊天 红点 拖拽 QQ 清除