1. 程式人生 > >學習自定義 —— 自定義線型圖控制元件

學習自定義 —— 自定義線型圖控制元件

自定義控制元件篇:

                                                      自定義折線圖

前言:自定義控制元件永遠都是客戶端開發的一個必須攻破的難題

首先得了解需要實現的樣式,確定有沒有可繼承的控制元件類,若沒有就直接繼承View。

View也有自己的生命週期,先了解下我們常用的幾個自定義View方法:

構造方法  -  onMeasure - onLayout - onDraw -  onTouchEvent - onAnimationStart

在構造方法裡進行畫圖工具和資料的初始化

        srceenWidth = Tool.getWidth(context);
        srceenHeight = Tool.getHeight(context);
        paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true); //去鋸齒
        paint.setStrokeWidth(2);
        paint.setTextSize(context.getResources().getDimensionPixelSize(R.dimen.font_super_smallest));
        paint.setColor(context.getResources().getColor(R.color._7C7F99));

        String text = "08-06";
        Rect rect = new Rect();
        paint.getTextBounds(text,0,text.length(), rect);
        textheight = rect.height();
        textWidth = rect.width();

        int srceenHeightpf = srceenHeight/100;
        YPoint = Tool.dpToPx(10);
        YLength = srceenHeightpf*PriceShjListKinView.Ypinf -YPoint;//K線高度縮小給上下留白
        YPoint = YPoint -(YPoint/2);//上下各一半
        YScale = YLength/YScalenum;//Y軸均分

在onMeasure方法裡對View的高度和寬度做螢幕適配

setMeasuredDimension(srceenWidth, Tool.getHeight(context)/3);

在onLayout中可以獲取到View中的子View進行高寬的動態變更

