写在前面:
本文原创者醉悔今朝http://blog.csdn.net/qq_38911444/article/details/77963619
一开始又看了几篇写QQ红点消除功能的文章,基本上都差不多,这里给出其中一个的链接:http://blog.csdn.net/crazy__chen/article/details/49903475,但是这些文章都只是介绍了一下红点绘制以及拉伸、消除动画,并没有涉及到实际应用中去,也就是说,都只是在自己的这个View中演示红点消除功能动画而已,因此就有了我接下来自己写的一个Demo。
在我的Demo中,自定义的RedPointLayout是继承自LinearLayout,可以实现像QQ消息上红点类似的功能。只需要向RedPointLayout中传入一个红点的集合,就可以绘制出想要的任意个数红点,并且各红点之间相互独立。
效果图如下:
Demo地址http://git.oschina.net/ZuiHuiJinZhao/RedPointLayout_Demo
开始介绍:
1、内部类RedPoint
从名字就可以知道,这是一个红点类,每一个RedPoint实例就是一个需要绘制的红点,具体代码如下:
public class RedPoint{ /** 红点初始位置中心点坐标 */ private Point centerPoint; /** 红点小红点中心点坐标 */ private Point rePoint; /** 红点大红点中心点坐标 */ private Point toPoint; /** 红点的大小(即半径) */ private int size; /** 红点要展示的文字内容 */ private String text; /** 文字的大小 */ private int textSize; /** 文字的颜色 */ private int textColor; /** 红点的颜色 */ private int color; /** 红点的消除距离 */ private int distance; public RedPoint(){ centerPoint = new Point(0, 0); rePoint = new Point(0, 0); toPoint = new Point(0, 0); size = 20; text = "0"; textSize = 18; textColor = 0xffffffff; color = 0xffff318c; distance = 80; } public void setCenterPoint(Point centerPoint) { this.centerPoint = centerPoint; rePoint.set(centerPoint.x, centerPoint.y); toPoint.set(centerPoint.x, centerPoint.y); Log.i(TAG, "rePoint.x = " + rePoint.x + "rePoint.y = " + rePoint.y); Log.i(TAG, "toPoint.x = " + toPoint.x + "toPoint.y = " + toPoint.y); } public void setRePoint(Point redPoint) { this.rePoint = redPoint; } public void setTopoint(Point topoint) { this.toPoint = topoint; } public void setSize(int size) { this.size = size; } public void setText(String text) { this.text = text; } public void setTextSize(int textSize) { this.textSize = textSize; } public void setTextColor(int textColor) { try{ ColorStateList csl = ColorStateList.valueOf(textColor); this.textColor = csl.getColorForState(getDrawableState(), 0); }catch (Exception e){ this.textColor = textColor; } } public void setTextColor(ColorStateList textColor) { this.textColor = textColor.getColorForState(getDrawableState(), 0); } public void setColor(int color) { try{ ColorStateList csl = ColorStateList.valueOf(color); this.color = csl.getColorForState(getDrawableState(), 0); }catch (Exception e){ e.printStackTrace(); } } public void setColor(ColorStateList color) { this.color = color.getColorForState(getDrawableState(), 0); } public void setDistance(int distance) { this.distance = distance; } public Point getCenterPoint() { return centerPoint; } public Point getRePoint() { return rePoint; } public Point getToPoint() { return toPoint; } public int getSize() { return size; } public String getText() { return text; } public int getTextSize() { return textSize; } public int getTextColor() { return textColor; } public int getColor() { return color; } public int getDistance() { return distance; } }2、红点增删关键方法setRedPoint()、addRedPoint()、clearRedPoint()、removeRedPoint()
RedPointLayout中有一个专门用于存储红点的ArrayList,名为redPointArrayList,这几个方法都与其有关,用于添加或者删除红点
setRedPoint()可直接传入一个ArrayList
/** * 直接传入完整的redPointArrayList并刷新显示 * @param redPointArrayList */ public void setRedPoint(ArrayListredPointArrayList){ try { if(redPointArrayList == null){ return; } this.redPointArrayList.clear(); this.redPointArrayList = null; this.redPointArrayList = redPointArrayList; invalidate(); }catch (Exception e){ e.printStackTrace(); } }
addRedPoint()可直接在redPointArrayList中添加一个红点(可重复调用添加多个),RedPointLayout同样会自动刷新并绘制出所有的的所有红点
/** * 添加红点 * @param redPoint */ public void addRedPoint(RedPoint redPoint){ try { if(redPoint == null){ return; } redPointArrayList.add(redPoint); invalidate(); }catch (Exception e){ e.printStackTrace(); } }
clearRedPoint()可清除redPointArrayList中的所有红点,并刷新界面
public void clearRedPoint(){ try { if(redPointArrayList != null){ redPointArrayList.clear(); } }catch (Exception e){ e.printStackTrace(); } }
removeRedPoint()传入一个int类型的下标,可移除redPointArrayList中对应下标的红点,并刷新界面
/** * 根据下标移除红点 * @param index */ public void removeRedPoint(int index){ try { if(index > 0 && index < redPointArrayList.size()){ redPointArrayList.remove(index); } }catch (Exception e){ e.printStackTrace(); } }
3、红点及动画效果绘制
由于RedPointLayout是继承自LinearLayout,因此绘制操作通过重写dispatchDraw()实现,在该方法重调用如下方法
/** * 绘制红点 * @param canvas */ private void drawRedPointList(Canvas canvas){ //绘制所有红点 for(int i=0;i<redPointArrayList.size();i++){ RedPoint redPoint = redPointArrayList.get(i); mCanvasPaint.setColor(redPoint.getColor()); mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setTextSize(redPoint.getTextSize()); mTextPaint.setColor(redPoint.getTextColor()); Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt(); canvas.drawCircle(redPoint.getRePoint().x, redPoint.getRePoint().y, redPoint.getSize()/2, mCanvasPaint); canvas.drawCircle(redPoint.getToPoint().x, redPoint.getToPoint().y, redPoint.getSize(), mCanvasPaint); if(Math.sqrt((redPoint.getToPoint().x - redPoint.getRePoint().x)*(redPoint.getToPoint().x - redPoint.getRePoint().x) +(redPoint.getToPoint().y - redPoint.getRePoint().y)*(redPoint.getToPoint().y - redPoint.getRePoint().y)), redPoint); } //计算文字在红点中垂直居中时的y值 int baseline = redPoint.getToPoint().y + ( - fontMetrics.ascent - fontMetrics.descent) / 2; canvas.drawText(redPoint.getText(), redPoint.getToPoint().x, baseline, mTextPaint); } if(beng){ if(mCurExplosionIndex < mExplosionBitmaps.length){ //设置气泡爆炸图片的位置 Log.i(TAG, "mCurExplosionIndex = " + mCurExplosionIndex); mBitMapPaint.setFilterBitmap(true); //根据当前进行到爆炸气泡的位置index来绘制爆炸气泡bitmap canvas.drawBitmap(mExplosionBitmaps[mCurExplosionIndex], null, mExplosionRect, mBitMapPaint); }else{ beng = false; } } }
其中有做判断,当大红点(即红点当前位置)与小红点(即红点原位置)不在同一位置且尚未超出消除距离时,调用以下方法绘制出粘连效果:
/** * 通过贝塞尔曲线绘制大小红点之间的粘连效果 * @param canvas * @param redPoint */ private void drawBeiSaierLine(Canvas canvas, RedPoint redPoint){ Point p1 = getQieDian(redPoint.getRePoint(), redPoint.getToPoint(), redPoint.getSize()/2); Point p2 = getQieDian(redPoint.getToPoint(), redPoint.getRePoint(), redPoint.getSize()); Point p3 = getQieDian2(redPoint.getRePoint(), redPoint.getToPoint(), redPoint.getSize()/2); Point p4 = getQieDian2(redPoint.getToPoint(), redPoint.getRePoint(), redPoint.getSize()); mPath.reset(); mPath.moveTo(p1.x, p1.y); mPath.quadTo((redPoint.getToPoint().x + redPoint.getRePoint().x)/2, (redPoint.getToPoint().y + redPoint.getRePoint().y)/2, p4.x, p4.y); mPath.lineTo(p2.x, p2.y); mPath.quadTo((redPoint.getToPoint().x + redPoint.getRePoint().x)/2, (redPoint.getToPoint().y + redPoint.getRePoint().y)/2, p3.x, p3.y); canvas.drawPath(mPath, mCanvasPaint); }
4、触摸及红点的移动判断
因为红点是绘制在RedPointLayout上,而RedPointLayout是继承自LinearLayout,最终的表现效果是如同QQ消息的在某一条消息上显示的红点,而那条消息所在的控件又是可点击的,为了使触摸事件优先触发红点的效果,因此这里重写的是dispatchTouchEvent()而非onTouchEvent():
/** * 由于要使红点对触摸事件的响应优先于RedPointLayout的子控件,所以重写dispatchTouchEvent来处理触摸事件,并决定是否拦截 * 而不是重写onTouchEvent * @param event * @return */ @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: //获取触摸到的红点(-1为触摸位置没有红点) curMoveRedPointIndex = getCurMoveRedPointIndex(event.getX(), event.getY() + getScrollY()); if(curMoveRedPointIndex == -1){ return super.dispatchTouchEvent(event); }else{ //开始更新大红点到触摸位置,小红点留在原地 RedPoint curMoveRedPoint = redPointArrayList.get(curMoveRedPointIndex); redPointArrayList.remove(curMoveRedPointIndex); curMoveRedPoint.setTopoint(new Point((int)event.getX(), (int)event.getY() + getScrollY())); redPointArrayList.add(curMoveRedPointIndex, curMoveRedPoint); invalidate(); return true; } case MotionEvent.ACTION_MOVE: if(curMoveRedPointIndex == -1){ return super.dispatchTouchEvent(event); }else{ //更新红点位置 RedPoint curMoveRedPoint = redPointArrayList.get(curMoveRedPointIndex); redPointArrayList.remove(curMoveRedPointIndex); curMoveRedPoint.setTopoint(new Point((int)event.getX(), (int)event.getY() + getScrollY())); redPointArrayList.add(curMoveRedPointIndex, curMoveRedPoint); invalidate(); return true; } case MotionEvent.ACTION_UP: if(curMoveRedPointIndex == -1){ return super.dispatchTouchEvent(event); }else{ //更新红点位置,并判断大红点与小红点的距离,若小于消除距离(curMoveRedPoint.getDistance())则执行动画将大红点归位 //否则,消除所有红点,并将该红点从redPointArrayList中移除 RedPoint curMoveRedPoint = redPointArrayList.get(curMoveRedPointIndex); redPointArrayList.remove(curMoveRedPointIndex); curMoveRedPoint.setTopoint(new Point((int)event.getX(), (int)event.getY() + getScrollY())); if(Math.sqrt((curMoveRedPoint.getToPoint().x - curMoveRedPoint.getRePoint().x)*(curMoveRedPoint.getToPoint().x - curMoveRedPoint.getRePoint().x)+(curMoveRedPoint.getToPoint().y - curMoveRedPoint.getRePoint().y)*(curMoveRedPoint.getToPoint().y - curMoveRedPoint.getRePoint().y))i(TAG, "正在执行动画···"); beng = false; redPointArrayList.add(curMoveRedPointIndex, curMoveRedPoint); startAnimator(); } else{ Log.i(TAG, "正在执行爆炸···"); beng = true; mExplosionRect.set((int)(curMoveRedPoint.getToPoint().x - Math.sqrt(curMoveRedPoint.getSize() * curMoveRedPoint.getSize())), (int)(curMoveRedPoint.getToPoint().y - Math.sqrt(curMoveRedPoint.getSize() * curMoveRedPoint.getSize())) , (int)(curMoveRedPoint.getToPoint().x + Math.sqrt(curMoveRedPoint.getSize() * curMoveRedPoint.getSize())), (int)(curMoveRedPoint.getToPoint().y + Math.sqrt(curMoveRedPoint.getSize() * curMoveRedPoint.getSize()))); startBubbleDismissAnim(); } return true; } } return false; }
附上整个自定义控件的代码:
import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.animation.LinearInterpolator; import android.view.animation.PathInterpolator; import android.widget.ScrollView; import java.util.ArrayList; import redpoint.szm.com.R; /** * Created by:Sun ZhongMou on 2017/8/28 10:08 **/ @SuppressLint("newApi") public class RedPointLayout extends ScrollView { private static final int REDPOINT_VISIBLE = 0; private static final int REDPOINT_GONE = 1; private static final String TAG = "RedPointLayout"; private Paint mTextPaint; private Paint mBitMapPaint; private Paint mCanvasPaint; private Path mPath; private Rect mRect; private Rect mExplosionRect; private ArrayListredPointArrayList; private int curMoveRedPointIndex; /** 气泡爆炸动画资源 */ private int[] mExplosionDrawables = {R.drawable.explosion_one, R.drawable.explosion_two , R.drawable.explosion_three, R.drawable.explosion_four, R.drawable.explosion_five}; /** 气泡爆炸的bitmap数组 */ private Bitmap[] mExplosionBitmaps; /** 当前展示的气泡图下标 */ private int mCurExplosionIndex; /** 动画展示与否 */ private boolean beng; public RedPointLayout(Context context){ this(context, null); } public RedPointLayout(Context context, AttributeSet attrs){ this(context, attrs, 0); } public RedPointLayout(Context context, AttributeSet attrs , int defStyleAttr){ super(context, attrs, defStyleAttr); init(); } private void init(){ mPath = new Path(); mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBitMapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mCanvasPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setAntiAlias(true); mBitMapPaint.setAntiAlias(true); mCanvasPaint.setAntiAlias(true); mRect = new Rect(); mExplosionRect = new Rect(); redPointArrayList = new ArrayList (); mExplosionBitmaps = new Bitmap[mExplosionDrawables.length]; for (int i = 0; i < mExplosionDrawables.length; i++) { //将气泡爆炸的drawable转为bitmap Bitmap bitmap = BitmapFactory.decodeResource(getResources(), mExplosionDrawables[i]); mExplosionBitmaps[i] = bitmap; } } /** * 直接传入完整的redPointArrayList并刷新显示 * @param redPointArrayList */ public void setRedPoint(ArrayList redPointArrayList){ try { if(redPointArrayList == null){ return; } this.redPointArrayList.clear(); this.redPointArrayList = null; this.redPointArrayList = redPointArrayList; invalidate(); }catch (Exception e){ e.printStackTrace(); } } /** * 添加红点 * @param redPoint */ public void addRedPoint(RedPoint redPoint){ try { if(redPoint == null){ return; } redPointArrayList.add(redPoint); invalidate(); }catch (Exception e){ e.printStackTrace(); } } public void clearRedPoint(){ try { if(redPointArrayList != null){ redPointArrayList.clear(); } }catch (Exception e){ e.printStackTrace(); } } /** * 根据下标移除红点 * @param index */ public void removeRedPoint(int index){ try { if(index > 0 && index < redPointArrayList.size()){ redPointArrayList.remove(index); } }catch (Exception e){ e.printStackTrace(); } } public void reStart(){ invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); //获取当前控件大小以及位置 getLocalVisibleRect(mRect); } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); drawRedPointList(canvas); } /** * 绘制红点 * @param canvas */ private void drawRedPointList(Canvas canvas){ //绘制所有红点 for(int i=0;i<redPointArrayList.size();i++){ RedPoint redPoint = redPointArrayList.get(i); mCanvasPaint.setColor(redPoint.getColor()); mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setTextSize(redPoint.getTextSize()); mTextPaint.setColor(redPoint.getTextColor()); Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt(); canvas.drawCircle(redPoint.getRePoint().x, redPoint.getRePoint().y, redPoint.getSize()/2, mCanvasPaint); canvas.drawCircle(redPoint.getToPoint().x, redPoint.getToPoint().y, redPoint.getSize(), mCanvasPaint); if(Math.sqrt((redPoint.getToPoint().x - redPoint.getRePoint().x)*(redPoint.getToPoint().x - redPoint.getRePoint().x) +(redPoint.getToPoint().y - redPoint.getRePoint().y)*(redPoint.getToPoint().y - redPoint.getRePoint().y)) , redPoint); } //计算文字在红点中垂直居中时的y值 int baseline = redPoint.getToPoint().y + ( - fontMetrics.ascent - fontMetrics.descent) / 2; canvas.drawText(redPoint.getText(), redPoint.getToPoint().x, baseline, mTextPaint); } if(beng){ if(mCurExplosionIndex < mExplosionBitmaps.length){ //设置气泡爆炸图片的位置 Log.i(TAG, "mCurExplosionIndex = " + mCurExplosionIndex); mBitMapPaint.setFilterBitmap(true); //根据当前进行到爆炸气泡的位置index来绘制爆炸气泡bitmap canvas.drawBitmap(mExplosionBitmaps[mCurExplosionIndex], null, mExplosionRect, mBitMapPaint); }else{ beng = false; } } } /** * 通过贝塞尔曲线绘制大小红点之间的粘连效果 * @param canvas * @param redPoint */ private void drawBeiSaierLine(Canvas canvas, RedPoint redPoint){ Point p1 = getQieDian(redPoint.getRePoint(), redPoint.getToPoint(), redPoint.getSize()/2); Point p2 = getQieDian(redPoint.getToPoint(), redPoint.getRePoint(), redPoint.getSize()); Point p3 = getQieDian2(redPoint.getRePoint(), redPoint.getToPoint(), redPoint.getSize()/2); Point p4 = getQieDian2(redPoint.getToPoint(), redPoint.getRePoint(), redPoint.getSize()); mPath.reset(); mPath.moveTo(p1.x, p1.y); mPath.quadTo((redPoint.getToPoint().x + redPoint.getRePoint().x)/2, (redPoint.getToPoint().y + redPoint.getRePoint().y)/2, p4.x, p4.y); mPath.lineTo(p2.x, p2.y); mPath.quadTo((redPoint.getToPoint().x + redPoint.getRePoint().x)/2, (redPoint.getToPoint().y + redPoint.getRePoint().y)/2, p3.x, p3.y); canvas.drawPath(mPath, mCanvasPaint); } /** * 检查触摸点是否在红点范围内,如果再红点范围内,则返回具体的红点在redPointArrayList中的index * PS:此处考虑到红点一般比较小,因此设置为在红点2倍半径内都做响应 * @param x * @param y * @return */ private int getCurMoveRedPointIndex(float x, float y){ for(int i=0;i<redPointArrayList.size();i++){ RedPoint redPoint = redPointArrayList.get(i); if(Math.sqrt((redPoint.getToPoint().x - x)*(redPoint.getToPoint().x - x)+(redPoint.getToPoint().y - y)*(redPoint.getToPoint().y - y)) 2){ return i; } } return -1; } /** * 由于要使红点对触摸事件的响应优先于RedPointLayout的子控件,所以重写dispatchTouchEvent来处理触摸事件,并决定是否拦截 * 而不是重写onTouchEvent * @param event * @return */ @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: //获取触摸到的红点(-1为触摸位置没有红点) curMoveRedPointIndex = getCurMoveRedPointIndex(event.getX(), event.getY() + getScrollY()); if(curMoveRedPointIndex == -1){ return super.dispatchTouchEvent(event); }else{ //开始更新大红点到触摸位置,小红点留在原地 RedPoint curMoveRedPoint = redPointArrayList.get(curMoveRedPointIndex); redPointArrayList.remove(curMoveRedPointIndex); curMoveRedPoint.setTopoint(new Point((int)event.getX(), (int)event.getY() + getScrollY())); redPointArrayList.add(curMoveRedPointIndex, curMoveRedPoint); invalidate(); return true; } case MotionEvent.ACTION_MOVE: if(curMoveRedPointIndex == -1){ return super.dispatchTouchEvent(event); }else{ //更新红点位置 RedPoint curMoveRedPoint = redPointArrayList.get(curMoveRedPointIndex); redPointArrayList.remove(curMoveRedPointIndex); curMoveRedPoint.setTopoint(new Point((int)event.getX(), (int)event.getY() + getScrollY())); redPointArrayList.add(curMoveRedPointIndex, curMoveRedPoint); invalidate(); return true; } case MotionEvent.ACTION_UP: if(curMoveRedPointIndex == -1){ return super.dispatchTouchEvent(event); }else{ //更新红点位置,并判断大红点与小红点的距离,若小于消除距离(curMoveRedPoint.getDistance())则执行动画将大红点归位 //否则,消除所有红点,并将该红点从redPointArrayList中移除 RedPoint curMoveRedPoint = redPointArrayList.get(curMoveRedPointIndex); redPointArrayList.remove(curMoveRedPointIndex); curMoveRedPoint.setTopoint(new Point((int)event.getX(), (int)event.getY() + getScrollY())); if(Math.sqrt((curMoveRedPoint.getToPoint().x - curMoveRedPoint.getRePoint().x)*(curMoveRedPoint.getToPoint().x - curMoveRedPoint.getRePoint().x)+(curMoveRedPoint.getToPoint().y - curMoveRedPoint.getRePoint().y)*(curMoveRedPoint.getToPoint().y - curMoveRedPoint.getRePoint().y)) i(TAG, "正在执行动画···"); beng = false; redPointArrayList.add(curMoveRedPointIndex, curMoveRedPoint); startAnimator(); } else{ Log.i(TAG, "正在执行爆炸···"); beng = true; mExplosionRect.set((int)(curMoveRedPoint.getToPoint().x - Math.sqrt(curMoveRedPoint.getSize() * curMoveRedPoint.getSize())), (int)(curMoveRedPoint.getToPoint().y - Math.sqrt(curMoveRedPoint.getSize() * curMoveRedPoint.getSize())) , (int)(curMoveRedPoint.getToPoint().x + Math.sqrt(curMoveRedPoint.getSize() * curMoveRedPoint.getSize())), (int)(curMoveRedPoint.getToPoint().y + Math.sqrt(curMoveRedPoint.getSize() * curMoveRedPoint.getSize()))); startBubbleDismissAnim(); } return true; } } return false; } private void startAnimator(){ //通过ValueAnimator更新redPointArrayList中当前移动的红点坐标信息,并刷新 RedPoint curMoveRedPoint = redPointArrayList.get(curMoveRedPointIndex); ValueAnimator va1 = ValueAnimator.ofInt(curMoveRedPoint.getToPoint().x, curMoveRedPoint.getRePoint().x); ValueAnimator va2 = ValueAnimator.ofInt(curMoveRedPoint.getToPoint().y, curMoveRedPoint.getRePoint().y); va1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { RedPoint curMoveRedPoint = redPointArrayList.get(curMoveRedPointIndex); redPointArrayList.remove(curMoveRedPointIndex); curMoveRedPoint.getToPoint().set((int)animation.getAnimatedValue(), curMoveRedPoint.getToPoint().y); redPointArrayList.add(curMoveRedPointIndex, curMoveRedPoint); invalidate(); } }); va2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { RedPoint curMoveRedPoint = redPointArrayList.get(curMoveRedPointIndex); redPointArrayList.remove(curMoveRedPointIndex); curMoveRedPoint.getToPoint().set(curMoveRedPoint.getToPoint().x, (int)animation.getAnimatedValue()); redPointArrayList.add(curMoveRedPointIndex, curMoveRedPoint); invalidate(); } }); AnimatorSet as = new AnimatorSet(); as.setDuration(200); as.play(va1).with(va2); as.setInterpolator(new PathInterpolator(0.45f, 1.98f, 0.85f, 0.58f)); as.start(); } /** * 设置气泡消失的动画 */ private void startBubbleDismissAnim() { //做一个int型属性动画,从0开始,到气泡爆炸图片数组个数结束 ValueAnimator anim = ValueAnimator.ofInt(0, mExplosionDrawables.length); anim.setInterpolator(new LinearInterpolator()); anim.setDuration(1000); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //拿到当前的值并重绘 Log.i(TAG, "mCurExplosionIndex···" + mCurExplosionIndex); mCurExplosionIndex = (int) animation.getAnimatedValue(); invalidate(); } }); anim.start(); } public Point getQieDian(Point ptCenter, Point ptOutside, double dbRadious) { Point E = new Point(),F = new Point(),G = new Point(),H = new Point(); double r = dbRadious; //1. 坐标平移到圆心ptCenter处,求园外点的新坐标E E.x = ptOutside.x-ptCenter.x; E.y = ptOutside.y-ptCenter.y; //平移变换到E //2. 求园与OE的交点坐标F, 相当于E的缩放变换 double t= r / Math.sqrt(E.x * E.x + E.y * E.y); //得到缩放比例 F.x = (int)(E.x * t); F.y = (int)(E.y * t); //缩放变换到F //3. 将E旋转变换角度a到切点G,其中cos(a)=r/OF=t, 所以a=arccos(t); double a = Math.acos(t); //得到旋转角度 G.x = (int)(F.x*Math.cos(a) -F.y*Math.sin(a)); G.y = (int)(F.x*Math.sin(a) +F.y*Math.cos(a)); //旋转变换到G //4. 将G平移到原来的坐标下得到新坐标H H.x= G.x+ptCenter.x; H.y= G.y+ptCenter.y; //平移变换到H //5. 返回H return H; //6. 实际应用过程中,只要一个中间变量E,其他F,G,H可以不用。 } public Point getQieDian2(Point ptCenter, Point ptOutside, double dbRadious) { Point E = new Point(),F = new Point(),G = new Point(),H = new Point(); double r = dbRadious; //1. 坐标平移到圆心ptCenter处,求园外点的新坐标E E.x = ptOutside.x-ptCenter.x; E.y = ptOutside.y-ptCenter.y; //平移变换到E //2. 求园与OE的交点坐标F, 相当于E的缩放变换 double t= r / Math.sqrt(E.x * E.x + E.y * E.y); //得到缩放比例 F.x = (int)(E.x * t); F.y = (int)(E.y * t); //缩放变换到F //3. 将E旋转变换角度a到切点G,其中cos(a)=r/OF=t, 所以a=arccos(t); double a = -Math.acos(t); //得到旋转角度 G.x = (int)(F.x*Math.cos(a) -F.y*Math.sin(a)); G.y = (int)(F.x*Math.sin(a) +F.y*Math.cos(a)); //旋转变换到G //4. 将G平移到原来的坐标下得到新坐标H H.x= G.x+ptCenter.x; H.y= G.y+ptCenter.y; //平移变换到H //5. 返回H return H; //6. 实际应用过程中,只要一个中间变量E,其他F,G,H可以不用。 } public RedPoint createRedPoint(){ return new RedPoint(); } public class RedPoint{ /** 红点初始位置中心点坐标 */ private Point centerPoint; /** 红点小红点中心点坐标 */ private Point rePoint; /** 红点大红点中心点坐标 */ private Point toPoint; /** 红点的大小(即半径) */ private int size; /** 红点要展示的文字内容 */ private String text; /** 文字的大小 */ private int textSize; /** 文字的颜色 */ private int textColor; /** 红点的颜色 */ private int color; /** 红点的消除距离 */ private int distance; public RedPoint(){ centerPoint = new Point(0, 0); rePoint = new Point(0, 0); toPoint = new Point(0, 0); size = 20; text = "0"; textSize = 18; textColor = 0xffffffff; color = 0xffff318c; distance = 80; } public void setCenterPoint(Point centerPoint) { this.centerPoint = centerPoint; rePoint.set(centerPoint.x, centerPoint.y); toPoint.set(centerPoint.x, centerPoint.y); Log.i(TAG, "rePoint.x = " + rePoint.x + "rePoint.y = " + rePoint.y); Log.i(TAG, "toPoint.x = " + toPoint.x + "toPoint.y = " + toPoint.y); } public void setRePoint(Point redPoint) { this.rePoint = redPoint; } public void setTopoint(Point topoint) { this.toPoint = topoint; } public void setSize(int size) { this.size = size; } public void setText(String text) { this.text = text; } public void setTextSize(int textSize) { this.textSize = textSize; } public void setTextColor(int textColor) { try{ ColorStateList csl = ColorStateList.valueOf(textColor); this.textColor = csl.getColorForState(getDrawableState(), 0); }catch (Exception e){ this.textColor = textColor; } } public void setTextColor(ColorStateList textColor) { this.textColor = textColor.getColorForState(getDrawableState(), 0); } public void setColor(int color) { try{ ColorStateList csl = ColorStateList.valueOf(color); this.color = csl.getColorForState(getDrawableState(), 0); }catch (Exception e){ e.printStackTrace(); } } public void setColor(ColorStateList color) { this.color = color.getColorForState(getDrawableState(), 0); } public void setDistance(int distance) { this.distance = distance; } public Point getCenterPoint() { return centerPoint; } public Point getRePoint() { return rePoint; } public Point getToPoint() { return toPoint; } public int getSize() { return size; } public String getText() { return text; } public int getTextSize() { return textSize; } public int getTextColor() { return textColor; } public int getColor() { return color; } public int getDistance() { return distance; } } }
第一次写文章,描述不够清晰,欢迎提出意见与建议,觉得作者写的不好,请轻喷(*^▽^*)