自定义View之产业结构图

2019-01-09  本文已影响7人  小于先森

需求与结果

技术人信仰:没图说个卵子。

UI设计图:

UI图
在这里插入图片描述

实现图:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路篇

毋庸置疑,肯定要用自定义ViewGroup+View,会有童鞋问:“为啥不直接用自定义View”,不想回答,自行思考。
首先 ,我们要先想下配置,看到图的瞬间我们就应该有个大概的思路。大致必须要拥有以下几个类:

实践篇1(ChartConstant)

首先我们要确初始化的时候需要的属性:

实践篇2(PathView)

自定义View主要步骤三部曲:
构造方法;
onMeasure();
onDraw();

这个PathView 一眼看去,可知,需要这么几个属性:

确定了这些然后在构造方法中初始化:

        setClickable(true);
        MIN_HEIGHT = ChartConstant.ChartAxis_MinHeight;
        CHART_VIEW_WIDTH = ChartConstant.ChartContentWidth;
        Z_LENGTH = MIN_HEIGHT * 10;
        y1_length = y2_length = y3_length = y4_length = Z_LENGTH;
        Y_LENGTH = (int) Math.sqrt(Math.pow(Z_LENGTH, 2) * 2);
        X_LENGTH = CHART_VIEW_WIDTH * 3;
        AXIS_TEXT_HEIGHT = MIN_HEIGHT * 2;

        //初始化画笔
        mLinePaint = new Paint();
        mLinePaint.setStyle(Paint.Style.STROKE);
        mLinePaint.setAntiAlias(true);
        mLinePaint.setColor(AXIS_COLOR);
        mLinePaint.setStrokeWidth(1.5f);
        mBgPaint = new Paint();
        mBgPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mBgPaint.setAntiAlias(true);
        mBgPaint.setColor(Color.parseColor("#f5caca"));
        mBgPaint.setAlpha(122);
        axis_textPaint = new TextPaint();
        axis_textPaint.setAntiAlias(true);
        axis_textPaint.setColor(AXIS_NAME_COLOR);
        axis_textPaint.setFakeBoldText(true);
        axis_textPaint.setTextSize(DisplayUtils.sp2px(context, 15));
        axis_textpaint_y = new TextPaint();
        axis_textpaint_y.setAntiAlias(true);
        axis_textpaint_y.setColor(Color.parseColor("#4b4b4b"));
        axis_textpaint_y.setTextSize(DisplayUtils.sp2px(context, 11));


        //横向数组
        y0List = new ArrayList<>();
        y1List = new ArrayList<>();
        y2List = new ArrayList<>();
        y3List = new ArrayList<>();
        y4List = new ArrayList<>();

        points = new ArrayList<>();

接下来看Measure方法

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int height = Z_LENGTH + AXIS_TEXT_HEIGHT + MIN_HEIGHT;//总高度
        int width = X_LENGTH;
        width = initViewWidth(width);
        setMeasuredDimension(width, height);


    }

    private int initViewWidth(int width) {
        for (int i = 0; i < 5; i++) {
            if (width < getRealWidth(i)) {
                width = getRealWidth(i);
            }
        }
        return width;
    }

    private int getRealWidth(int i) {
        int width = getMeasuredWidth();
        switch (i) {
            case 0:
                width = X_LENGTH + ChartConstant.ChartContentWidth * 3;
                break;
            case 1:
                width = y1_length + ChartConstant.ChartAxis_MinHeight * 3 + ChartConstant.ChartContentWidth * 2;
                break;
            case 2:
                width = y2_length + ChartConstant.ChartAxis_MinHeight * 5 + ChartConstant.ChartContentWidth * 2;
                break;
            case 3:
                width = y3_length + ChartConstant.ChartAxis_MinHeight * 7 + ChartConstant.ChartContentWidth * 2;
                break;
            case 4:
                width = y4_length + ChartConstant.ChartAxis_MinHeight * 9 + ChartConstant.ChartContentWidth * 2;
                break;
        }
        return width;
    }

高度是确定的 就是最小高度*13然后上边加个轴名高度 下边加个最低高度。宽度就是获取5条线的宽度 比一比谁更长就选谁。

下边是onDraw()方法,讲下思路,具体代码去github上看源码吧。

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawpolygon(canvas);
        drawAxis(canvas);
        drawAxisText(canvas);
    }

看图确定哪个在最底部。
首先是几何图,然后花轴承,然后画字。Over!

实践篇3 (ChartContentView&Y_ChartGroup)

ChartContentView

一个自定义EditText 主要就是有两个属性 一个isChecked(判断是否选中,选中就要设置这个为多边形的一个点) 一个Point(就是中心点的意思)
然后就是onMeasure()设置宽高为Constant里的宽高

Y_ChartGroup

主要为了管理方便,因为这个轴上的控件都是变化控件。Z轴均为不变控件。

实践篇4(ChartGroup)

这个是自定义ViewGroup
自定义ViewGroup与自定义View的区别就是多了一个onLayout()排版
其他的都差不多。onMeasure()也有一丢丢的变化。
按步骤来

第一步 初始化。

