Android自定义View实现表格

最终效果展示

在这里插入图片描述

代码实现
package com.example.formdemo.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

import java.util.List;

public class FormView extends View {

    public FormView(Context context) {
        this(context,null);
    }

    public FormView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public FormView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private float widthSize;
    private float heightSize;

    private Paint linePaint;
    private Paint linePaint2;
    private Paint itemsTextPaint;
    private Paint dataTextPaint;

    private List<String> transverseItems;//transverse 横轴项
    private String transverseName;
    private String transverseUnit;

    private List<String> longitudinalItems;//longitudinal 纵轴项
    private String longitudinalName;
    private String longitudinalUnit;

    private List<List<Integer>> dataList;//数据

    private int ALERT_VALUE =100;//警报值超过这个值的数据显示红色

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

        //减-12 是为了避开View边缘绘制
        widthSize=MeasureSpec.getSize(widthMeasureSpec)-12;
        heightSize=MeasureSpec.getSize(heightMeasureSpec)-12;

        linePaint=new Paint();
        linePaint.setColor(Color.BLACK);
        linePaint.setStrokeWidth(2);

        linePaint2=new Paint();
        linePaint2.setColor(Color.BLACK);
        linePaint2.setStyle(Paint.Style.STROKE);//设置样式为不填充
        linePaint2.setStrokeWidth(2);
        linePaint2.setAntiAlias(true);//抗锯齿功能

        itemsTextPaint =new Paint();
        itemsTextPaint.setColor(Color.BLACK);
        itemsTextPaint.setStrokeWidth(2);
        itemsTextPaint.setTextSize(34);
        itemsTextPaint.setTextAlign(Paint.Align.CENTER);//设置文字居中显示

        dataTextPaint =new Paint();
        dataTextPaint.setColor(Color.RED);
        dataTextPaint.setStrokeWidth(2);
        dataTextPaint.setTextSize(30);
        dataTextPaint.setTextAlign(Paint.Align.CENTER);//设置文字居中显示

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (transverseItems==null || transverseItems.size()==0)return;
        if (longitudinalItems==null || longitudinalItems.size()==0)return;

        //绘制竖格线
        float transverseStep=widthSize/(transverseItems.size()+1);
        for (int k=0;k<=transverseItems.size()+1;k++){
            //加6 是为了避开View边缘绘制
            float startX=k*transverseStep+6;
            float startY=0+6;
            float endX=k*transverseStep+6;
            float endY=heightSize+6;
            canvas.drawLine(startX,startY,endX,endY,linePaint);
        }

        //绘制横格线
        float longitudinalStep=heightSize/(longitudinalItems.size()+1);
        for (int k=0;k<=longitudinalItems.size()+1;k++){
            //加6 是为了避开View边缘绘制
            float startX=0+6;
            float startY=k*longitudinalStep+6;
            float endX=widthSize+6;
            float endY=k*longitudinalStep+6;
            canvas.drawLine(startX,startY,endX,endY,linePaint);
        }

        //绘制外框
        canvas.drawLine(0+1,0+1,widthSize+11,0+1,linePaint);//上边缘
        canvas.drawLine(widthSize+11,0+1,widthSize+11,heightSize+11,linePaint);//右边缘
        canvas.drawLine(widthSize+11,heightSize+11,0+1,heightSize+11,linePaint);//下边缘
        canvas.drawLine(0+1,heightSize+11,0+1,0+1,linePaint);//左边缘

        //绘制第一个格子的斜切线(分两段绘制)
        float X1=0*transverseStep+6;
        float Y1=0+6;
        float X2=transverseStep/2f;
        float Y2=longitudinalStep/2f;
        float X3=transverseStep+6;
        float Y3=longitudinalStep+6;
        //canvas.drawLine(X1,Y1,X3,Y3,linePaint2);//直线斜切
        Path path1=new Path();
        path1.moveTo(X1,Y1);//起点
        path1.quadTo(X2,Y1-4 //控制点
                ,X2,Y2);//终点
        canvas.drawPath(path1,linePaint2);
        Path path2=new Path();
        path2.moveTo(X2,Y2);//起点
        path2.quadTo(X2,Y3+4 //控制点
                ,X3,Y3);//终点
        canvas.drawPath(path2,linePaint2);

        //绘制轴名称
        canvas.drawText(transverseName,(X3+X2)/2f,(Y3+Y1)/2f+(itemsTextPaint.getTextSize()/5f), itemsTextPaint);
        canvas.drawText(longitudinalName,(X2+X1)/2f,(Y3+Y1)/2f+(itemsTextPaint.getTextSize()/5f), itemsTextPaint);

        //绘制横轴项
        for (int k=0;k<transverseItems.size();k++){
            float textX=(transverseStep+6)+(transverseStep/2f)+(k*transverseStep);
            float textY=(longitudinalStep/2f)+6+(itemsTextPaint.getTextSize()/5f);
            canvas.drawText(transverseItems.get(k),textX,textY, itemsTextPaint);
        }

        //绘制Y轴项
        for (int k=0;k<longitudinalItems.size();k++){
            float textX=(transverseStep/2f)+6;
            float textY=(longitudinalStep+6)+(longitudinalStep/2f)+(k*longitudinalStep)+(itemsTextPaint.getTextSize()/5f);
            canvas.drawText(longitudinalItems.get(k),textX,textY, itemsTextPaint);
        }

        //数据校验,异常判断
        if (dataList.size() != transverseItems.size())return;//要求:横项数=数据长度 (数据与功能项不能对应停止绘制)
        for (int k=0;k<dataList.size();k++){
            if (dataList.get(k).size()!=longitudinalItems.size())return;//要求:纵项数=子数据长度 (数据与功能项不能对应停止绘制)
        }

