自定义控件粘性控件.docx
《自定义控件粘性控件.docx》由会员分享,可在线阅读,更多相关《自定义控件粘性控件.docx(28页珍藏版)》请在冰豆网上搜索。
自定义控件粘性控件
自定义控件:
粘性控件
GooView粘性控件
了解几何图形工具的用法
掌握画不规则图形的方法
应用场景:
未读提醒,效果图:
绘制一帧的效果
画一帧粘性控件的步骤分析
1.画一个固定圆
2.画一个拖拽圆
3.画中间连接部分
将中间连接部分水平放置,四个角的坐标定为固定值,分别标记上点的编号,矩形中心的点为控件点,画曲线时用
自定义一个GooView继承View
publicclassGooViewextendsView{
privatePaintpaint;
publicGooView(Contextcontext){
this(context,null);
}
publicGooView(Contextcontext,AttributeSetattrs){
this(context,attrs,0);
}
publicGooView(Contextcontext,AttributeSetattrs,intdefStyle){
super(context,attrs,defStyle);
//初始化画笔
paint=newPaint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
}
@Override
protectedvoidonDraw(Canvascanvas){
super.onDraw(canvas);
//画中间连接部分
Pathpath=newPath();
//跳到点1,默认为(0f,0f)
path.moveTo(250f,250f);
//从点1->点2画曲线
path.quadTo(150f,300f,50f,250f);
//从点2->点3画直线
path.lineTo(50f,350f);
//从点3->点4画曲线
path.quadTo(150f,300f,250f,350f);
canvas.drawPath(path,paint);
//画拖拽圆
canvas.drawCircle(90f,90f,16f,paint);
//画固定圆
canvas.drawCircle(150f,150f,12f,paint);
}
}
第20-30行用Path画中间曲线部分
第25行quadTo(x1,y1,x2,y2)方法可以画当前所在点到x2,y2间的一条曲线,x1,y1是当前点与x2,y2间的一个控件点,它的位置决定曲线弯曲的方向和弧度,将GooView显示到MainActivity中
publicclassMainActivityextendsActivity{
@Override
protectedvoidonCreate(BundlesavedInstanceState){
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(newGooView(this));
}
}
贝塞尔曲线
二阶贝塞尔曲线,三阶贝塞尔曲线
分别给拖拽圆,固定圆的圆心,半径,两个附着点命名,修改GooView的onDraw()方法
protectedvoidonDraw(Canvascanvas){
super.onDraw(canvas);
//固定圆的两个附着点
PointF[]mStickPoints=newPointF[]{
newPointF(250f,250f),newPointF(250f,350f)
};
//固定圆的两个附着点
PointF[]mDragPoints=newPointF[]{
newPointF(50f,250f),newPointF(50f,350f)
};
//控制点
PointFmControlPoint=newPointF(150f,300f);
//画中间连接部分
Pathpath=newPath();
//跳到点1,默认为(0f,0f)
path.moveTo(mStickPoints[0].x,mStickPoints[0].y);
//从点1->点2画曲线
path.quadTo(mControlPoint.x,mControlPoint.y,mDragPoints[0].x,mDragPoints[0].y);
//从点2->点3画直线
path.lineTo(mDragPoints[1].x,mDragPoints[1].y);
//从点3->点4画曲线
path.quadTo(mControlPoint.x,mControlPoint.y,mStickPoints[1].x,mStickPoints[1].y);
canvas.drawPath(path,paint);
//画拖拽圆
//拖拽圆圆心
PointFmDragCenter=newPointF(90f,90f);
//拖拽圆半径
floatmDragRadius=16f;
canvas.drawCircle(mDragCenter.x,mDragCenter.y,mDragRadius,paint);
//画固定圆
//固定圆圆心
PointFmStickCenter=newPointF(150f,150f);
//固定圆半径
floatmStickRadius=12f;
canvas.drawCircle(mStickCenter.x,mStickCenter.y,mStickRadius,paint);
}
第3-14行替换附着点及控制点
第30-40行替换拖拽圆及固定圆的圆心及半径
将替换后的变量转换成GooView的成员变量
//固定圆圆心
PointFmStickCenter=newPointF(150f,150f);
//固定圆半径
floatmStickRadius=12f;
//拖拽圆圆心
PointFmDragCenter=newPointF(90f,90f);
//拖拽圆半径
floatmDragRadius=16f;
//固定圆的两个附着点
PointF[]mStickPoints=newPointF[]{newPointF(250f,250f),
newPointF(250f,350f)};
//固定圆的两个附着点
PointF[]mDragPoints=newPointF[]{newPointF(50f,250f),
newPointF(50f,350f)};
//控制点
PointFmControlPoint=newPointF(150f,300f);
@Override
protectedvoidonDraw(Canvascanvas){
super.onDraw(canvas);
//画中间连接部分
Pathpath=newPath();
//跳到点1,默认为(0f,0f)
path.moveTo(mStickPoints[0].x,mStickPoints[0].y);
//从点1->点2画曲线
path.quadTo(mControlPoint.x,mControlPoint.y,mDragPoints[0].x,
mDragPoints[0].y);
//从点2->点3画直线
path.lineTo(mDragPoints[1].x,mDragPoints[1].y);
//从点3->点4画曲线
path.quadTo(mControlPoint.x,mControlPoint.y,mStickPoints[1].x,
mStickPoints[1].y);
canvas.drawPath(path,paint);
//画拖拽圆
canvas.drawCircle(mDragCenter.x,mDragCenter.y,mDragRadius,paint);
//画固定圆
canvas.drawCircle(mStickCenter.x,mStickCenter.y,mStickRadius,paint);
}
拖拽圆和固定圆的圆心和半径已知,角3的正弦值为两圆心纵坐标之差比上横坐标之差,则角3的角度可知,则角1可知,AB,AC的长度即可计算出来,mDragPoints[0]的坐标可以计算出来,同理其它三个附着点坐标也可知。
mControlPoint为两圆心连线的中点
几何图形工具
/**
*几何图形工具
*/
publicclassGeometryUtil{
/**
*Asmeaningofmethodname.
*获得两点之间的距离
*@paramp0
*@paramp1
*@return
*/
publicstaticfloatgetDistanceBetween2Points(PointFp0,PointFp1){
floatdistance=(float)Math.sqrt(Math.pow(p0.y-p1.y,2)+
Math.pow(p0.x-p1.x,2));
returndistance;
}
/**
*Getmiddlepointbetweenp1andp2.
*获得两点连线的中点
*@paramp1
*@paramp2
*@return
*/
publicstaticPointFgetMiddlePoint(PointFp1,PointFp2){
returnnewPointF((p1.x+p2.x)/2.0f,(p1.y+p2.y)/2.0f);
}
/**
*Getpointbetweenp1andp2bypercent.
*根据百分比获取两点之间的某个点坐标
*@paramp1
*@paramp2
*@parampercent
*@return
*/
publicstaticPointFgetPointByPercent(PointFp1,PointFp2,floatpercent){
returnnewPointF(evaluateValue(percent,p1.x,p2.x),
evaluateValue(percent,p1.y,p2.y));
}
/**
*根据分度值,计算从start到end中,fraction位置的值。
fraction范围为0->1
*@paramfraction
*@paramstart
*@paramend
*@return
*/
publicstaticfloatevaluateValue(floatfraction,Numberstart,Numberend){
returnstart.floatValue()+(end.floatValue()-start.floatValue())*fraction;
}
/**
*Getthepointofintersectionbetweencircleandline.
*获取通过指定圆心,斜率为lineK的直线与圆的交点。
*
*@parampMiddleThecirclecenterpoint.
*@paramradiusThecircleradius.
*@paramlineKTheslopeoflinewhichcrossthepMiddle.
*@return
*/
publicstaticPointF[]getIntersectionPoints(PointFpMiddle,floatradius,DoublelineK){
PointF[]points=newPointF[2];
floatradian,xOffset=0,yOffset=0;
if(lineK!
=null){
radian=(float)Math.atan(lineK);
xOffset=(float)(Math.sin(radian)*radius);
yOffset=(float)(Math.cos(radian)*radius);
}else{
xOffset=radius;
yOffset=0;
}
points[0]=newPointF(pMiddle.x+xOffset,pMiddle.y-yOffset);
points[1]=newPointF(pMiddle.x-xOffset,pMiddle.y+yOffset);
returnpoints;
}
}
利用几何图形工具类计算四个附着点坐标及控件点坐标
protectedvoidonDraw(Canvascanvas){
super.onDraw(canvas);
floatyOffset=mStickCenter.y-mDragCenter.y;
floatxOffset=mStickCenter.x-mDragCenter.x;
DoublelineK=null;
if(xOffset!
=0){
//xOffset分母不能为0
lineK=(double)(yOffset/xOffset);
}
//计算四个附着点
mDragPoints=GeometryUtil.getIntersectionPoints(mDragCenter,
mDragRadius,lineK);
mStickPoints=GeometryUtil.getIntersectionPoints(mStickCenter,
mStickRadius,lineK);
//一个控制点
mControlPoint=GeometryUtil.getMiddlePoint(mDragCenter,mStickCenter);
//画中间连接部分
Pathpath=newPath();
//跳到点1,默认为(0f,0f)
path.moveTo(mStickPoints[0].x,mStickPoints[0].y);
//从点1->点2画曲线
path.quadTo(mControlPoint.x,mControlPoint.y,mDragPoints[0].x,
mDragPoints[0].y);
//从点2->点3画直线
path.lineTo(mDragPoints[1].x,mDragPoints[1].y);
//从点3->点4画曲线
path.quadTo(mControlPoint.x,mControlPoint.y,mStickPoints[1].x,
mStickPoints[1].y);
canvas.drawPath(path,paint);
//画拖拽圆
canvas.drawCircle(mDragCenter.x,mDragCenter.y,mDragRadius,paint);
//画固定圆
canvas.drawCircle(mStickCenter.x,mStickCenter.y,mStickRadius,paint);
}
第3-17行计算四个附着点及控制点坐标
1.4计算固定圆半径
GooView重写onSizeChanged()方法,计算状态栏高度
@Override
protectedvoidonSizeChanged(intw,inth,intoldw,intoldh){
super.onSizeChanged(w,h,oldw,oldh);
//获取状态栏的高度,传入一个显示在屏幕上的view即可
statusBarHeight=Utils.getStatusBarHeight(this);
}
Utils.Java
publicclassUtils{
publicstaticToastmToast;
publicstaticvoidshowToast(ContextmContext,Stringmsg){
if(mToast==null){
mToast=Toast.makeText(mContext,"",Toast.LENGTH_SHORT);
}
mToast.setText(msg);
mToast.show();
}
/**
*获取状态栏高度
*
*@paramv
*@return
*/
publicstaticintgetStatusBarHeight(Viewv){
if(v==null){
return0;
}
Rectframe=newRect();
v.getWindowVisibleDisplayFrame(frame);
returnframe.top;
}
}
修改onDraw()方法
protectedvoidonDraw(Canvascanvas){
super.onDraw(canvas);
floatyOffset=mStickCenter.y-mDragCenter.y;
floatxOffset=mStickCenter.x-mDragCenter.x;
DoublelineK=null;
if(xOffset!
=0){
//xOffset分母不能为0
lineK=(double)(yOffset/xOffset);
}
//计算四个附着点
mDragPoints=GeometryUtil.getIntersectionPoints(mDragCenter,
mDragRadius,lineK);
mStickPoints=GeometryUtil.getIntersectionPoints(mStickCenter,
mStickRadius,lineK);
//一个控制点
mControlPoint=GeometryUtil.getMiddlePoint(mDragCenter,mStickCenter);
//移动画布
canvas.save();
canvas.translate(0,-statusBarHeight);
//画中间连接部分
Pathpath=newPath();
//跳到点1,默认为(0f,0f)
path.moveTo(mStickPoints[0].x,mStickPoints[0].y);
//从点1->点2画曲线
path.quadTo(mControlPoint.x,mControlPoint.y,mDragPoints[0].x,
mDragPoints[0].y);
//从点2->点3画直线
path.lineTo(mDragPoints[1].x,mDragPoints[1].y);
//从点3->点4画曲线
path.quadTo(mControlPoint.x,mControlPoint.y,mStickPoints[1].x,
mStickPoints[1].y);
canvas.drawPath(path,paint);
//画拖拽圆
canvas.drawCircle(mDragCenter.x,mDragCenter.y,mDragRadius,paint);
//画固定圆
canvas.drawCircle(mStickCenter.x,mStickCenter.y,mStickRadius,paint);
canvas.restore();
}
第18-20行把画布向上移动状态栏的高度,移动前需要保存一下当前状态,做完操作后需要恢复一下状态,由于在onTouchEvent()中用的是getRawX(),getRawY()获取的是相对屏幕的坐标,所以GooView画图操作时需要向上移到一个状态栏的高度才能刚好和手指重合拖拽圆跟随手指移动时,随着拖拽与固定圆的距离的变大,固定圆的半径越来越小
//允许的最大距离
floatfarestDistance=80f;
/**
*通过两圆圆心的距离,计算固定圆的半径
*@return
*/
privatefloatcomputeStickRadius(){
//通过几何图形工具类可以计算出两圆圆心的距离,distance是可以大于80f;
floatdistance=GeometryUtil.getDistanceBetween2Points(mDragCenter,mStickCenter);
//需要的是0.0f->1.0f的值,所在大于80f让distance等于80f
distance=Math.min(farestDistance,distance);
floatpercent=distance/farestDistance;
//需要固定圆心半径在12f->3f间变化,可以利用类型估值器
returnevaluate(percent,mStickRadius,mStickRadius*0.25f);
}
//FloatEvaluator.java中拷贝
publicFloatevaluate(floatfraction,NumberstartValue,NumberendValue){
floatstartFloat=startValue.floatValue();
returnstartFloat+fraction*(endValue.floatValue()-startFloat);