addView:

  1. PathView
  2. Z轴上的ChartContentView
  3. Y_ChartGroup
private void init(Context context){
        zlist = new ArrayList<>();
        ygrouplist = new ArrayList<>();
        screen_width = DisplayUtils.getScreenWidth(context);
        screen_height = DisplayUtils.getScreenHeight(context)-DisplayUtils.dip2px(context,65);
        left_distance = DisplayUtils.dip2px(context,20);
        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        mScroller = new Scroller(context);
        AXIS_START = DisplayUtils.dip2px(context,80);//
        z_distance=DisplayUtils.dip2px(context,33);
        PathView pathView = new PathView(context);
        ChartConstant.Global_PathView = pathView;
        addView(pathView);
        for (int i=0;i<str_z.length;i++){//Z轴上的组件
            ChartContentView view = new ChartContentView(context);
            view.setOnLongClickListener(onLongClickListener);
            view.unlockMaxLength();
            view.setText(str_z[i]);
            view.setTag(R.id.chart_content_id,i);
            view.setTag(R.id.chart_content_type,ChartConstant.TYPE_Z);
            view.setFocusable(false);
            addView(view);
            ChartUtils.SetZPoint(view);
            zlist.add(view);
        }
        pathView.setZList(zlist);
        for (int i=4;i>=0;i--){//y43210
            Y_ChartGroup y = new Y_ChartGroup(context);
            y.setTag(i);
            y.setY_Type(getY_type(i));
            addView(y);
            ygrouplist.add(y);
        }
    }
第二步 Measure

看图得知 我们的宽高基本是以PathView的宽高为基准。宽度就是多了最左边的一个控件的宽度,but 我在确定PathView的宽度的时候多给了一个控件的宽度 。so直接setpathView的宽度跟高度就可以。

第三步 Layout

就是确定位置 然后调用子View的 onlayout方法 确定子View的展示的位置,一开始错误的认知了后四个参数,后四个参数是父布局对于他的父布局的边距距离,不应直接用于子布局中,记住这个就可以了。

第四不 onTouch事件

因为分辨率的问题 必须要写成可拖动的 就是要用到Scroller 具体不会用的可自行百度,Google相关用法,不多说,让你们看下实现代码吧。毕竟关于自定义View 事件分发的文章太多了,推荐爱神的自定义系列文章。

 private float mXDown,mYDown,mXLastMove,mYLastMove;
    private float mXMove,mYMove;
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if(mScroller!=null&&!mScroller.isFinished()){
                    mScroller.abortAnimation();
                }
                mXDown = ev.getRawX();
                mYDown = ev.getRawY();
                mXLastMove = mXDown;
                mYLastMove = mYDown;
                break;
            case MotionEvent.ACTION_MOVE:
                mXMove = ev.getRawX();
                mYMove = ev.getRawY();
                mXLastMove = mXMove;
                mYLastMove = mYMove;
                float diffX = Math.abs(mXMove - mXDown);
                float diffY = Math.abs(mYMove - mYDown);
                // 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
                if (diffX > mTouchSlop||diffY>mTouchSlop) {
                    return true;
                }
                break;

        }
        return super.onInterceptTouchEvent(ev);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:

                break;
            case MotionEvent.ACTION_MOVE:
                mXMove = event.getRawX();
                mYMove = event.getRawY();
                int scrollY= (int) (mYLastMove-mYMove);
                int scrollX = (int) (mXLastMove-mXMove);
                Log.e("Test","scrollX:"+getScrollX()+"====scrollY:"+getScrollY());
                scrollBy(scrollX,scrollY);
                mXLastMove =mXMove;
                mYLastMove = mYMove;
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                boolean needScroll= false;
                int distance_y=0;
                int distance_x=0;
                if(getScrollY()<0||getMeasuredHeight()<=screen_height){
                    distance_y = -getScrollY();
                    needScroll = true;
                }else if(getScrollY()+screen_height>getMeasuredHeight()){
                    distance_y = -(getScrollY()+screen_height - getMeasuredHeight());
                }

                if(getScrollX()<0||getMeasuredWidth()<=screen_width){
                    distance_x = -getScrollX();
                    needScroll = true;
                }else if(getScrollX()+screen_width>getMeasuredWidth()){
                    distance_x = -(getScrollX()+screen_width-getMeasuredWidth());
                    needScroll = true;
                }
                if(needScroll){
                    mScroller.startScroll(getScrollX(),getScrollY(),distance_x,distance_y,500);
                    invalidate();
                }

                break;

        }
        return super.onTouchEvent(event);
    }

    @Override
    public void computeScroll() {
        if(mScroller.computeScrollOffset()){
            scrollTo(mScroller.getCurrX(),mScroller.getCurrY());

            postInvalidate();
        }
        super.computeScroll();

    }

End

当然,许多地方还得优化,不过我感觉这个需求没多久就要被砍。且应该大部分公司都用不到这个东东,有具体需要自行更改吧,这只是一个思路。
源码已传至github
github地址:https://github.com/yudehai0204/AxisChartDemo

上一篇 下一篇

猜你喜欢

热点阅读