本文实例为大家分享了Android自定义View实现QQ消息气泡的具体代码,供大家参考,具体内容如下
效果图:
原理:
控件源码:
public class DragView extends View {
private int defaultZoomSize = 8;
//初始化圆的大小
private int initRadius;
//圆1的圆心位置
private PointF center1;
private PointF center2;
private PointF point1;
private PointF point2;
private PointF point3;
private PointF point4;
private int mWidth;
private int mHeight;
private float realZoomSize;
private float currentRadius;
private float minRadiusScale = 1 / 2f;
private Paint paint;
private Path path;
private Bitmap bitmap;
@DragStatus
private int mDragStatus;
public DragView(Context context) {
this(context, null);
}
public DragView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public DragView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(4);
paint.setAntiAlias(true);
path = new Path();
center1 = new PointF();
center2 = new PointF();
point1 = new PointF();
point2 = new PointF();
point3 = new PointF();
point4 = new PointF();
bitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.icon_pot);
initRadius = Math.min(bitmap.getWidth(), bitmap.getHeight()) / 2;
Log.e("zhen", "解析bitmap: " + bitmap.getWidth() + " * " + bitmap.getHeight() + " * " + initRadius);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
center1.set(mWidth / 2, mHeight / 2);
Log.d("zhen", "圆心位置:x" + center1.x + " y: " + center1.y);
}
private boolean isSelected = false;
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (Math.sqrt(Math.pow(x - center1.x, 2) + Math.pow(y - center1.y, 2)) < initRadius
&& mDragStatus == DragStatus.NORMAL) {
inAnimation = false;
isSelected = true;
Log.e("zhen", "选中状态");
}
break;
case MotionEvent.ACTION_MOVE:
if (isSelected) {
// Log.d("zhen", "拖动距离: " + dragDistance);
if (mDragStatus != DragStatus.DRAG_BACK && mDragStatus != DragStatus.DRAG_TO) {
mDragStatus = DragStatus.DRAG_MOVE;
center2.set(x, y);
float dragDistance = (float) (Math.sqrt(Math.pow(center2.x - center1.x, 2)
+ Math.pow(center2.y - center1.y, 2)));
//多少倍圆的大小
realZoomSize = dragDistance / initRadius;
invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
if (isSelected) {
if (realZoomSize <= defaultZoomSize) {
//回弹,改变center2.x, center2.y直到等于center1.x, center1.y
doAnimation(DragStatus.DRAG_BACK, center2, center1);
}
}
isSelected = false;
break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//圆的半径改变
currentRadius = initRadius * (1 + (minRadiusScale - 1) / defaultZoomSize * realZoomSize);
if (realZoomSize > defaultZoomSize) {
//圆缩小为一半,去往目的地,就应该消失了
doAnimation(DragStatus.DRAG_TO, center1, center2);
}
//中间矩形
// paint.setColor(Color.BLACK);
float angle = (float) Math.atan((center2.y - center1.y) / (center2.x - center1.x));
float sinValue;
float cosValue;
float controlX;
float controlY;
sinValue = (float) Math.abs((currentRadius * Math.sin(angle)));
cosValue = (float) Math.abs((currentRadius * Math.cos(angle)));
controlX = (center1.x + center2.x) / 2;
controlY = (center1.y + center2.y) / 2;
point1.set(center1.x - sinValue, center1.y - cosValue);
point2.set(center1.x + sinValue, center1.y + cosValue);
point3.set(center2.x - sinValue, center2.y - cosValue);
point4.set(center2.x + sinValue, center2.y + cosValue);
path.reset();
switch (mDragStatus) {
case DragStatus.NORMAL:
currentRadius = initRadius;
//原始图片
canvas.drawBitmap(bitmap, center1.x - initRadius, center1.y - initRadius, paint);
//起始位置的圆
// paint.setColor(Color.RED);
// canvas.drawCircle(center1.x, center1.y, currentRadius, paint);
break;
case DragStatus.DRAG_MOVE:
//拖动过程中
path.moveTo(point1.x, point1.y);
path.lineTo(point2.x, point2.y);
path.quadTo(controlX, controlY, point4.x, point4.y);
path.lineTo(point3.x, point3.y);
path.quadTo(controlX, controlY, point1.x, point1.y);
canvas.drawPath(path, paint);
//起始位置的圆
paint.setColor(Color.RED);
canvas.drawCircle(center1.x, center1.y, currentRadius, paint);
//结束位置的圆
// paint.setColor(Color.BLUE);
// canvas.drawCircle(center2.x, center2.y, currentRadius, paint);
//原始图片
canvas.drawBitmap(bitmap, center2.x - initRadius, center2.y - initRadius, paint);
break;
case DragStatus.DRAG_BACK:
//改变center2.x, center2.y直到等于center1.x, center1.y
path.reset();
path.moveTo(point1.x, point1.y);
path.quadTo(center2.x, center2.y, point2.x, point2.y);
canvas.drawPath(path, paint);
//起始位置的圆
// paint.setColor(Color.RED);
// canvas.drawCircle(center1.x, center1.y, currentRadius, paint);
//原始图片
canvas.drawBitmap(bitmap, center1.x - initRadius, center1.y - initRadius, paint);
break;
case DragStatus.DRAG_TO:
//改变center1.x, center1.y,直到等于center2.x, center2.y
path.reset();
path.moveTo(point3.x, point3.y);
path.quadTo(center1.x, center1.y, point4.x, point4.y);
canvas.drawPath(path, paint);
// //起始位置的圆
// paint.setColor(Color.RED);
// canvas.drawCircle(center1.x, center1.y, currentRadius, paint);
// //结束位置的圆
// paint.setColor(Color.BLUE);
// canvas.drawCircle(center2.x, center2.y, currentRadius, paint);
//原始图片
canvas.drawBitmap(bitmap, center2.x - initRadius, center2.y - initRadius, paint);
break;
}
// Log.d("zhen", "dragStatus: " + mDragStatus + " 圆1:" + center1 + " 圆2:" + center2 + " 半径: " + currentRadius);
// Log.w("zhen", "dragStatus: " + mDragStatus + " point3:" + point3 + " point4" + point4 + " sinValue " + sinValue + " cosValue " + cosValue);
Log.w("zhen", "dragStatus: " + mDragStatus + " 圆1:" + center1 + " 圆2:" + center2 + " 半径: " + currentRadius);
}
int i = 0;
private boolean inAnimation = false;
private void doAnimation(int dragStatus, final PointF startPoint, final PointF endPoint) {
if (inAnimation) return;
inAnimation = true;
final int step = 10;
final float stepx = (endPoint.x - startPoint.x) / step;
final float stepy = (endPoint.y - startPoint.y) / step;
i = 1;
mDragStatus = dragStatus;
Log.d("zhen", "dragStatus: " + mDragStatus + " startPoint:" + startPoint
+ " endPoint:" + endPoint + " stepx: " + stepy + " stepx: " + stepy);
new Thread(new Runnable() {
@Override
public void run() {
while (i <= step) {
startPoint.x += stepx;
startPoint.y += stepy;
postInvalidate();
i++;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mDragStatus = DragStatus.NORMAL;
invalidate();
Log.e("zhen", "恢复为可拖动状态");
}
}).start();
}
@IntDef({DragStatus.DRAG_MOVE, DragStatus.DRAG_TO, DragStatus.DRAG_BACK})
public @interface DragStatus {
int NORMAL = 0;
//拖动中
int DRAG_MOVE = 1;
//
int DRAG_TO = 2;
//回弹
int DRAG_BACK = 3;
}
}