自定义控件之五维图

2016-07-09  本文已影响426人  shada

先看效果

Paste_Image.png

做起来很简单。就是用三角函数,算出五边形五个定点的坐标,然后根据左边连线就行了。根据看图。

images.png

具体代码如下:

  class FiveDimensionView extends View {
    private Paint pentagonPaint;
    private Paint coverPaint;
    private TextPaint textPaint;
    private Path path;
    private Entity entity;
    private float distance;
    private Path coverPath;
    public FiveDimensionView(Context context) {
        this(context,null);
    }
    public FiveDimensionView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public FiveDimensionView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.FiveDimensionView);
        //五边形边的宽度
        float pentagonWidth=array.getDimension(R.styleable.FiveDimensionView_pentagon_width,dp2px(1));
        //五边形边的颜色
        int pentagonColor=array.getColor(R.styleable.FiveDimensionView_pentagon_color, Color.RED);
        //覆盖区域的颜色
        int coverColor=array.getColor(R.styleable.FiveDimensionView_cover_color,Color.BLUE); 
       //字体颜色
        int textColor=array.getColor(R.styleable.FiveDimensionView_text_color,Color.BLACK);
        //字体大小
        float textSize=array.getDimension(R.styleable.FiveDimensionView_text_size,13);
        //字体距离五边形顶点的长度
        distance = array.getDimension(R.styleable.FiveDimensionView_text_distance,dp2px(5));
        array.recycle();
        //正五边形
        pentagonPaint = new Paint();
        pentagonPaint.setAntiAlias(true);
        pentagonPaint.setColor(pentagonColor);
        pentagonPaint.setStyle(Paint.Style.STROKE);
        pentagonPaint.setStrokeWidth(pentagonWidth);
        //覆盖区域
        coverPaint = new Paint();
        coverPaint.setAntiAlias(true);
        coverPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        coverPaint.setColor(coverColor);
        //文字
        textPaint = new TextPaint();
        textPaint.setColor(textColor);
        textPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,textSize,context.getResources().getDisplayMetrics()));
        textPaint.setAntiAlias(true);
        path = new Path();
        coverPath = new Path();
    } 
   @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize=MeasureSpec.getSize(widthMeasureSpec);
        int widthMode=MeasureSpec.getMode(widthMeasureSpec);
        int heightSize=MeasureSpec.getSize(heightMeasureSpec);
        int heightMode=MeasureSpec.getMode(heightMeasureSpec);
        int width=0,height=0;
        if(widthMode==MeasureSpec.AT_MOST){
            width=dp2px(250);
        }else{
            width=widthSize;
        }
        if(heightMode==MeasureSpec.AT_MOST){
            height=dp2px(250);
        }else{
            height=heightSize;
        } 
       setMeasuredDimension(width,height);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //半径
        float radius=Math.min(getWidth(),getHeight())/2*0.8f;
        canvas.save();
        //原点移至当前view的中心
        canvas.translate(getWidth()/2,getHeight()/2);
        List<PointF> points = getPoints(radius);
        //绘制五边形边
        for(int i = 0; i< points.size(); i++){
            PointF pointF = points.get(i);
            if(i==0){
                path.moveTo(pointF.x,pointF.y);
            }else{
                path.lineTo(pointF.x,pointF.y);
            }
        }
        path.close();
        canvas.drawPath(path,pentagonPaint);
        //绘制圆心和顶点的连线
        for(PointF pointF:points){
            path.moveTo(0,0);
            path.lineTo(pointF.x,pointF.y);
            canvas.drawPath(path,pentagonPaint);
            path.reset();
        }
        if(entity==null){
            entity=new Entity();
            entity.setTotal(1);
            List<DisEntity>list=new ArrayList<>();
            list.add(new DisEntity("第一",0));
            list.add(new DisEntity("第二",0)); 
            list.add(new DisEntity("第三",0));
            list.add(new DisEntity("第四",0));
            list.add(new DisEntity("第五",0));
            entity.setList(list);
        }
        List<DisEntity> list = entity.getList();
        //绘制覆盖区域
        for(int i = 0; i< points.size(); i++){
            PointF pointF = points.get(i);
            float rate=list.get(i).getScore()*1.0f/entity.getTotal();
            if(i==0){
                coverPath.moveTo(pointF.x*rate,pointF.y*rate);
            }else{
                coverPath.lineTo(pointF.x*rate,pointF.y*rate);
            }
        }
        coverPath.close();
        canvas.drawPath(coverPath,coverPaint);
        //绘制文字
        for (int i=0;i<points.size();i++){
            PointF pointF = points.get(i);
            String title = list.get(i).getTitle();
            Rect bounds = new Rect();
            //获取字体宽高
            textPaint.getTextBounds(title,0,title.length(),bounds);
            float textWidth = bounds.width();
            int textHeight= bounds.height();
            switch (i){
                case 0:
                    canvas.drawText(title,pointF.x-textWidth/2,pointF.y-distance,textPaint);
                    break;
                case 1:
                    canvas.drawText(title,pointF.x+distance,pointF.y+textHeight/2,textPaint);
                    break;
                case 2:
                    canvas.drawText(title,pointF.x+distance-textWidth/2,pointF.y+distance+textHeight,textPaint);                    break;
                case 3:
                    canvas.drawText(title,pointF.x-distance-textWidth/2,pointF.y+textHeight+distance,textPaint);
                    break;
                case 4:
                    canvas.drawText(title,pointF.x-distance-textWidth,pointF.y+textHeight/2,textPaint);
                    break;
            }
        }
        canvas.restore();
    }
    //获取正五边形五个顶点
    private List<PointF> getPoints(float radius){
        List<PointF>points=new ArrayList<>();
        points.add(new PointF(0,-radius));//第0个点
        //第1个点
        points.add(new PointF((float)(radius*Math.cos(Math.toRadians(18))),-(float)(radius*Math.sin(Math.toRadians(18)))));
        //第2个点
        points.add(new PointF((float)(radius*Math.sin(Math.toRadians(36))),(float)(radius*Math.cos(Math.toRadians(36)))));
        //第3个点
        points.add(new PointF((float)(-radius*Math.sin(Math.toRadians(36))),(float)(radius*Math.cos(Math.toRadians(36)))));
        //第4个点
        points.add(new PointF((float)(-radius*Math.cos(Math.toRadians(18))),-(float)(radius*Math.sin(Math.toRadians(18)))));
        return points;
    }
    public Entity getEntity() {
        return entity;
    }
    public void setEntity(Entity entity) {
        this.entity = entity;
        invalidate();
    }
    private int dp2px(int dp){
        return (int) (getContext().getResources().getDisplayMetrics().density*dp+0.5);
    }
}

