且看个效果图:
要实现我们这样的效果,首先分析下,线条的绘制和中间圆圈的实现,以及文字的绘制。
float percentage = 0.0f;
patheffect effect = new dashpatheffect(new float[]{pathlength, pathlength}, pathlength - pathlength*percentage);
这里贴出animatedpathview的完整代码:
public class animatedpathview extends view {
private paint mpaint;
private path mpath;
private int mstrokecolor = color.parsecolor("#ff6c6c");
private int mstrokewidth = 8;
private float mprogress = 0f;
private float mpathlength = 0f;
private float circlex = 0f;
private float circley = 0f;
private int radius = 0;
private string pathtext="化妆包...";
private int textx,texty;
public animatedpathview(context context) {
this(context, null);
init();
}
public animatedpathview(context context, attributeset attrs) {
this(context, attrs, 0);
public animatedpathview(context context, attributeset attrs, int defstyle) {
super(context, attrs, defstyle);
typedarray a = context.obtainstyledattributes(attrs, r.styleable.animatedpathview);
mstrokecolor = a.getcolor(r.styleable.animatedpathview_pathcolor, color.parsecolor("#ff6c6c"));
mstrokewidth = a.getinteger(r.styleable.animatedpathview_pathwidth, 8);
a.recycle();
private void init() {
mpaint = new paint();
mpaint.setcolor(mstrokecolor);
mpaint.setstyle(paint.style.stroke);
mpaint.setstrokewidth(mstrokewidth);
mpaint.setantialias(true);
setpath(new path());
public void setpath(path p) {
mpath = p;
pathmeasure measure = new pathmeasure(mpath, false);
mpathlength = measure.getlength();
public void setpathtext(string pathtext,int textx,int texty ) {
this.pathtext=pathtext;
this.textx=textx;
this.texty=texty;
public void setpath(float[]... points) {
if (points.length == 0)
throw new illegalargumentexception("cannot have zero points in the line");
path p = new path();
p.moveto(points[0][0], points[0][1]);
for (int i = 1; i < points.length; i++) {
p.lineto(points[i][0], points[i][1]);
}
//将第一个xy坐标点作为绘制的原点
circlex = points[0][0] - radius / 2;
circley = points[0][1] - radius / 2;
setpath(p);
public void setpercentage(float percentage) {
if (percentage < 0.0f || percentage > 1.0f)
throw new illegalargumentexception("setpercentage not between 0.0f and 1.0f");
mprogress = percentage;
invalidate();
public void scalepathby(float x, float y) {
matrix m = new matrix();
m.postscale(x, y);
mpath.transform(m);
public void scalecircleradius(int radius) {
this.radius = radius;
@override
protected void ondraw(canvas canvas) {
super.ondraw(canvas);
//绘制圆形
// drawcircle(canvas);
//绘线条
drawpatheffect(canvas);
//绘制文字
drawtext(canvas);
canvas.restore();
private void drawtext(canvas canvas) {
mpaint.settextsize(28);
mpaint.setcolor(color.parsecolor("#ffffff"));
if (canvas!=null&& !textutils.isempty(pathtext)){
canvas.drawtext(pathtext,textx,texty,mpaint);
private void drawpatheffect(canvas canvas) {
patheffect patheffect = new dashpatheffect(new float[]{mpathlength, mpathlength}, (mpathlength - mpathlength * mprogress));
mpaint.setpatheffect(patheffect);
mpaint.setstrokewidth(4);
canvas.save();
canvas.translate(getpaddingleft(), getpaddingtop());
canvas.drawpath(mpath, mpaint);
private void drawcircle(canvas canvas) {
int strokenwidth = 25;
mpaint.setstrokewidth(strokenwidth);
canvas.drawcircle(circlex, circley, radius , mpaint);
protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
super.onmeasure(widthmeasurespec, heightmeasurespec);
int widthsize = measurespec.getsize(widthmeasurespec);
int heightsize = measurespec.getsize(heightmeasurespec);
int widthmode = measurespec.getmode(widthmeasurespec);
int heightmode = measurespec.getmode(widthmeasurespec);
int measuredwidth, measuredheight;
if (widthmode == measurespec.at_most)
throw new illegalstateexception("animatedpathview cannot have a wrap_content property");
else
measuredwidth = widthsize;
if (heightmode == measurespec.at_most)
measuredheight = heightsize;
setmeasureddimension(measuredwidth, measuredheight);
}
public class pointview extends framelayout {
private context mcontext;
private list<pointscalebean> points;
private framelayout layoupoints;
private animatedpathview animatedpath;
private int radius=10;
private string text="图文标签 $99.00";
public pointview(context context) {
public pointview(context context, attributeset attrs) {
public pointview(context context, attributeset attrs, int defstyleattr) {
super(context, attrs, defstyleattr);
initview(context, attrs);
private void initview(context context, attributeset attrs) {
this.mcontext = context;
view imgpointlayout = inflate(context, r.layout.layout_point, this);
layoupoints = (framelayout) imgpointlayout.findviewbyid(r.id.layoupoints);
animatedpath=(animatedpathview) imgpointlayout.findviewbyid(r.id.animated_path);
public void addpoints(int width, int height) {
addpoint(width, height);
public void setpoints(list<pointscalebean> points) {
this.points = points;
private void addpoint(int width, int height) {
layoupoints.removeallviews();
for (int i = 0; i < points.size(); i++) {
double width_scale = points.get(i).widthscale;
double height_scale = points.get(i).heightscale;
linearlayout view = (linearlayout) layoutinflater.from(mcontext).inflate(r.layout.layout_img_point, this, false);
imageview imageview = (imageview) view.findviewbyid(r.id.imgpoint);
imageview.settag(i);
animationdrawable animationdrawable = (animationdrawable) imageview.getdrawable();
animationdrawable.start();
layoutparams layoutparams = (layoutparams) view.getlayoutparams();
layoutparams.leftmargin = (int) (width * width_scale);
layoutparams.topmargin = (int) (height * height_scale);
// imageview.setonclicklistener(this);
layoupoints.addview(view, layoutparams);
initview();
initpathanimated();
private void initpathanimated() {
viewtreeobserver observer = animatedpath.getviewtreeobserver();
if(observer != null){
observer.addongloballayoutlistener(new viewtreeobserver.ongloballayoutlistener() {
@override
public void ongloballayout() {
animatedpath.getviewtreeobserver().removeglobalonlayoutlistener(this);
animatedpath.scalecircleradius(radius);
animatedpath.scalepathby(animatedpath.getwidth()/2,animatedpath.getheight()/2);
float[][] points = new float[][]{
{animatedpath.getwidth()/2-radius/2,animatedpath.getheight()/2-radius/2},
{animatedpath.getwidth()/2- uiutils.dp2px(mcontext,30), animatedpath.getheight()/2- uiutils.dp2px(mcontext,40)},
{animatedpath.getwidth()/2-uiutils.dp2px(mcontext,150), animatedpath.getheight()/2- uiutils.dp2px(mcontext,40)},
};
animatedpath.setpath(points);
// animatedpath.setpathtext(text,animatedpath.getwidth()/2-uiutils.dp2px(mcontext,150), animatedpath.getheight()/2- uiutils.dp2px(mcontext,50));
}
});
private void initview() {
animatedpath.setonclicklistener(new view.onclicklistener() {
@override
public void onclick(view view) {
objectanimator anim = objectanimator.offloat(view, "percentage", 0.0f, 1.0f);
anim.setduration(2000);
anim.setinterpolator(new linearinterpolator());
anim.start();
}
});
上面对应的布局和资源文件:
layou_point.xml
<?xml version="1.0" encoding="utf-8"?>
<framelayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<com.yju.app.widght.path.animatedpathview
android:id="@+id/animated_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<framelayout
android:id="@+id/layoupoints"
android:layout_gravity="center" />
</framelayout>
layout_img_point.xml
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<imageview
android:id="@+id/imgpoint"
android:layout_width="wrap_content"
android:src="@drawable/point_img" />
</linearlayout>
文中用到的anim就是帧动画了,
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@drawable/point_img1"
android:duration="100" />
....省略n多图片资源
android:drawable="@drawable/point_img13"
</animation-list>
而最后我们只需要在我们自己的mainactivity中添加简单的代码既可实现上面的效果:
private void initpointview() {
list<pointscalebean> list=new arraylist<>();
pointscalebean point=new pointscalebean();
point.widthscale = 0.36f;
point.heightscale = 0.75f;
list.add(point);
pointview.setpoints(list);
pointview.addpoints(viewgroup.layoutparams.match_parent,viewgroup.layoutparams.match_parent);
对于布局我是这么做的,将view的父布局的背景加一个图片,实际的开发中大家可以写一个相对的布局,这个就能实现实时的效果了,好了就写到这里,有疑问请留言或者加群(278792776)。