前些日子在网上看了大神的文章,是模仿qq的拖拽消息消失的效果,看起来很酷,因此就跟着作了起来,随便记录一下,以便之后复习,有兴趣的朋友也能够看一下。效果图以下:java
可想而知,须要自定义view,另外,还涉及到贝塞尔曲线,用到了一些基础的几何知识,如勾股定理和正弦和余弦等,关于贝塞尔曲线的知识,你们能够上网看详细资料,这里不作赘述,主要是有点烧脑,本人也有点懒O(∩_∩)O哈哈~下面贴代码:android
这个是自定义的view类:canvas
public class TextView extends FrameLayout { private PointF mStartPoint, mCurPoint; private int mRadius = 20; private Paint mPaint; private boolean mTouch; private boolean isanimationstart = false; private Path mPath; private android.widget.TextView textView; private int banjing; private double distance = 0; private ImageView imageView; public TextView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public TextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public TextView(Context context) { super(context); init(); } @Override protected void dispatchDraw(Canvas canvas) { canvas.saveLayer(new RectF(0, 0, getWidth(), getHeight()), mPaint, Canvas.ALL_SAVE_FLAG);//保存整个屏幕为一个层 canvas.drawCircle(mStartPoint.x, mStartPoint.y, banjing, mPaint);//在初始化的地方画圆,半径随着拉开的距离而变化 if (mTouch) { calculatePath();//计算贝塞尔曲线路径的定义方法 canvas.drawCircle(mCurPoint.x, mCurPoint.y, mRadius, mPaint);//在手指点击的地方画圆 canvas.drawPath(mPath, mPaint);//绘制路径 // 设置textview的位置为手指点击的位置的中心 textView.setX(mCurPoint.x - textView.getWidth() / 2); textView.setY(mCurPoint.y - textView.getHeight() / 2); } if (!mTouch) { if (((int) distance) > 250) { // 当拉开距离必定后执行消失动画,让textview消失 imageView.setX(textView.getX() - imageView.getWidth() / 2); imageView.setY(textView.getY() - imageView.getHeight() / 2); imageView.setVisibility(VISIBLE); textView.setVisibility(GONE); ((AnimationDrawable) imageView.getDrawable()).start();//执行逐帧动画 mPath.reset(); mPaint.reset();//此处进行重置 } else {//当拉开距离小于250时,让textview还原到初始位置,不消失 textView.setX(mStartPoint.x - textView.getWidth() / 2); textView.setY(mStartPoint.y - textView.getHeight() / 2); } } canvas.restore();//保存状态 super.dispatchDraw(canvas);//做用是绘制子控件,在上面的话会覆盖掉本身 } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { //判断手指触摸点是否在控件的矩形当中 Rect rects = new Rect(); int[] location = new int[2]; textView.getLocationOnScreen(location);//获得控件在屏幕中的坐标 //赋值给控件的矩形的四边 rects.left = location[0]; rects.top = location[1]; rects.right = textView.getWidth() + rects.left; rects.bottom = textView.getHeight() + rects.top; if (rects.contains((int) event.getRawX(), (int) event.getRawY())) { //若是点击位置在控件矩形当中,返回true mTouch = true; } } break; case MotionEvent.ACTION_UP: { // 手指拿起还原 mTouch = false; } break; } mCurPoint.set(event.getX(), event.getY());//定位当前手指点击的位置并传给mCurPoint postInvalidate();//更新,以后再次调用dispatchdraw方法 return true;//返回true,以即可以处理下一次的事件 } // 计算出四个切点的坐标并绘制出贝塞尔曲线 private void calculatePath() { float startx = mStartPoint.x; float starty = mStartPoint.y; float x = mCurPoint.x; float y = mCurPoint.y; float dx = x - startx; float dy = y - starty; double a = Math.atan(dy / dx);//a表示夹角α //计算出四个点的x,y的偏移量 float offsetx = (float) (banjing * Math.sin(a)); float offsety = (float) (banjing * Math.cos(a)); //分别计算出四个点的坐标 float x1 = startx + offsetx; float y1 = starty - offsety; float x2 = x + offsetx; float y2 = y - offsety; float x4 = startx - offsetx; float y4 = starty + offsety; float x3 = x - offsetx; float y3 = y + offsety; //计算控制点的坐标(为两个圆心的中点坐标) float controlx = (startx + x) / 2; float controly = (starty + y) / 2; //路径清空 mPath.reset(); mPath.moveTo(x1, y1);//移动点到第一个切点,即贝塞尔曲线的起始点 mPath.quadTo(controlx, controly, x2, y2); mPath.lineTo(x3, y3); mPath.quadTo(controlx, controly, x4, y4); mPath.lineTo(x1, y1); //两个圆心的距离 distance = Math.sqrt(Math.pow(x - startx, 2) + Math.pow(y - starty, 2)); banjing = (int) (mRadius - distance / 15);//能够随着拉开距离的变化而变化的半径 if (banjing < 3) { banjing = 0; isanimationstart = true; if (((int) distance) < 255) { isanimationstart = false; banjing = 3; } } } public void init() { mStartPoint = new PointF(200, 250); mCurPoint = new PointF(); mPaint = new Paint(); mPath = new Path(); mPaint.setColor(Color.RED); mPaint.setStyle(Paint.Style.FILL_AND_STROKE); //添加TextView并设置属性 RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); textView = new android.widget.TextView(getContext()); textView.setText("99+"); textView.setBackgroundResource(R.drawable.bg); textView.setTextSize(22); textView.setTextColor(Color.WHITE); textView.setLayoutParams(params); addView(textView); //添加一个imageview以便后面执行消失的逐帧动画并设置参数 imageView = new ImageView(getContext()); imageView.setImageResource(R.drawable.tip_anim); imageView.setLayoutParams(params); imageView.setVisibility(INVISIBLE); addView(imageView); } }
而后是布局文件XML:(没有多余的,就是把自定义的view设置到布局中)ide
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#3f4050" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:weightSum="1" tools:context="com.heyongrui.qqredpoint.MainActivity"> <com.heyongrui.qqredpoint.TextView android:id="@+id/custom" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:layout_below="@+id/view"></com.heyongrui.qqredpoint.TextView> </LinearLayout>
以上就是所有代码,只是个例子,因此没有其余的功能和代码。布局