代码中用到的实体类

public class Entity {
    //集合中是顶点上文字和文字所对应的分数
    private List<DisEntity> list;
    //total是总分,根据分数和总分的比值,得到覆盖区域的顶点
    private int total;
    public List<DisEntity> getList() {
        return list;
    }
    public void setList(List<DisEntity> list) {
        this.list = list;
    }
    public int getTotal() {
        return total;
    } 
   public void setTotal(int total) {
        this.total = total;
    }
}
public class DisEntity {
    private String title;
    private int score;
    public DisEntity(String title, int score) {
        this.title = title;
        this.score = score;
    }
    public String getTitle() {
        return title;
    } 
   public void setTitle(String title) {
        this.title = title;
    }
    public int getScore() {
        return score;
    }
    public void setScore(int score) {
        this.score = score;
    }
}

自定义控件在布局中的使用

<RelativeLayout
    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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="example.com.dimensionview.MainActivity">
   <example.com.dimensionview.FiveDimensionView
       android:id="@+id/five"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
</RelativeLayout>
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        FiveDimensionView five= (FiveDimensionView)findViewById(R.id.five);
        Entity entity=new Entity();
        entity.setTotal(10);
        List<DisEntity>list=new ArrayList<>();
        list.add(new DisEntity("第1",4));
        list.add(new DisEntity("第2",5));
        list.add(new DisEntity("第3",6));
        list.add(new DisEntity("第4",7));
        list.add(new DisEntity("第5",8));
        entity.setList(list);
        five.setEntity(entity);    }}

自定义属性

  <declare-styleable name="FiveDimensionView">
    <attr name="pentagon_width" format="dimension"></attr> 
   <attr name="pentagon_color" format="color"></attr>
    <attr name="cover_color" format="color"></attr>
    <attr name="text_size" format="dimension"></attr>
    <attr name="text_color" format="color"></attr>
    <attr name="text_distance" format="dimension"></attr>
</declare-styleable>
上一篇 下一篇

猜你喜欢

热点阅读