android 炫酷表格
程序员文章站
2022-05-26 16:12:14
...
android 自定义view实现炫酷表格,还是先来个效果图
直接上传代码:
public LineViewDemo(Context context, AttributeSet attrs) {
super(context, attrs);
inits(context, attrs);
}
public LineViewDemo(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inits(context, attrs);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public LineViewDemo(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
inits(context, attrs);
}
private void inits(Context context, AttributeSet attrs) {
this.mContext = context;
mDivderPaint = new Paint();
mDivderPaint.setAntiAlias(true);
mDivderPaint.setStyle(Paint.Style.STROKE);
mDivderPaint.setStrokeWidth(dp2px(0.5f));
mDivderPaint.setColor(Color.GRAY);
mPotinPaint = new Paint();
mPotinPaint.setAntiAlias(true);
mPotinPaint.setStyle(Paint.Style.FILL);
mBgPaint = new Paint();
mBgPaint.setAntiAlias(true);
mBgPaint.setStyle(Paint.Style.FILL);
mLinePaint = new Paint();
mLinePaint.setAntiAlias(true);
mLinePaint.setStyle(Paint.Style.FILL);
mLinePaint.setStrokeWidth(dp2px(1f));
mLinePaint.setColor(Color.parseColor("#42afff"));
mHintPaint = new TextPaint();
mHintPaint.setColor(Color.parseColor("#332b2c"));
mHintPaint.setTextSize(sp2px(10));
mTextPaint = new Paint();
mTextPaint.setColor(Color.GRAY);
mTextPaint.setTextSize(sp2px(12));
//模拟数据
getDatas();
}
private void getDatas() {
mSpanY = dp2px(60);
mSpanX = dp2px(60);
mSpanTop = dp2px(40);
mR = dp2px(10);
mHintSpan = dp2px(10);
mTextSpan = dp2px(5);
mBottomR = dp2px(4);
mValueX = new ArrayList<>();
mValueY = new ArrayList<>();
mPoints = new ArrayList<>();
for (int i = 1; i <= 18; i++) {
mValueX.add(i + "题");
}
for (int i = 0; i <= 5; i++) {
mValueY.add(i * 2 + "");
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
this.mCanvas = canvas;
drawLineX(canvas);
drawLineY(canvas);
drawLinePath(canvas);
drawPoint(canvas);
drawHint();
}
/**
* 绘制填充颜色
*/
private void drawLinePath(Canvas canvas) {
Path path = new Path();
path.moveTo(mSpanX, mRow * (1 + 2 * 5) / 2 + mSpanTop);
for (int i = 0; i < mValueX.size(); i++) {
PointEntity pointS = mPoints.get(i);
path.lineTo(pointS.getX(), pointS.getY());
}
path.lineTo(mSpanX + mCol * 17.f, mRow * (1 + 2 * 5) / 2 + mSpanTop);
path.close();
//绘制填充颜色
//需完成颜色的渐变
Shader shader = new LinearGradient(mSpanY, mRow, mSpanY, mRootH - mSpanX, Color.parseColor("#aa42afff"), Color.parseColor("#0042afff"), Shader.TileMode.REPEAT);
mLinePaint.setShader(shader);
canvas.drawPath(path, mLinePaint);
//还原折线画笔 避免下次重新绘制颜色不对
mLinePaint.reset();
mLinePaint.setAntiAlias(true);
mLinePaint.setStyle(Paint.Style.FILL);
mLinePaint.setStrokeWidth(dp2px(1f));
mLinePaint.setColor(Color.parseColor("#42afff"));
}
//画点及折线
private void drawPoint(Canvas canvas) {
for (int i = 0; i < mValueX.size(); i++) {
PointEntity pointS = mPoints.get(i);
//先画线
if (i + 1 < mValueX.size()) {
PointEntity pointE = mPoints.get(i + 1);
canvas.drawLine(pointS.getX(), pointS.getY(), pointE.getX(), pointE.getY(), mLinePaint);
}
//在画点
Shader shader;
if (i == mSelectInde) {
// int[] colors:表示所需要的渐变颜色数组 float[] stops:表示每个渐变颜色所在的位置百分点,取值0-1,数量必须与colors数组保持一致,不然直接crash,一般第一个数值取0,最后一个数值取1;如果第一个数值和最后一个数值并没有取0和1,比如我们这里取一个位置数组:{0.2,0.5,0.8},起始点是0.2百分比位置,结束点是0.8百分比位置,而0-0.2百分比位置和0.8-1.0百分比的位置都是没有指定颜色的。而这些位置的颜色就是根据我们指定的TileMode空白区域填充模式来自行填充!!!
shader = new RadialGradient(pointS.getX(), pointS.getY(), dp2px(6),
new int[]{Color.parseColor("#42afff"), Color.parseColor("#ffffff"), Color.parseColor("#42afff")},
new float[]{0, 0.8f, 1},
Shader.TileMode.CLAMP);
mPotinPaint.setShader(shader);
canvas.drawCircle(pointS.getX(), pointS.getY(), dp2px(6), mPotinPaint);
} else {
shader = new RadialGradient(pointS.getX(), pointS.getY(), dp2px(4),
new int[]{Color.parseColor("#42afff"), Color.parseColor("#42afff"), Color.parseColor("#ffffff")},
new float[]{0, 0.7f, 1},
Shader.TileMode.CLAMP);
mPotinPaint.setShader(shader);
canvas.drawCircle(pointS.getX(), pointS.getY(), dp2px(4), mPotinPaint);
}
}
}
/**
* 绘制提示文本
*/
private void drawHint() {
int marBottom = dp2px(6);
int radio = dp2px(2);
boolean isEnable = mSelectInde>=0&&mSelectInde<mPoints.size();
if (!isEnable){
return;
}
PointEntity p = mPoints.get(mSelectInde);
int x = p.getX();
int y = p.getY();
String msg = "而这些位置的颜色就是根据我们指定的TileMode空白区域填充模式来自行填充!!!有时效果我们是不可控的。所以为了方便起见,建议大家st";
int textW = getTextW(msg);
int textH = getTextH(msg);
Log.e(TAG, "drawHint: textw="+textW +"\ntextH="+textH+"\nx="+x+"\ny="+y);
if (textW>=mMaxHintW){
int n = textW/mMaxHintW;
n = n>3? 3:n;
textW = mMaxHintW;
textH = textH*n;
}
Log.e(TAG, "drawHint: textw="+textW +"\ntextH="+textH);
int left ;
int top = y-textH-dp2px(20);
if (textW/2>=x){
//左侧显示不下 ,从最左边开始
left = mHintSpan;
}else if (x+textW/2>=mRootW){
//右侧显示不下 ,从最右边开始
left = mRootW-mHintSpan-textW;
}else {
left = x-textW/2;
}
//画白色填充
Path path = new Path();
path.moveTo(left-mTextSpan,top-mTextSpan);
path.lineTo(left+textW+mTextSpan,top-mTextSpan);
path.lineTo(left+textW+mTextSpan,top+textH+mTextSpan);
path.lineTo(x-mBottomR,top+textH+mTextSpan);
path.lineTo(x,y-marBottom);
path.lineTo(x+mBottomR,top+textH+mTextSpan);
path.lineTo(left-mTextSpan,top+textH+mTextSpan);
path.close();
mBgPaint.setColor(Color.WHITE);
mCanvas.drawPath(path,mBgPaint);
//画红色边框
float[] pts = new float[]{
left-mTextSpan,top-mTextSpan,left+textW+mTextSpan,top-mTextSpan,
left+textW+mTextSpan,top-mTextSpan,left+textW+mTextSpan,top+textH+mTextSpan,
left+textW+mTextSpan,top+textH+mTextSpan,x-mBottomR,top+textH+mTextSpan,
x-mBottomR,top+textH+mTextSpan,x,y-marBottom,
x,y-marBottom,x+mBottomR,top+textH+mTextSpan,
x+mBottomR,top+textH+mTextSpan,left-mTextSpan,top+textH+mTextSpan,
left-mTextSpan,top+textH+mTextSpan,left-mTextSpan,top-mTextSpan
};
mBgPaint.setColor(Color.RED);
mCanvas.drawLines(pts, mBgPaint);
mBgPaint.setColor(Color.WHITE);
mCanvas.drawLine(x-mBottomR,top+textH+mTextSpan,x+mBottomR,top+textH+mTextSpan,mBgPaint);
StaticLayout layout = new StaticLayout(msg,mHintPaint,textW, Layout.Alignment.ALIGN_NORMAL,1f,0f,true);
Log.e(TAG, "drawHint: left="+left +"\ntop="+top);
mCanvas.translate(left,top);
layout.draw(mCanvas);
mCanvas.restore();
}
//画横线及y轴刻度
private void drawLineY(Canvas canvas) {
int width = getTextH(mValueY.get(0));
int heigt = getTextH(mValueY.get(0));
for (int i = 0; i < mValueY.size(); i++) {
//横线
canvas.drawLine(mSpanY - dp2px(10), mRow * (1 + 2 * i) / 2 + mSpanTop, mRootW, mRow * (1 + 2 * i) / 2 + mSpanTop, mDivderPaint);
//画文字
String text = mValueY.get(mValueY.size() - i - 1);
int x = mSpanY - width - dp2px(20);
if (i == 0) {
x -= dp2px(3);
}
canvas.drawText(text, x, mRow * (1 + 2 * i) / 2 + heigt / 2 + mSpanTop, mTextPaint);
}
width = getTextH("正确数");
heigt = getTextH("正确数");
canvas.drawText("正确数", mSpanX - width - dp2px(4), mSpanTop / 2 + heigt, mTextPaint);
}
//画竖线及x轴刻度
private void drawLineX(Canvas canvas) {
int width = getTextH(mValueX.get(0));
int heigt = getTextH(mValueX.get(0));
for (int i = 0; i < mValueX.size(); i++) {
//竖线
canvas.drawLine(mSpanX + mCol * i, mSpanTop, mSpanX + mCol * i, mRootH - dp2px(10) - mSpanX, mDivderPaint);
//画文字
canvas.drawText(mValueX.get(i), mSpanX + mCol * i - width, mRootH - mSpanX + heigt, mTextPaint);
}
}
//第一次才加载模拟数据
private boolean isFirst = true;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mRootH = getMeasuredHeight();
mRootW = getMeasuredWidth();
mCol = (int) ((mRootW - mSpanY) * 1.0f / mValueX.size() + 0.5f);
mRow = (int) ((mRootH - mSpanX - mSpanTop) * 1.0f / mValueY.size() + 0.5f);
mMaxHintW = mRootW/3;
Log.e(TAG, "mRootH=" + mRootH +
"\nmRootW=" + mRootW +
"\nmCol=" + mCol +
"\nmRow=" + mRow+
"\nmMaxHintW=" + mMaxHintW);
//第一次才加载模拟数据
if (!isFirst){
return;
}else {
isFirst = false;
}
/**
* 模拟数据
*/
Random random = new Random();
mPoints.clear();
for (int i = 0; i < 18; i++) {
PointEntity p = new PointEntity(mSpanX + mCol * i, (8 - random.nextInt(8)) * mRow / 2 + mRow / 2 + mSpanTop);
Log.e(TAG, "index=" + i + "\np.x=" + p.getX() + "\np.y=" + p.getY());
mPoints.add(p);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
actionDown(event);
break;
case MotionEvent.ACTION_UP:
actionUp(event);
break;
case MotionEvent.ACTION_MOVE:
actionMove(event);
break;
}
return super.onTouchEvent(event);
}
//按下
private void actionDown(MotionEvent event) {
float x = event.getX();
float y = event.getY();
for (int i = 0; i < mPoints.size(); i++) {
PointEntity p = mPoints.get(i);
int left = p.getX() - mR;
int right = p.getX() + mR;
int top = p.getY() - mR;
int bottom = p.getY() + mR;
boolean isInner = left <= x && right >= x && top <= y && bottom >= y;
if (isInner) {
if (mClickListener != null) {
mClickListener.onClickPoint(i,(int)x,(int)y);
}
upDataView(i);
}
}
}
private void upDataView(int position) {
mSelectInde = position;
invalidate();
Log.e(TAG, "upDataView: -----------------------------------------"+position );
}
private int dp2px(float d) {
float scale = mContext.getResources().getDisplayMetrics().density;
return (int) (d * scale + 0.5f);
}
private int sp2px(float s) {
float fontScale = mContext.getResources().getDisplayMetrics().scaledDensity;
return (int) (s * fontScale + 0.5f);
}
private int getTextW(String text) {
Rect textBounds = new Rect();
mTextPaint.getTextBounds(text, 0, text.length(), textBounds);
return textBounds.width();
}
private int getTextH(String text) {
Rect textBounds = new Rect();
mTextPaint.getTextBounds(text, 0, text.length(), textBounds);
mTextPaint.measureText(text);
return textBounds.height();
}
上一篇: 给媳妇惹生气了