From c5a3ea635187933aee3b0160ac0d52ac75869bec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A6=82=E6=84=8F=20=E5=BD=AD?= <2417589739@qq.com>
Date: Fri, 15 Mar 2024 12:09:27 +0800
Subject: [PATCH] =?UTF-8?q?=E5=8E=8B=E5=8A=9B=E9=94=85=E7=95=8C=E9=9D=A2?=
=?UTF-8?q?=E5=BC=80=E5=8F=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../bnframework/common/DashboardView.java | 492 ++++++++++++++++++
.../bnframework/common/ShadowContainer.java | 214 ++++++++
app/src/main/res/drawable/rectancle.xml | 5 +
.../layout/datatab/layout/activity_main.xml | 19 +
app/src/main/res/values/attrs.xml | 12 +
5 files changed, 742 insertions(+)
create mode 100644 app/src/main/java/com/bonait/bnframework/common/DashboardView.java
create mode 100644 app/src/main/java/com/bonait/bnframework/common/ShadowContainer.java
create mode 100644 app/src/main/res/drawable/rectancle.xml
diff --git a/app/src/main/java/com/bonait/bnframework/common/DashboardView.java b/app/src/main/java/com/bonait/bnframework/common/DashboardView.java
new file mode 100644
index 00000000..c220dc07
--- /dev/null
+++ b/app/src/main/java/com/bonait/bnframework/common/DashboardView.java
@@ -0,0 +1,492 @@
+package com.bonait.bnframework.common;
+
+
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.SweepGradient;
+//import android.support.v4.content.ContextCompat;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.animation.LinearInterpolator;
+
+import com.bonait.bnframework.business.ExecuteTheRecipe;
+
+import java.util.Random;
+
+
+/**
+ * DashboardView style 4,仿汽车速度仪表盘
+ * Created by woxingxiao on 2016-12-19.
+ */
+public class DashboardView extends View {
+
+ private int mRadius; // 扇形半径
+ private int mStartAngle = 150; // 起始角度
+ private int mSweepAngle = 240; // 绘制角度
+ private int mMin = 0; // 最小值
+ private int mMax = 180; // 最大值
+ private int mSection = 10; // 值域(mMax-mMin)等分份数
+ private int mPortion = 5; // 一个mSection等分份数
+ private String mHeaderText = "km/h"; // 表头
+ private float mVelocity = mMin; // 实时速度
+ private int mStrokeWidth; // 画笔宽度
+ private int mLength1; // 长刻度的相对圆弧的长度
+ private int mLength2; // 刻度读数顶部的相对圆弧的长度
+ private int mPLRadius; // 指针长半径
+ private int mPSRadius; // 指针短半径
+
+ private int mPadding;
+ private float mCenterX, mCenterY; // 圆心坐标
+ private Paint mPaint;
+ private Path pointerPath=new Path();
+ private Paint pointerPaint=new Paint();
+ private boolean IsInit=false;
+ private RectF mRectFArc;
+ private RectF mRectFInnerArc;
+ private Rect mRectText;
+ private String[] mTexts;
+ private int[] mColors;
+ private Paint mTextPaint=new Paint();
+ private int textSize=40;
+
+ private String unity="℃";
+ private int textColor=Color.parseColor("#03A9F4");
+ private int backgroundColor=Color.parseColor("#292a30");
+
+ public void setMaxValue(int maxValue){
+ mMax=maxValue;
+ init();
+ }
+
+ public void setHeaderText(String header){
+ mHeaderText=header;
+ init();
+ }
+
+ public void setBackgroundColor(int color){
+ backgroundColor=color;
+ }
+
+ public void setTextSize(int size){
+ textSize=size;
+ }
+
+ public void setTextColor(int color){
+ textColor=color;
+ }
+
+ public void setUnity(String mUnity){
+ unity=mUnity;
+ }
+
+ public DashboardView(Context context) {
+ this(context, null);
+ }
+
+ public DashboardView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DashboardView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ init();
+ }
+
+ private void init() {
+ mStrokeWidth = dp2px(3);
+ mLength1 = dp2px(8) + mStrokeWidth;
+ mLength2 = mLength1 + dp2px(4);
+
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setStrokeCap(Paint.Cap.ROUND);
+
+ mRectFArc = new RectF();
+ mRectFInnerArc = new RectF();
+ mRectText = new Rect();
+
+ mTexts = new String[mSection + 1]; // 需要显示mSection + 1个刻度读数
+ for (int i = 0; i < mTexts.length; i++) {
+ int n = (mMax - mMin) / mSection;
+ mTexts[i] = String.valueOf(mMin + i * n);
+ }
+ mColors = new int[]{Color.GREEN, Color.YELLOW, Color.RED};//内圆渐变设置
+ }
+
+
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ mPadding = Math.max(
+ Math.max(getPaddingLeft(), getPaddingTop()),
+ Math.max(getPaddingRight(), getPaddingBottom())
+ );
+ setPadding(mPadding, mPadding, mPadding, mPadding);
+
+ int width = resolveSize(dp2px(260), widthMeasureSpec);
+ mRadius = (width - mPadding * 2 - mStrokeWidth * 2) / 2;
+
+ // 由起始角度确定的高度
+ float[] point1 = getCoordinatePoint(mRadius, mStartAngle);
+ // 由结束角度确定的高度
+ float[] point2 = getCoordinatePoint(mRadius, mStartAngle + mSweepAngle);
+ int height = (int) Math.max(point1[1] + mRadius + mStrokeWidth * 2,
+ point2[1] + mRadius + mStrokeWidth * 2);
+ setMeasuredDimension(width, height + getPaddingTop() + getPaddingBottom());
+
+ mCenterX = mCenterY = getMeasuredWidth() / 2f;
+ mRectFArc.set(
+ getPaddingLeft() + mStrokeWidth,
+ getPaddingTop() + mStrokeWidth,
+ getMeasuredWidth() - getPaddingRight() - mStrokeWidth,
+ getMeasuredWidth() - getPaddingBottom() - mStrokeWidth
+ );
+
+ mPaint.setTextSize(sp2px(16));
+ mPaint.getTextBounds("0", 0, "0".length(), mRectText);
+ mRectFInnerArc.set(
+ getPaddingLeft() + mLength2 + mRectText.height() + dp2px(30),
+ getPaddingTop() + mLength2 + mRectText.height() + dp2px(30),
+ getMeasuredWidth() - getPaddingRight() - mLength2 - mRectText.height() - dp2px(30),
+ getMeasuredWidth() - getPaddingBottom() - mLength2 - mRectText.height() - dp2px(30)
+ );
+
+ mPLRadius = mRadius - dp2px(10);
+ mPSRadius = dp2px(25);
+ }
+
+ private float Scale(float InputValue, float InputMax, float InputMin, float OutMax, float OutMin)
+ {
+ float value = ((OutMax - OutMin) * (InputValue - InputMin)) / (InputMax - InputMin) + OutMin;
+ if (value > OutMax) return OutMax;
+ if (value < OutMin) return OutMin;
+ return value;
+ }
+
+ private void CanvasInit(Canvas canvas){
+ mPaint.reset();
+ pointerPaint.reset();
+ pointerPath.reset();
+ mTextPaint.reset();
+
+ canvas.drawColor(backgroundColor);//背景色
+ this.setPadding(10,30,10,10);
+
+ /**
+ * 画圆弧
+ */
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(mStrokeWidth);
+ mPaint.setColor(Color.parseColor("#03A9F4"));
+ canvas.drawArc(mRectFArc, mStartAngle, mSweepAngle, false, mPaint);
+
+ /**
+ * 画长刻度
+ * 画好起始角度的一条刻度后通过canvas绕着原点旋转来画剩下的长刻度
+ */
+ double cos = Math.cos(Math.toRadians(mStartAngle - 180));
+ double sin = Math.sin(Math.toRadians(mStartAngle - 180));
+ float x0 = (float) (mPadding + mStrokeWidth + mRadius * (1 - cos));
+ float y0 = (float) (mPadding + mStrokeWidth + mRadius * (1 - sin));
+ float x1 = (float) (mPadding + mStrokeWidth + mRadius - (mRadius - mLength1) * cos);
+ float y1 = (float) (mPadding + mStrokeWidth + mRadius - (mRadius - mLength1) * sin);
+
+ canvas.save();
+ canvas.drawLine(x0, y0, x1, y1, mPaint);
+ float angle = mSweepAngle * 1f / mSection;
+ for (int i = 0; i < mSection; i++) {
+ canvas.rotate(angle, mCenterX, mCenterY);
+ canvas.drawLine(x0, y0, x1, y1, mPaint);
+ }
+ canvas.restore();
+
+ /**
+ * 画短刻度
+ * 同样采用canvas的旋转原理
+ */
+ canvas.save();
+ mPaint.setStrokeWidth(mStrokeWidth / 2f);
+ float x2 = (float) (mPadding + mStrokeWidth + mRadius - (mRadius - 2 * mLength1 / 3f) * cos);
+ float y2 = (float) (mPadding + mStrokeWidth + mRadius - (mRadius - 2 * mLength1 / 3f) * sin);
+ canvas.drawLine(x0, y0, x2, y2, mPaint);
+ angle = mSweepAngle * 1f / (mSection * mPortion);
+ for (int i = 1; i < mSection * mPortion; i++) {
+ canvas.rotate(angle, mCenterX, mCenterY);
+ if (i % mPortion == 0) { // 避免与长刻度画重合
+ continue;
+ }
+ canvas.drawLine(x0, y0, x2, y2, mPaint);
+ }
+ canvas.restore();
+
+ /**
+ * 画长刻度读数
+ */
+ mPaint.setTextSize(sp2px(16));
+ mPaint.setStyle(Paint.Style.FILL);
+ float α;
+ float[] p;
+ angle = mSweepAngle * 1f / mSection;
+ for (int i = 0; i <= mSection; i++) {
+ α = mStartAngle + angle * i;
+ p = getCoordinatePoint(mRadius - mLength2, α);
+ if (α % 360 > 135 && α % 360 < 225) {
+ mPaint.setTextAlign(Paint.Align.LEFT);
+ } else if ((α % 360 >= 0 && α % 360 < 45) || (α % 360 > 315 && α % 360 <= 360)) {
+ mPaint.setTextAlign(Paint.Align.RIGHT);
+ } else {
+ mPaint.setTextAlign(Paint.Align.CENTER);
+ }
+ try {
+ mPaint.getTextBounds(mHeaderText, 0, mTexts[i].length(), mRectText);
+ }catch(Exception e){}
+
+ int txtH = mRectText.height();
+ if (i <= 1 || i >= mSection - 1) {
+ canvas.drawText(mTexts[i], p[0], p[1] + txtH / 2, mPaint);
+ } else if (i == 3) {
+ canvas.drawText(mTexts[i], p[0] + txtH / 2, p[1] + txtH, mPaint);
+ } else if (i == mSection - 3) {
+ canvas.drawText(mTexts[i], p[0] - txtH / 2, p[1] + txtH, mPaint);
+ } else {
+ canvas.drawText(mTexts[i], p[0], p[1] + txtH, mPaint);
+ }
+ }
+
+ mPaint.setStrokeCap(Paint.Cap.SQUARE);
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(dp2px(10));
+ mPaint.setShader(generateSweepGradient());
+ canvas.drawArc(mRectFInnerArc, mStartAngle + 1, mSweepAngle - 2, false, mPaint);
+
+ mPaint.setStrokeCap(Paint.Cap.ROUND);
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setShader(null);
+
+ /**
+ * 画表头
+ * 没有表头就不画
+ */
+ if (!TextUtils.isEmpty(mHeaderText)) {
+ mPaint.setTextSize(textSize);
+ mPaint.setColor(textColor);
+ mPaint.setTextAlign(Paint.Align.CENTER);
+ mPaint.getTextBounds(mHeaderText, 0, mHeaderText.length(), mRectText);
+ canvas.drawText(mHeaderText, mCenterX, mCenterY + dp2px(100), mPaint);
+// canvas.drawText(mHeaderText, mCenterX, mCenterY - mRectText.height() * 3, mPaint);
+ }
+
+ /**
+ * 画指针
+ */
+ float θ = mStartAngle + mSweepAngle * (mVelocity - mMin) / (mMax - mMin); // 指针与水平线夹角
+ int r = mRadius / 8;
+ int tempWidth = 10;
+ canvas.save();
+ canvas.translate(getMeasuredWidth() / 2, mCenterY);
+ canvas.rotate(θ);
+ pointerPaint.setColor(Color.parseColor("#03A9F4"));
+ pointerPaint.setAntiAlias(true);//抗锯齿
+ pointerPath.moveTo(mRadius-this.getPaddingLeft()-30, 0);
+ pointerPath.lineTo(0, 0 - tempWidth);
+ pointerPath.lineTo(0, 0 + tempWidth);
+ pointerPath.close();
+ canvas.drawPath(pointerPath, pointerPaint);
+ canvas.restore();
+
+ /**
+ * 中心圆
+ */
+ mPaint.setColor(Color.parseColor("#CCCCCC"));
+ canvas.drawCircle(mCenterX, mCenterY, r, mPaint);
+
+ /**
+ * 画实时度数值
+ */
+ mPaint.setColor(Color.parseColor("#6200EE"));
+ mPaint.setStrokeWidth(dp2px(2));
+ int xOffset = dp2px(22);
+
+ mTextPaint.reset();
+ mTextPaint.setColor(Color.WHITE);
+ mTextPaint.setTextAlign(Paint.Align.CENTER);
+ mTextPaint.setTextSize(textSize);
+ mTextPaint.setColor(textColor);
+ mTextPaint.setAntiAlias(true);
+ canvas.drawText( String.format("%.2f", mVelocity)+unity,mCenterX, mCenterY + dp2px(60),mTextPaint);
+
+ }
+
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ CanvasInit(canvas);
+ }
+
+ /**
+ * 数码管样式
+ */
+ // 1
+ // ——
+ // 2 | | 3
+ // —— 4
+ // 5 | | 6
+ // ——
+ // 7
+ private void drawDigitalTube(Canvas canvas, float num, int xOffset) {
+ float x = mCenterX + xOffset;
+ float y = mCenterY + dp2px(40);
+ int lx = dp2px(5);
+ int ly = dp2px(10);
+ int gap = dp2px(2);
+
+ // 1
+ mPaint.setAlpha(num == -1 || num == 1 || num == 4 ? 25 : 255);
+ canvas.drawLine(x - lx, y, x + lx, y, mPaint);
+ // 2
+ mPaint.setAlpha(num == -1 || num == 1 || num == 2 || num == 3 || num == 7 ? 25 : 255);
+ canvas.drawLine(x - lx - gap, y + gap, x - lx - gap, y + gap + ly, mPaint);
+ // 3
+ mPaint.setAlpha(num == -1 || num == 5 || num == 6 ? 25 : 255);
+ canvas.drawLine(x + lx + gap, y + gap, x + lx + gap, y + gap + ly, mPaint);
+ // 4
+ mPaint.setAlpha(num == -1 || num == 0 || num == 1 || num == 7 ? 25 : 255);
+ canvas.drawLine(x - lx, y + gap * 2 + ly, x + lx, y + gap * 2 + ly, mPaint);
+ // 5
+ mPaint.setAlpha(num == -1 || num == 1 || num == 3 || num == 4 || num == 5 || num == 7
+ || num == 9 ? 25 : 255);
+ canvas.drawLine(x - lx - gap, y + gap * 3 + ly,
+ x - lx - gap, y + gap * 3 + ly * 2, mPaint);
+ // 6
+ mPaint.setAlpha(num == -1 || num == 2 ? 25 : 255);
+ canvas.drawLine(x + lx + gap, y + gap * 3 + ly,
+ x + lx + gap, y + gap * 3 + ly * 2, mPaint);
+ // 7
+ mPaint.setAlpha(num == -1 || num == 1 || num == 4 || num == 7 ? 25 : 255);
+ canvas.drawLine(x - lx, y + gap * 4 + ly * 2, x + lx, y + gap * 4 + ly * 2, mPaint);
+ }
+
+ private int dp2px(int dp) {
+ return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
+ Resources.getSystem().getDisplayMetrics());
+ }
+
+ private int sp2px(int sp) {
+ return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
+ Resources.getSystem().getDisplayMetrics());
+ }
+
+ public float[] getCoordinatePoint(int radius, float angle) {
+ float[] point = new float[2];
+
+ double arcAngle = Math.toRadians(angle); //将角度转换为弧度
+ if (angle < 90) {
+ point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
+ point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
+ } else if (angle == 90) {
+ point[0] = mCenterX;
+ point[1] = mCenterY + radius;
+ } else if (angle > 90 && angle < 180) {
+ arcAngle = Math.PI * (180 - angle) / 180.0;
+ point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
+ point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
+ } else if (angle == 180) {
+ point[0] = mCenterX - radius;
+ point[1] = mCenterY;
+ } else if (angle > 180 && angle < 270) {
+ arcAngle = Math.PI * (angle - 180) / 180.0;
+ point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
+ point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
+ } else if (angle == 270) {
+ point[0] = mCenterX;
+ point[1] = mCenterY - radius;
+ } else {
+ arcAngle = Math.PI * (360 - angle) / 180.0;
+ point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
+ point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
+ }
+
+ return point;
+ }
+
+ private SweepGradient generateSweepGradient() {
+ SweepGradient sweepGradient = new SweepGradient(mCenterX, mCenterY,
+ mColors,
+ new float[]{0, 140 / 360f, mSweepAngle / 360f}
+ );
+
+ Matrix matrix = new Matrix();
+ matrix.setRotate(mStartAngle - 3, mCenterX, mCenterY);
+ sweepGradient.setLocalMatrix(matrix);
+
+ return sweepGradient;
+ }
+
+ public float getVelocity() {
+ return mVelocity;
+ }
+
+ public void setVelocity(float velocity) {
+ if (mVelocity == velocity || velocity < mMin || velocity > mMax) {
+ return;
+ }
+ mVelocity = velocity;
+ postInvalidate();
+ }
+
+
+ private boolean isAnimFinished = true;
+
+ public void setValue(float value){
+ if (isAnimFinished) {
+ ObjectAnimator animator = ObjectAnimator.ofFloat(this, "setVelocity", getVelocity(), value);
+ animator.setDuration(100).setInterpolator(new LinearInterpolator());
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ isAnimFinished = false;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ isAnimFinished = true;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ isAnimFinished = true;
+ }
+ });
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float value = (float)animation.getAnimatedValue();
+ setVelocity(value);
+ }
+ });
+ animator.start();
+ }
+
+ }
+
+}
+
diff --git a/app/src/main/java/com/bonait/bnframework/common/ShadowContainer.java b/app/src/main/java/com/bonait/bnframework/common/ShadowContainer.java
new file mode 100644
index 00000000..5927b76c
--- /dev/null
+++ b/app/src/main/java/com/bonait/bnframework/common/ShadowContainer.java
@@ -0,0 +1,214 @@
+package com.bonait.bnframework.common;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.bonait.bnframework.R;
+
+
+/**
+ * 阴影容器
+ * 需要在 res->values->attrs.xml中添加以下内容
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * Reference: https://github.com/cjlemon/Shadow
+ */
+public class ShadowContainer extends ViewGroup {
+
+ private final float deltaLength;
+ private final float cornerRadius;
+ private final Paint mShadowPaint;
+ private boolean drawShadow;
+
+ public ShadowContainer(Context context) {
+ this(context, null);
+ }
+
+ public ShadowContainer(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ShadowContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ShadowContainer);
+ int shadowColor = a.getColor(R.styleable.ShadowContainer_containerShadowColor, Color.RED);
+ float shadowRadius = a.getDimension(R.styleable.ShadowContainer_containerShadowRadius, 0);
+ deltaLength = a.getDimension(R.styleable.ShadowContainer_containerDeltaLength, 0);
+ cornerRadius = a.getDimension(R.styleable.ShadowContainer_containerCornerRadius, 0);
+ float dx = a.getDimension(R.styleable.ShadowContainer_deltaX, 0);
+ float dy = a.getDimension(R.styleable.ShadowContainer_deltaY, 0);
+ drawShadow = a.getBoolean(R.styleable.ShadowContainer_enable, true);
+ a.recycle();
+ mShadowPaint = new Paint();
+ mShadowPaint.setStyle(Paint.Style.FILL);
+ mShadowPaint.setAntiAlias(true);
+ mShadowPaint.setColor(shadowColor);
+ mShadowPaint.setShadowLayer(shadowRadius, dx, dy, shadowColor);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (drawShadow) {
+ /*
+ setShadowLayer()/setMaskFilter is not support hardware acceleration, so using LAYER_TYPE_SOFTWARE, but software layers isn't always good.
+ LAYER_TYPE_SOFTWARE: software layers should be avoided when the affected view tree updates often.
+ */
+ if (getLayerType() != LAYER_TYPE_SOFTWARE) {
+ setLayerType(LAYER_TYPE_SOFTWARE, null);
+ }
+ View child = getChildAt(0);
+ int left = child.getLeft();
+ int top = child.getTop();
+ int right = child.getRight();
+ int bottom = child.getBottom();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ canvas.drawRoundRect(left, top, right, bottom, cornerRadius, cornerRadius, mShadowPaint);
+ } else {
+ Path drawablePath = new Path();
+ drawablePath.moveTo(left + cornerRadius, top);
+ drawablePath.arcTo(new RectF(left, top, left + 2 * cornerRadius, top + 2 * cornerRadius), -90, -90, false);
+ drawablePath.lineTo(left, bottom - cornerRadius);
+ drawablePath.arcTo(new RectF(left, bottom - 2 * cornerRadius, left + 2 * cornerRadius, bottom), 180, -90, false);
+ drawablePath.lineTo(right - cornerRadius, bottom);
+ drawablePath.arcTo(new RectF(right - 2 * cornerRadius, bottom - 2 * cornerRadius, right, bottom), 90, -90, false);
+ drawablePath.lineTo(right, top + cornerRadius);
+ drawablePath.arcTo(new RectF(right - 2 * cornerRadius, top, right, top + 2 * cornerRadius), 0, -90, false);
+ drawablePath.close();
+ canvas.drawPath(drawablePath, mShadowPaint);
+ }
+ }
+ super.dispatchDraw(canvas);
+ }
+
+ /**
+ * setMeasuredDimension(): store the modified width and modified height.
+ *
+ * @param widthMeasureSpec the original width
+ * @param heightMeasureSpec the original height
+ */
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (getChildCount() != 1) {
+ throw new IllegalStateException("Child View can have only one!!!");
+ }
+ int measuredWidth = getMeasuredWidth();
+ int measuredHeight = getMeasuredHeight();
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ View child = getChildAt(0);
+ MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
+ int childBottomMargin = (int) (Math.max(deltaLength, layoutParams.bottomMargin) + 1);
+ int childLeftMargin = (int) (Math.max(deltaLength, layoutParams.leftMargin) + 1);
+ int childRightMargin = (int) (Math.max(deltaLength, layoutParams.rightMargin) + 1);
+ int childTopMargin = (int) (Math.max(deltaLength, layoutParams.topMargin) + 1);
+ int widthMeasureSpecMode;
+ int widthMeasureSpecSize;
+ int heightMeasureSpecMode;
+ int heightMeasureSpecSize;
+ if (widthMode == MeasureSpec.UNSPECIFIED) {
+ widthMeasureSpecMode = MeasureSpec.UNSPECIFIED;
+ widthMeasureSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+ } else {
+ if (layoutParams.width == MarginLayoutParams.MATCH_PARENT) {
+ widthMeasureSpecMode = MeasureSpec.EXACTLY;
+ widthMeasureSpecSize = measuredWidth - childLeftMargin - childRightMargin;
+ } else if (MarginLayoutParams.WRAP_CONTENT == layoutParams.width) {
+ widthMeasureSpecMode = MeasureSpec.AT_MOST;
+ widthMeasureSpecSize = measuredWidth - childLeftMargin - childRightMargin;
+ } else {
+ widthMeasureSpecMode = MeasureSpec.EXACTLY;
+ widthMeasureSpecSize = layoutParams.width;
+ }
+ }
+ if (heightMode == MeasureSpec.UNSPECIFIED) {
+ heightMeasureSpecMode = MeasureSpec.UNSPECIFIED;
+ heightMeasureSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+ } else {
+ if (layoutParams.height == MarginLayoutParams.MATCH_PARENT) {
+ heightMeasureSpecMode = MeasureSpec.EXACTLY;
+ heightMeasureSpecSize = measuredHeight - childBottomMargin - childTopMargin;
+ } else if (MarginLayoutParams.WRAP_CONTENT == layoutParams.height) {
+ heightMeasureSpecMode = MeasureSpec.AT_MOST;
+ heightMeasureSpecSize = measuredHeight - childBottomMargin - childTopMargin;
+ } else {
+ heightMeasureSpecMode = MeasureSpec.EXACTLY;
+ heightMeasureSpecSize = layoutParams.height;
+ }
+ }
+ measureChild(child, MeasureSpec.makeMeasureSpec(widthMeasureSpecSize, widthMeasureSpecMode), MeasureSpec.makeMeasureSpec(heightMeasureSpecSize, heightMeasureSpecMode));
+ int parentWidthMeasureSpec = MeasureSpec.getMode(widthMeasureSpec);
+ int parentHeightMeasureSpec = MeasureSpec.getMode(heightMeasureSpec);
+ int height = measuredHeight;
+ int width = measuredWidth;
+ int childHeight = child.getMeasuredHeight();
+ int childWidth = child.getMeasuredWidth();
+ if (parentHeightMeasureSpec == MeasureSpec.AT_MOST) {
+ height = childHeight + childTopMargin + childBottomMargin;
+ }
+ if (parentWidthMeasureSpec == MeasureSpec.AT_MOST) {
+ width = childWidth + childRightMargin + childLeftMargin;
+ }
+ if (width < childWidth + 2 * deltaLength) {
+ width = (int) (childWidth + 2 * deltaLength);
+ }
+ if (height < childHeight + 2 * deltaLength) {
+ height = (int) (childHeight + 2 * deltaLength);
+ }
+ if (height != measuredHeight || width != measuredWidth) {
+ setMeasuredDimension(width, height);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ View child = getChildAt(0);
+ int measuredWidth = getMeasuredWidth();
+ int measuredHeight = getMeasuredHeight();
+ int childMeasureWidth = child.getMeasuredWidth();
+ int childMeasureHeight = child.getMeasuredHeight();
+ child.layout((measuredWidth - childMeasureWidth) / 2, (measuredHeight - childMeasureHeight) / 2, (measuredWidth + childMeasureWidth) / 2, (measuredHeight + childMeasureHeight) / 2);
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ return new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT);
+ }
+
+ @Override
+ protected LayoutParams generateLayoutParams(LayoutParams p) {
+ return new MarginLayoutParams(p);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new MarginLayoutParams(getContext(), attrs);
+ }
+
+ public void setDrawShadow(boolean drawShadow) {
+ if (this.drawShadow == drawShadow) {
+ return;
+ }
+ this.drawShadow = drawShadow;
+ postInvalidate();
+ }
+
+}
+
+
diff --git a/app/src/main/res/drawable/rectancle.xml b/app/src/main/res/drawable/rectancle.xml
new file mode 100644
index 00000000..f738fb31
--- /dev/null
+++ b/app/src/main/res/drawable/rectancle.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/datatab/layout/activity_main.xml b/app/src/main/res/layout/datatab/layout/activity_main.xml
index 84d0aea9..7868ea51 100644
--- a/app/src/main/res/layout/datatab/layout/activity_main.xml
+++ b/app/src/main/res/layout/datatab/layout/activity_main.xml
@@ -19,6 +19,7 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file