模仿掌上英雄联盟能力分析效果
原图: 实现:
先计算出圆心到A点的坐标,
在计算出旋转的角度(360/7),
然后旋转7次Canvas,绘制7条中心线。
为了加深显示效果,先绘制一个绿色背景作参考。
2.绘制最外层正七边形
各坐标点的计算主要用到了三角函数。
A点
x : 对应圆心center。
y :paddingTop+2个字体高度的距离
B点
x : A点的x坐标+EB
利用三角函数公式,BE=sin(AB的夹角)*OB
这里有个坑,Math.sin(x)里的x是弧度,而不是角度。
如果要计算角度则需要加上Math.sin(Math.toRadians(x))
center+ Math.sin(Math.toRadians(360/7)) *one_radius
y :A点的y坐标+AE
同理AE=OA-OE,OE=cos(AB的夹角)*OB
OE=cos(AB的夹角)*OB
Math.abs(Math.cos(Math.toRadians(360/7)) *one_radius
由于余弦有正负值,这里要取绝对值
AE=OA-OE
(getPaddingTop() +2*str_rect.height() +one_radius
C点
x :圆心X+FC
Math.sin(Math.toRadians(360/7+360/7/2)) *one_radius
y: 圆心Y+OF
(Math.cos(Math.toRadians(360/7+360/7/2)) *one_radius) +center
D点
x :圆心x+HD
center+ Math.sin(Math.toRadians(360/7/2)) *one_radius
y:圆心y+OH
Math.cos(Math.toRadians(360/7/2)) *one_radius) +center
右边点绘制完后,左边点自然就简单了,y位置一样,x位置只需要把相加改成相减即可。
绘制完最外层七边形后,剩下来的就好办了,只需要计算出每个七边形的间距即可。
由原型图分析,每一个正七边形占半径的四分之一。
1.MainActivity.java
package com.example.administrator.polygonsview; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.SeekBar; public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener{ Polygons pv; SeekBar s1,s2,s3,s4,s5,s6,s7; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); pv=(Polygons)findViewById(R.id.pv); s1=(SeekBar)findViewById(R.id.sb1); s1.setOnSeekBarChangeListener(this); s2=(SeekBar)findViewById(R.id.sb2); s2.setOnSeekBarChangeListener(this); s3=(SeekBar)findViewById(R.id.sb3); s3.setOnSeekBarChangeListener(this); s4=(SeekBar)findViewById(R.id.sb4); s4.setOnSeekBarChangeListener(this); s5=(SeekBar)findViewById(R.id.sb5); s5.setOnSeekBarChangeListener(this); s6=(SeekBar)findViewById(R.id.sb6); s6.setOnSeekBarChangeListener(this); s7=(SeekBar)findViewById(R.id.sb7); s7.setOnSeekBarChangeListener(this); } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { float value=(float)(seekBar.getProgress()/10.0); switch (seekBar.getId()){ case R.id.sb1: pv.setValue1(value); break; case R.id.sb2: pv.setValue2(value); break; case R.id.sb3: pv.setValue3(value); break; case R.id.sb4: pv.setValue4(value); break; case R.id.sb5: pv.setValue5(value); break; case R.id.sb6: pv.setValue6(value); break; case R.id.sb7: pv.setValue7(value); break; } } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }
2.自定义View Polygons.java
package com.example.administrator.polygonsview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.view.View; /** * Created by Administrator on 2016/11/17. */ public class Polygons extends View { private int center; //中心点 private float one_radius; //外层菱形圆半径 private float distance;//多边形之间的间距 private int defaultSize=300; //默认大小 private Rect str_rect; //字体矩形 private String[] str={"击杀", "生存", "助攻", "物理", "魔法", "防御", "金钱"}; private Paint center_paint; //中心线画笔 private Paint str_paint; //字体画笔 private Paint one_paint; //最外层多边形画笔 private Paint two_paint; //第二层多边形画笔 private Paint three_paint; //第三次 private Paint four_paint; //第四层 private Paint rank_Paint; //等级进度画笔 private Context mContext; private Paint paint; private float f1,f2,f3,f4,f5,f6,f7; public Polygons(Context context) { super(context); this.mContext=context; //mContext要在构造函数中进行初始化 init(); } public Polygons(Context context, AttributeSet attrs) { super(context, attrs); this.mContext=context; init(); } public Polygons(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.mContext=context; init(); } private void init(){ defaultSize=dp_px(defaultSize); //初始化字体画笔 str_paint=new Paint(); str_paint.setAntiAlias(true); str_paint.setColor(Color.BLACK); str_paint.setTextSize(dp_px(16)); str_rect=new Rect(); str_paint.getTextBounds(str[0],0,str[0].length(),str_rect); //计算文字所在的矩形,可以得到宽高。宽:rect.width() 高: rect.height(); //初始化中心线画笔 center_paint=new Paint(); center_paint.setAntiAlias(true); //抗锯齿 center_paint.setColor(Color.WHITE); //初始化最外层多边形画笔 one_paint=new Paint(); one_paint.setAntiAlias(true); //getResources().getColor(R.color.one) 此用法getColor已经过时, ContextCompat.getColor(context,R.color.one) 由此方法代替,获取资源颜色 one_paint.setColor(ContextCompat.getColor(mContext,R.color.one)); one_paint.setStyle(Paint.Style.FILL); //设置空心 //初始化第二层画笔 two_paint=new Paint(); two_paint.setAntiAlias(true); two_paint.setColor(ContextCompat.getColor(mContext,R.color.two)); two_paint.setStyle(Paint.Style.FILL); //初始化第三层多边形画笔 three_paint = new Paint(); three_paint.setAntiAlias(true); three_paint.setColor(ContextCompat.getColor(mContext,R.color.three)); three_paint.setStyle(Paint.Style.FILL); //初始化最内层多边形画笔 four_paint = new Paint(); four_paint.setAntiAlias(true); four_paint.setColor(ContextCompat.getColor(mContext,R.color.four)); four_paint.setStyle(Paint.Style.FILL); //等级进度画笔 rank_Paint=new Paint(); rank_Paint.setAntiAlias(true); rank_Paint.setColor(Color.RED); rank_Paint.setStrokeWidth(8); //该方法用于设置画笔的空心线宽 rank_Paint.setStyle(Paint.Style.STROKE); //设置空心 paint=new Paint(); } public void setValue1(float value){ f1=one_radius-one_radius/4 * value; invalidate(); } public void setValue2(float value){ f2=one_radius-one_radius/4 * value; invalidate(); } public void setValue3(float value){ f3=one_radius-one_radius/4 * value; invalidate(); } public void setValue4(float value){ f4=one_radius-one_radius/4 * value; invalidate(); } public void setValue5(float value){ f5=one_radius-one_radius/4 * value; invalidate(); } public void setValue6(float value){ f6=one_radius-one_radius/4 * value; invalidate(); } public void setValue7(float value){ f7=one_radius-one_radius/4 * value; invalidate(); } @Override protected void onDraw(Canvas canvas) { paintStr(canvas); onePolygons(canvas); twoPolygons(canvas); centerLine(canvas); paintRank(canvas); super.onDraw(canvas); } //绘制七边形的中心线 private void centerLine(Canvas canvas){ canvas.save(); //保存当前状态 canvas.rotate(0,center,center); float startY=getPaddingTop()+2*str_rect.height(); float endY=center; float du=(float) (360/7 + 0.5); for(int i=0;i<7;i++){ canvas.drawLine(center,startY,center,endY,center_paint); //画直线 canvas.rotate(du,center,center); //旋转画布,实现循环依次画中心线 } canvas.restore(); //恢复之前状态 } //绘制字体 private void paintStr(Canvas canvas){ canvas.drawText(str[0],center - str_rect.width()/2,(float) (getPaddingTop()+1.5 * str_rect.height()),str_paint); canvas.drawText(str[1],(float)(center + Math.sin(Math.toRadians(360/7)) * one_radius + str_rect.height()/2), (float)((getPaddingTop() + 2 * str_rect.height() + one_radius - Math.abs(Math.cos(Math.toRadians(360 / 7)) * one_radius))), str_paint); canvas.drawText(str[2], (float) (center + Math.sin(Math.toRadians(360 / 7 + 360 / 7 / 2)) * one_radius + str_rect.height() / 2), (float) (Math.cos(Math.toRadians(360 / 7 + 360 / 7 / 2)) * one_radius) + center + str_rect.height() / 2, str_paint); canvas.drawText(str[3], (float) (center + Math.sin(Math.toRadians(360 / 7 / 2)) * one_radius - str_rect.height() / 2 + str_rect.width() / 2), (float) ((Math.cos(Math.toRadians(360 / 7 / 2)) * one_radius) + center + str_rect.height()), str_paint); canvas.drawText(str[4], (float) (center - Math.sin(Math.toRadians(360 / 7 / 2)) * one_radius + str_rect.height() / 2 - str_rect.width() * 1.5), (float) ((Math.cos(Math.toRadians(360 / 7 / 2)) * one_radius) + center + str_rect.height()), str_paint); canvas.drawText(str[5], (float) (center - Math.sin(Math.toRadians(360 / 7 + 360 / 7 / 2)) * one_radius - str_rect.height() / 2 - str_rect.width()), (float) (Math.cos(Math.toRadians(360 / 7 + 360 / 7 / 2)) * one_radius) + center + str_rect.height() / 2, str_paint); canvas.drawText(str[6], (float) (center - Math.sin(Math.toRadians(360 / 7)) * one_radius - str_rect.height() / 2 - str_rect.width()), (float) ((getPaddingTop() + 2 * str_rect.height() + one_radius - Math.abs(Math.cos(Math.toRadians(360 / 7)) * one_radius))), str_paint); } //绘制最外层多边形 private void onePolygons(Canvas canvas){ Path path=new Path(); path.moveTo(center, getPaddingTop() + 2 * str_rect.height()); //起始点 path.lineTo((float) (center + Math.sin(Math.toRadians(360 / 7)) * one_radius), (float) (getPaddingTop() + 2 * str_rect.height() + one_radius - Math.abs(Math.cos(Math.toRadians(360 / 7)) * one_radius))); path.lineTo((float) (center + Math.sin(Math.toRadians(360 / 7 + 360 / 7 / 2)) * one_radius), (float) (Math.cos(Math.toRadians(360 / 7 + 360 / 7 / 2)) * one_radius) + center); path.lineTo((float) (center + Math.sin(Math.toRadians(360 / 7 / 2)) * one_radius), (float) (Math.cos(Math.toRadians(360 / 7 / 2)) * one_radius) + center); path.lineTo((float) (center - Math.sin(Math.toRadians(360 / 7 / 2)) * one_radius), (float) (Math.cos(Math.toRadians(360 / 7 / 2)) * one_radius) + center); path.lineTo((float) (center - Math.sin(Math.toRadians(360 / 7 + 360 / 7 / 2)) * one_radius), (float) (Math.cos(Math.toRadians(360 / 7 + 360 / 7 / 2)) * one_radius) + center); path.lineTo((float) (center - Math.sin(Math.toRadians(360 / 7)) * one_radius), (float) (getPaddingTop() + 2 * str_rect.height() + one_radius - Math.abs(Math.cos(Math.toRadians(360 / 7)) * one_radius))); path.close(); canvas.drawPath(path,one_paint); } //利用循环绘制第2,3,4层多边形 private void twoPolygons(Canvas canvas){ for(int i=1;i<4;i++){ switch (i){ case 1: paint=two_paint; break; case 2: paint=three_paint; break; case 3: paint=four_paint; break; } distance = i*one_radius/4; Path path = new Path(); path.moveTo(center, getPaddingTop() + 2 * str_rect.height() + distance); //起始点 path.lineTo((float) (center + Math.sin(Math.toRadians(360 / 7)) * (one_radius - distance)), (float) (getPaddingTop() + 2 * str_rect.height() + (one_radius) - Math.abs(Math.cos(Math.toRadians(360 / 7)) * (one_radius - distance)))); path.lineTo((float) (center + Math.sin(Math.toRadians(360 / 7 + 360 / 7 / 2)) * (one_radius - distance)), (float) (Math.cos(Math.toRadians(360 / 7 + 360 / 7 / 2)) * (one_radius - distance)) + center); path.lineTo((float) (center + Math.sin(Math.toRadians(360 / 7 / 2)) * (one_radius - distance)), (float) (Math.cos(Math.toRadians(360 / 7 / 2)) * (one_radius - distance)) + center); path.lineTo((float) (center - Math.sin(Math.toRadians(360 / 7 / 2)) * (one_radius - distance)), (float) (Math.cos(Math.toRadians(360 / 7 / 2)) * (one_radius - distance)) + center); path.lineTo((float) (center - Math.sin(Math.toRadians(360 / 7 + 360 / 7 / 2)) * (one_radius - distance)), (float) (Math.cos(Math.toRadians(360 / 7 + 360 / 7 / 2)) * (one_radius - distance)) + center); path.lineTo((float) (center - Math.sin(Math.toRadians(360 / 7)) * (one_radius - distance)), (float) (getPaddingTop() + 2 * str_rect.height() + (one_radius) - Math.abs(Math.cos(Math.toRadians(360 / 7)) * (one_radius - distance)))); path.close(); canvas.drawPath(path,paint); } } //绘制等级进度 private void paintRank(Canvas canvas) { Path path = new Path(); path.moveTo(center, getPaddingTop() + 2 * str_rect.height() + f1); //起始点 path.lineTo((float) (center + Math.sin(Math.toRadians(360 / 7)) * (one_radius - f2)), (float) (getPaddingTop() + 2 * str_rect.height() + (one_radius) - Math.abs(Math.cos(Math.toRadians(360 / 7)) * (one_radius - f2)))); path.lineTo((float) (center + Math.sin(Math.toRadians(360 / 7 + 360 / 7 / 2)) * (one_radius - f3)), (float) (Math.cos(Math.toRadians(360 / 7 + 360 / 7 / 2)) * (one_radius - f3)) + center); path.lineTo((float) (center + Math.sin(Math.toRadians(360 / 7 / 2)) * (one_radius - f4)), (float) (Math.cos(Math.toRadians(360 / 7 / 2)) * (one_radius - f4)) + center); path.lineTo((float) (center - Math.sin(Math.toRadians(360 / 7 / 2)) * (one_radius - f5)), (float) (Math.cos(Math.toRadians(360 / 7 / 2)) * (one_radius - f5)) + center); path.lineTo((float) (center - Math.sin(Math.toRadians(360 / 7 + 360 / 7 / 2)) * (one_radius - f6)), (float) (Math.cos(Math.toRadians(360 / 7 + 360 / 7 / 2)) * (one_radius - f6)) + center); path.lineTo((float) (center - Math.sin(Math.toRadians(360 / 7)) * (one_radius - f7)), (float) (getPaddingTop() + 2 * str_rect.height() + (one_radius) - Math.abs(Math.cos(Math.toRadians(360 / 7)) * (one_radius - f7)))); path.close(); canvas.drawPath(path, rank_Paint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int wMode=MeasureSpec.getMode(widthMeasureSpec); int wSize=MeasureSpec.getSize(widthMeasureSpec); int hMode=MeasureSpec.getMode(heightMeasureSpec); int hSize=MeasureSpec.getSize(heightMeasureSpec); int width,height; if(wMode==MeasureSpec.EXACTLY){ width=wSize; }else { width=Math.min(wSize,defaultSize); } if(hMode==MeasureSpec.EXACTLY){ height=hSize; }else { height=Math.min(hSize,defaultSize); } center = width/2; //中心点 one_radius = center-getPaddingTop()-2*str_rect.height(); f1 = one_radius-one_radius / 4 * 1; f2 = one_radius-one_radius / 4 * 1; f3 = one_radius-one_radius / 4 * 1; f4 = one_radius-one_radius / 4 * 1; f5 = one_radius-one_radius / 4 * 1; f6 = one_radius-one_radius / 4 * 1; f7 = one_radius-one_radius / 4 * 1; setMeasuredDimension(width,height); } //dp转px public int dp_px(int values){ float density=getResources().getDisplayMetrics().density; return (int) (values * density + 0.5f); } }3.activity_main.xml
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:orientation="vertical" tools:context="com.example.administrator.polygonsview.MainActivity"> <com.example.administrator.polygonsview.Polygons android:id="@+id/pv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:padding="5dp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_margin="15dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="击杀" /> <SeekBar android:id="@+id/sb1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:max="40" android:progress="10" /> LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="生存" /> <SeekBar android:id="@+id/sb2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:max="40" android:progress="10" /> LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="助攻" /> <SeekBar android:id="@+id/sb3" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:max="40" android:progress="10" /> LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="物理" /> <SeekBar android:id="@+id/sb4" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:max="40" android:progress="10" /> LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="魔法" /> <SeekBar android:id="@+id/sb5" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:max="40" android:progress="10" /> LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="防御" /> <SeekBar android:id="@+id/sb6" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:max="40" android:progress="10" /> LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="金钱" /> <SeekBar android:id="@+id/sb7" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:max="40" android:progress="10" /> LinearLayout> LinearLayout> /> LinearLayout>
4.colors.xml
<color name="one">#c3e3e5color> <color name="two">#85cdd4color> <color name="three">#48afb6color> <color name="four">#22737bcolor>
5.什么时候调用onMeasure方法?
在Android开发中,当Android原生控件不能满足我们的需求的时候,就需要自定义View。View在屏幕上绘制出来先要经过measure(计算)和layout(布局)。
当子View的父控件要放置该View的时候,父控件会传递两个参数给View——widthMeasureSpec和heightMeasureSpec。这两个参数是View可以获取的宽高尺寸和模式值混合的int数据。可以通过int mode = MeasureSpec.getMode(widthMeasureSpec)得到模式,用int size = MeasureSpec.getSize(widthMeasureSpec)得到尺寸。
mode共有三种情况,取值分别为
MeasureSpec.UNSPECIFIED,MeasureSpec.EXACTLY,MeasureSpec.AT_MOST。
参考自:http://blog.csdn.net/as7210636/article/details/52692102