        //绘制数据
        for (int k=0;k<dataList.size();k++){
            for (int a=0;a<dataList.get(k).size();a++){
                float textX=(transverseStep+6)+(transverseStep/2f)+(k*transverseStep);
                float textY=(longitudinalStep+6)+(longitudinalStep/2f)+(a*longitudinalStep)+(dataTextPaint.getTextSize()/5f);

                //null数据处理
                if (dataList.get(k).get(a)==null){ dataList.get(k).set(a,0); }

                if (dataList.get(k).get(a)>= ALERT_VALUE){
                    dataTextPaint.setColor(Color.RED);
                }else {
                    dataTextPaint.setColor(Color.BLUE);
                }
                canvas.drawText(dataList.get(k).get(a)+"",textX,textY, dataTextPaint);
            }
        }

    }

    /**
     * 设置横轴数据
     * @param items 轴项信息
     * @param name 轴名称
     * @param unit 单位
     */
    public void setTransverse(List<String> items,String name,String unit){
        transverseItems=items;
        transverseName=name;
        transverseUnit=unit;
        postInvalidate();
    }

    /**
     * 设置纵轴数据
     * @param items 轴项信息
     * @param name 轴名称
     * @param unit 单位
     */
    public void setLongitudinal(List<String> items,String name,String unit){
        longitudinalItems=items;
        longitudinalName=name;
        longitudinalUnit=unit;
        postInvalidate();
    }

    /**
     * 设置数据
     * @param dataLists
     */
    public void setData(List<List<Integer>> dataLists){
        this.dataList=dataLists;
        postInvalidate();
    }

}
使用方法
  1. 在xml中引用
<com.example.formdemo.view.FormView
        android:id="@+id/form_view"
        android:layout_margin="20px"
        android:layout_width="match_parent"
        android:layout_height="600px"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
  1. 导入数据
	private void initFormView() {

        List<String> trans=new ArrayList<>();
        trans.add("已售");
        trans.add("库存");
        trans.add("破损");

        List<String> longit=new ArrayList<>();
        longit.add("XS");
        longit.add("S");
        longit.add("M");
        longit.add("L");
        longit.add("XL");
        longit.add("XXL");

        //已售数据
        List<Integer> dataList1=new ArrayList<>();
        dataList1.add(null);
        dataList1.add(67);
        dataList1.add(54);
        dataList1.add(130);
        dataList1.add(45);
        dataList1.add(23);

        //库存数据
        List<Integer> dataList2=new ArrayList<>();
        dataList2.add(34);
        dataList2.add(121);
        dataList2.add(67);
        dataList2.add(78);
        dataList2.add(34);
        dataList2.add(43);

        //破损数据
        List<Integer> dataList3=new ArrayList<>();
        dataList3.add(8);
        dataList3.add(4);
        dataList3.add(0);
        dataList3.add(1);
        dataList3.add(6);
        dataList3.add(4);

        List<List<Integer>> dataList=new ArrayList<>();
        dataList.add(dataList1);
        dataList.add(dataList2);
        dataList.add(dataList3);

        FormView formView=(FormView)findViewById(R.id.form_view);
        formView.setTransverse(trans,"服装","件");
        formView.setLongitudinal(longit,"尺码","");
        formView.setData(dataList);

		//========模拟变化的数据,实现动效===========
		for (int k=0;k<dataList.size();k++){
            for (int a=0;a<dataList.get(k).size();a++){
                int finalA = a;
                int finalK = k;
                //异常处理
                if (dataList.get(k).get(a)==null){
                    dataList.get(k).set(a,0);
                }
                new AbAHeartbeat().start(5, dataList.get(k).get(a), 255, new ActionCallback() {
                    @Override
                    public void toDo(Object o) {
                        dataList.get(finalK).set(finalA, (Integer) o);
                        formView.setData(dataList);
                    }
                });
            }
        }

    }

动态效果辅助类

package com.example.formdemo.util;

import android.os.Looper;
import android.os.Message;


/**
 * 心跳计时器
 */
public class AbAHeartbeat {

    //事件标签
    public final int ACTION_TAG =0xFF;
    //要回调的接口
    private ActionCallback thisActionDo;

    //Handler事件监听器
    private android.os.Handler mHandler = new android.os.Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case ACTION_TAG:

                    thisActionDo.toDo(msg.obj);

                    if ((int)msg.obj==maxValue && forTag==true){
                        doHUi();
                    }
                    break;
            }

        }
    };


    int dataValue;
    int maxValue;
    int countDownTime;
    public void start(int countDownTime,int dataValue,int maxValue,ActionCallback actionDo){
        this.dataValue=dataValue;
        this.maxValue=maxValue;
        this.countDownTime=countDownTime;
        thisActionDo=actionDo;
        for (int a=0;a<maxValue;a++){
            Message msg = mHandler.obtainMessage();
            msg.what = ACTION_TAG;
            msg.obj=a+1;
            mHandler.sendMessageDelayed(msg,countDownTime*a);
        }
    };

    private boolean forTag=true;
    private void doHUi(){
        forTag=false;
        for (int k=0;k<=maxValue-dataValue;k++){
            Message msg2 = mHandler.obtainMessage();
            msg2.what = ACTION_TAG;
            msg2.obj=maxValue-k;
            mHandler.sendMessageDelayed(msg2,countDownTime*k);
        }
    }

}
package com.example.formdemo.util;

public interface ActionCallback {

    void toDo(Object o);

}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绝命三郎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值