在onDraw中進行繪圖:重點

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    this.setBackgroundColor(getResources().getColor(R.color.trade_tab));
    //新增X軸刻度和文字
    drawYklin(canvas);

    //新增X軸刻度和文字
    drawXklin(canvas);

    /*早盤價折線*/
    painm = new Paint();
    painm.setStyle(Paint.Style.FILL);
    painm.setAntiAlias(true); //去鋸齒
    painm.setStrokeWidth(2);
    painm.setTextSize(context.getResources().getDimensionPixelSize(R.dimen.font_super_smallest));
    painm.setColor(context.getResources().getColor(R.color._DE9724));
    if(coordinatecoyp==null){
        coordinatecoyp = new ArrayList<>();
    }else {
        coordinatecoyp.clear();
    }
    int YMax = YLength - (YScalenum-1) * YScale;
    float KinMax = YLength - (YLength-YScale) * Tool.getSHJcalPrice(upPrice,downPrice,upPrice-downPrice);
    float C = KinMax - YMax;
    if(Mpricelist!=null){
        for(int i=0; i<Mpricelist.size(); i++){
            if(i!=Mpricelist.size()-1){
                float y = YLength - (YLength-YScale+C) * Tool.getSHJcalPrice(Mpricelist.get(i).getYlin(),downPrice,upPrice-downPrice);
                float y1 = YLength - (YLength-YScale+C) * Tool.getSHJcalPrice(Mpricelist.get(i+1).getYlin(),downPrice,upPrice-downPrice);
                canvas.drawLine( i * XScale + textWidth/2 + addXwidth,y- YScale/2- textheight/2,
                        (i+1) * XScale+ textWidth/2 + addXwidth,y1- YScale/2- textheight/2, painm);
            }
            for(SHjKinBean sHjKinBean: coordinate){
                if(sHjKinBean.getXlin().equals(Mpricelist.get(i).getXlin())&&sHjKinBean.getCoordinateX()==COORDINATEM){
                    SHjKinBean sHjKinBean1 = new SHjKinBean();
                    sHjKinBean1.setCoordinateY(YLength - (YLength-XScale) * Tool.getSHJcalPrice(Mpricelist.get(i).getYlin(),downPrice,upPrice-downPrice));
                    sHjKinBean1.setCoordinateX(  i * XScale+ textWidth/2 + addXwidth);
                    sHjKinBean1.setXlin(Mpricelist.get(i).getXlin());
                    coordinatecoyp.add(sHjKinBean1);
                }
            }
        }
    }
    /*午盤價折線*/
    paina = new Paint();
    paina.setStyle(Paint.Style.FILL);
    paina.setAntiAlias(true); //去鋸齒
    paina.setStrokeWidth(2);
    paina.setTextSize(context.getResources().getDimensionPixelSize(R.dimen.font_super_smallest));
    paina.setColor(context.getResources().getColor(R.color._10A8F6));
    if(Apricelist!=null){
        for(int i=0; i<Apricelist.size(); i++){
            if(i!=Apricelist.size()-1){
                float y = YLength - (YLength-YScale+C) * Tool.getSHJcalPrice(Apricelist.get(i).getYlin(),downPrice,upPrice-downPrice);
                float y1 = YLength - (YLength-YScale+C) * Tool.getSHJcalPrice(Apricelist.get(i+1).getYlin(),downPrice,upPrice-downPrice);
                canvas.drawLine( i * XScale + textWidth/2 + addXwidth,y- YScale/2- textheight/2,
                        (i+1) * XScale + textWidth/2 + addXwidth,y1- YScale/2- textheight/2, paina);
            }
            for(SHjKinBean sHjKinBean: coordinate){
                if(sHjKinBean.getXlin().equals(Apricelist.get(i).getXlin())&&sHjKinBean.getCoordinateX()==COORDINATEA){
                    SHjKinBean sHjKinBean1 = new SHjKinBean();
                    sHjKinBean1.setCoordinateY(YLength - (YLength-XScale) * Tool.getSHJcalPrice(Apricelist.get(i).getYlin(),downPrice,upPrice-downPrice));
                    sHjKinBean1.setCoordinateX( i * XScale+ textWidth/2 + addXwidth);
                    sHjKinBean1.setXlin(Apricelist.get(i).getXlin());
                    coordinatecoyp.add(sHjKinBean1);
                }
            }
        }
    }
    //畫點選彈出的詳情
    if (needPop&&coordinatecoyp.size()>0){
        drawPopView(popX,popY,canvas);
    }
}

如果資料變更,我們提供一個供資料返回後頁面呼叫的方法進行View的資料變化

public void PirceSetShjKinData(List<SHJBean> shjlist){
    this.shjlist = shjlist;
    UpteInvaldata();
    invalidate();
}

public void UpteInvaldata(){

    upPrice = 0;
    downPrice = 0;
    boolean isfrist= true;
    Mpricelist = new ArrayList<>();
    Apricelist = new ArrayList<>();
    coordinate = new ArrayList<>();//儲存同一天價格高的xy軸座標做
    String coordinatedata = "";
    for(SHJBean shjBeans:shjlist){
        try {
            float coordinatepriceM = -9999 , coordinatepriceA = -9999 ;
            float coordinateis = COORDINATEM;//預設早盤
                String price = shjBeans.getMprice()!=null?shjBeans.getMprice():shjBeans.getAprice();
                if(isfrist){//最低值需要拿第一個值
                    downPrice = Tool.getSHJDouble2(Double.valueOf(price));
                    isfrist = false;
                }
                coordinatedata = shjBeans.getData();
                Mpricelist.add(new SHjKinBean(Tool.getSHJDouble2(Double.valueOf(shjBeans.getMprice())),shjBeans.getData()));
                Apricelist.add(new SHjKinBean(Tool.getSHJDouble2(Double.valueOf(shjBeans.getAprice())),shjBeans.getData()));
                upPrice = upPrice>Tool.getSHJDouble2(Double.valueOf(price))?
                        upPrice:Tool.getSHJDouble2(Double.valueOf(price));//取出最高值
                downPrice = downPrice < Tool.getSHJDouble2(Double.valueOf(price))?
                        downPrice:Tool.getSHJDouble2(Double.valueOf(price));//取出最低值
                if(shjBeans.getMprice()!=null){
                    coordinatepriceM = Tool.getSHJDouble2(Double.valueOf(price));
                }else {
                    coordinatepriceA = Tool.getSHJDouble2(Double.valueOf(price));
                }

                /*拿出同一天最高價格是午盤還是早盤*/
            coordinateis = coordinatepriceM>coordinatepriceA?COORDINATEM:COORDINATEA;
            SHjKinBean KinBean = new SHjKinBean();
            KinBean.setCoordinateX(coordinateis);
            KinBean.setXlin(coordinatedata);
            coordinate.add(KinBean);
        }catch (Exception e){
            e.printStackTrace();
        }

    }
    XLength  = srceenWidth-Tool.dpToPx(45);
    XScale = (XLength-(int) textWidth/2)/shjlist.size();
    if(shjlist.size()<5){
        addXwidth = XScale/2;
    }else {
        addXwidth = 0;
    }

    YLabel = new String[YScalenum];
    float Ypricepf = Tool.getSHJcalPrice(upPrice,downPrice,YScalenum-1);//均分-1
    for(int i=0;i<YScalenum;i++){
        float Ypriceft = Tool.getSHJDouble2(Ypricepf*i)+downPrice;
        DecimalFormat decimalFormat = new DecimalFormat("0.00");
        YLabel[i] = decimalFormat.format(Ypriceft);
    }
    XLabel = new String[shjlist.size()];
    for(int i=0;i<XLabel.length;i++){
        XLabel[i] = shjlist.get(i).getData();
    }
}

更新繪製需要呼叫invalidate();如果沒有在UI執行緒裡進行,則需要postInvalidate();

定義一個變數通知佈局點選顯示區域

public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            if(MOVENUM==0){
                downX = event.getX();
            }
            MOVENUM++;
            break;
        case MotionEvent.ACTION_MOVE:
            if(needPop){
                isMOVE = true;
                popX = event.getX();
                popY = event.getY();
                invalidate();
            }
            break;
        case MotionEvent.ACTION_UP:
            if(!needPop&&!isMOVE){
                needPop = !needPop;
            }else {
                float X = downX>event.getX()?downX-event.getX():event.getX()-downX;
                if(X<=Tool.dpToPx(15)&&MOVENUM>1){
                    MOVENUM=0;
                    needPop = !needPop;
                    isMOVE = false;
                }
            }
            popX = event.getX();
            popY = event.getY();
            invalidate();
            downX = event.getX();
            break;
    }
    return true;
}

同樣,點選後需要通知View呼叫invalidate進行佈局更新

點選後的手指區域處理程式碼:

 float coorx;//最小的點選差比
    int popWith = 0;
    private void drawPopView(float x,float y,Canvas canvas){
        int position=0;//最小的x軸
        popWith = Tool.dpToPx(15);
        for(int i=0;i<coordinatecoyp.size();i++){
            if(i==0){
                coorx = coordinatecoyp.get(i).getCoordinateX()>x?coordinatecoyp.get(i).getCoordinateX()-x:x-coordinatecoyp.get(i).getCoordinateX();
            }
            float coorxcopy = coordinatecoyp.get(i).getCoordinateX()>x?coordinatecoyp.get(i).getCoordinateX()-x:x-coordinatecoyp.get(i).getCoordinateX();
            if(coorx>coorxcopy){
                position = i;
                coorx = coorxcopy;
            }
        }
        x = coordinatecoyp.get(position).getCoordinateX();
        Paint paintzb = new Paint();//豎線
        paintzb.setStyle(Paint.Style.FILL);
        paintzb.setAntiAlias(true); //去鋸齒
        paintzb.setStrokeWidth(1);
        paintzb.setTextSize(context.getResources().getDimensionPixelSize(R.dimen.font_super_smallest));
        paintzb.setColor(context.getResources().getColor(R.color.text_usually));
        float newx;
        if(x>XLength){
            newx = XLength;
        }else {
            newx = x;
        }
        canvas.drawLine(newx, YLength - YScale/2- textheight/2, newx, YLength - Ypoplin * YScale- YScale/2- textheight/2, paintzb);
//        y = coordinatecoyp.get(position).getCoordinateY();
        Paint painapop = new Paint();
        painapop.setStyle(Paint.Style.STROKE);
        painapop.setAntiAlias(true); //去鋸齒
        painapop.setStrokeWidth(2);
        painapop.setColor(context.getResources().getColor(R.color.text_usually));
        Path path= new Path();
        if(y-popWith*4 <10+ YPoint){
        /*超出上邊框*/
            y  = (10+YPoint) - (y-popWith*4) +y;
        }else if(y>YLength-YScale/2- textheight/2){
        /*超出下邊框*/
            float yend = y - YLength + textheight/2 + YScale/2;
            y  = y - yend;
        }
        String text = "交易日 :0101交易時間";
        float width = paint.measureText(text);//文字的寬度
        if(x+width>XLength){
        /*超出右邊框*/
            path.moveTo(x-popWith,y-popWith*4);
            path.lineTo(x-popWith,y);
            path.lineTo(x-width,y);
            path.lineTo(x-width,y-popWith*4);
            path.lineTo(x-popWith,y-popWith*4);
        }else {
            path.moveTo(x+popWith,y-popWith*4);
            path.lineTo(x+popWith,y);
            path.lineTo(x+width,y);
            path.lineTo(x+width,y-popWith*4);
            path.lineTo(x+popWith,y-popWith*4);
        }
        canvas.drawPath(path, painapop);

        painapop.setStyle(Paint.Style.FILL);
        painapop.setColor(context.getResources().getColor(R.color.trade_tab));
        canvas.drawPath(path, painapop);
        String data="- -",pricem="- -",pricea="- -";
        for(int i=0;i<Mpricelist.size();i++){
            if(Mpricelist.get(i).getXlin().equals(coordinatecoyp.get(position).getXlin())){
                data = Mpricelist.get(i).getXlin();
                pricem = ""+Mpricelist.get(i).getYlin();
            }
        }
        for(int i=0;i<Apricelist.size();i++){
            if(Apricelist.get(i).getXlin().equals(coordinatecoyp.get(position).getXlin())){
                data = Apricelist.get(i).getXlin();
                pricea = ""+Apricelist.get(i).getYlin();
            }
        }
        int popHeight = (popWith*4)/8;
        if(x+width>XLength){
        /*超出右邊框*/
            canvas.drawText("交易日:"+TimeDateUtil.changeDateFormat("yyyyMMdd", "MM-dd",data),x-width+Tool.dpToPx(10),y-popWith*4+popHeight*2+Tool.dpToPx(5),paint);
            canvas.drawText("早盤價:"+ Utils.pricesFormatFloat(pricem),x-width+Tool.dpToPx(10),y-popWith*4+popHeight*4+Tool.dpToPx(5),painm);
            canvas.drawText("午盤價:"+Utils.pricesFormatFloat(pricea),x-width+Tool.dpToPx(10),y-popWith*4+popHeight*6+Tool.dpToPx(5),paina);
        }else {
            canvas.drawText("交易日:"+TimeDateUtil.changeDateFormat("yyyyMMdd", "MM-dd",data),x+popWith+Tool.dpToPx(10),y-popWith*4+popHeight*2+Tool.dpToPx(5),paint);
            canvas.drawText("早盤價:"+Utils.pricesFormatFloat(pricem),x+popWith+Tool.dpToPx(10),y-popWith*4+popHeight*4+Tool.dpToPx(5),painm);
            canvas.drawText("午盤價:"+Utils.pricesFormatFloat(pricea),x+popWith+Tool.dpToPx(10),y-popWith*4+popHeight*6+Tool.dpToPx(5),paina);
        }
    }

整篇程式碼在下面貼出來