代码之家  ›  专栏  ›  技术社区  ›  kubiej21

使用drawColor()显示自定义画布工件

  •  0
  • kubiej21  · 技术社区  · 7 年前

    我已经创建了一个自定义视图来绘制百分表。如果我为针的角度设置了一个静态值,则仪表将按预期绘制(请参见下面的第一个图像)。如果我试图通过runOnUiThread设置针的角度,那么我就有奇怪的针伪影(见第二张图)。

    enter image description here enter image description here

    package net.dynu.kubie.redneksldhlr;
    
    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.Rect;
    import android.util.AttributeSet;
    import android.util.TypedValue;
    import android.view.View;
    
    public class DialGaugeView extends View {
    
    private int height, width, min = 0;
    private int numberFontSize, titleFontSize = 0;
    private float dialEdgeStroke, dialEdgeRadius = 0;
    private float dialFaceRadius = 0;
    private float tickMajorStroke, tickSweep, tickAngleStart, tickAngleEnd, tickEdgeRadius, tickInnerMajorRadius, tickInnerMinorRadius = 0;
    private int tickMajorCount = 0;
    private float tickMinorStroke = 0;
    private int tickMinorCount = 0;
    private int tickTotalCount = 0;
    private int numberMin, numberMax = 0;
    private float numberRadius = 0;
    private float arrowTipRadius, arrowRearRadius = 0;
    private float arrowCenterRadius, arrowPinRadius = 0;
    private float sensorInput = 0;
    private float temp = 0;
    private Paint paint;
    private boolean isInit;
    private float titleRadius = 0;
    private Rect rect = new Rect();
    private Path path = new Path();
    private String titleStr = "";
    
    //Variables common to drawing functions
    private float center_x, center_y = 0;
    private float x1, x2, x3, y1, y2, y3 = 0;
    private float angle = 0;
    private int number = 0;
    private String str = "";
    
    
    public DialGaugeView(Context context) {
        super(context);
    }
    
    public DialGaugeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray a = context.getTheme().obtainStyledAttributes(
                attrs,
                R.styleable.DialGaugeView,
                0, 0);
    
        try {
            tickMajorCount = a.getInteger(R.styleable.DialGaugeView_tickCountMajor, 5);
            tickMinorCount = a.getInteger(R.styleable.DialGaugeView_tickCountMinor, 0);
            tickSweep = a.getFloat(R.styleable.DialGaugeView_tickSweep, 270) / 2;
            numberMin = a.getInteger(R.styleable.DialGaugeView_displayMin, 0);
            numberMax = a.getInteger(R.styleable.DialGaugeView_displayMax, 1);
            titleStr = a.getString(R.styleable.DialGaugeView_displayTitle);
        } finally {
            a.recycle();
        }
    }
    
    public DialGaugeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    
    public int getTickMajorCount() {
        return tickMajorCount;
    }
    
    public void setTickMajorCount(int i) {
        tickMajorCount = i;
        isInit = false;
        invalidate();
        requestLayout();
    }
    
    public int getTickMinorCount() {
        return tickMinorCount;
    }
    
    public void setTickMinorCount(int i) {
        tickMinorCount = i;
        isInit = false;
        invalidate();
        requestLayout();
    }
    
    public float getTickSweep() {
        return tickSweep;
    }
    
    public void setTickSweep(float i) {
        tickSweep = i / 2;
        isInit = false;
        invalidate();
        requestLayout();
    }
    
    public int getDisplayMin() {
        return numberMin;
    }
    
    public void setDisplayMin(int i) {
        numberMin = i;
        isInit = false;
        invalidate();
        requestLayout();
    }
    
    public int getDisplayMax() {
        return numberMax;
    }
    
    public void setDisplayMax(int i) {
        numberMax = i;
        isInit = false;
        invalidate();
        requestLayout();
    }
    
    public float getSensorInput() {
        return sensorInput;
    }
    
    public void setSensorInput(float i) {
        sensorInput = i;
        isInit = false;
        invalidate();
        requestLayout();
    }
    
    public String getTitle() {
        return titleStr;
    }
    
    public void setTitle(String i) {
        titleStr = i;
        invalidate();
        requestLayout();
    }
    
    private void initGauge() {
        //Common variables
        height = getHeight();
        width = getWidth();
        min = Math.min(height, width);
        center_x = width / 2;
        center_y = height / 2;
        paint = new Paint();
        isInit = true;
    
        //Dial face variables
        dialEdgeStroke = min / 20;
        dialEdgeRadius = min / 2 - dialEdgeStroke / 2;
        dialFaceRadius = dialEdgeRadius - dialEdgeStroke / 2;
    
        //Tick variables
        //tickMajorCount = 7; //TODO - Class input needed
        //tickMinorCount = 1; //TODO - Class input needed
        //tickSweep = 270 / 2; //TODO - Class input needed //Degrees +/- from 90 degrees
        tickEdgeRadius = dialFaceRadius - dialEdgeStroke / 2;
        tickInnerMajorRadius = (float) (tickEdgeRadius - dialEdgeStroke * 1.5);
        tickInnerMinorRadius = ((tickEdgeRadius + tickInnerMajorRadius) / 2);
        tickMajorStroke = min / 75;
        tickAngleStart = 90 + tickSweep;
        tickAngleEnd = 90 - tickSweep;
        tickMinorStroke = tickMajorStroke / 2;
        tickTotalCount = tickMajorCount + (tickMajorCount - 1) * tickMinorCount;
    
        //Numeral variables
        //numberMin = 0; //TODO - Class input needed
        //numberMax = 120; //TODO - Class input needed
        numberRadius = (tickInnerMajorRadius); // - (tickEdgeRadius - tickInnerMajorRadius) * 1.25);
        numberFontSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, min / 40,
                getResources().getDisplayMetrics());
    
        //Title variables
        titleRadius = tickInnerMajorRadius;
        titleFontSize = numberFontSize;
    
        //Arrow variables
        //sensorInput = (float) (numberMax * .4965); //TODO - Class input needed
        arrowTipRadius = tickEdgeRadius;
        arrowRearRadius = arrowTipRadius / 2;
        arrowCenterRadius = (arrowTipRadius * 1 / 8);
        arrowPinRadius = (arrowTipRadius * 1 / 24);
    
    
    
    
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        if (!isInit) {
            initGauge();
        }
    
        canvas.drawColor(Color.BLACK);
    
        drawDialFace(canvas);
        drawTicks(canvas);
        drawNumeral(canvas);
        drawTitle(canvas);
        drawArrow(canvas);
    
        postInvalidateDelayed(500);
        invalidate();
        requestLayout();
    }
    
    private void drawDialFace(Canvas canvas) {
        paint.reset();
        paint.setColor(getResources().getColor(android.R.color.black));
        paint.setStrokeWidth(dialEdgeStroke);
        paint.setStyle(Paint.Style.STROKE);
        paint.setAntiAlias(true);
        canvas.drawCircle(center_x, center_y, dialEdgeRadius, paint);
    
        paint.reset();
        paint.setColor(getResources().getColor(android.R.color.white));
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        canvas.drawCircle(center_x, center_y, dialFaceRadius, paint);
    }
    
    private void drawTicks(Canvas canvas) {
        paint.reset();
        paint.setColor(getResources().getColor(android.R.color.black));
        paint.setStyle(Paint.Style.STROKE);
        paint.setAntiAlias(true);
    
        for(int i = 0; i < tickTotalCount; i++) {
            angle = (float) (((tickAngleEnd - tickAngleStart) * i / (tickTotalCount - 1) + tickAngleStart) / 180 * Math.PI);
            x1 = (float) (center_x + Math.cos(angle) * tickEdgeRadius);
            y1 = (float) (center_y - Math.sin(angle) * tickEdgeRadius);
            if((i % (tickMinorCount + 1)) == 0) {
                paint.setStrokeWidth(tickMajorStroke);
                x2 = (float) (center_x + Math.cos(angle) * tickInnerMajorRadius);
                y2 = (float) (center_y - Math.sin(angle) * tickInnerMajorRadius);
            } else {
                paint.setStrokeWidth(tickMinorStroke);
                x2 = (float) (center_x + Math.cos(angle) * tickInnerMinorRadius);
                y2 = (float) (center_y - Math.sin(angle) * tickInnerMinorRadius);
            }
            canvas.drawLine(x1, y1, x2, y2, paint);
        }
    }
    
    private void drawNumeral(Canvas canvas) {
        paint.reset();
        paint.setTextSize(numberFontSize);
        paint.setColor(getResources().getColor(android.R.color.black));
    
        for(int i = 0; i < tickMajorCount; i++) {
            angle = (float) (((tickAngleEnd - tickAngleStart) * i / (tickMajorCount - 1) + tickAngleStart) / 180 * Math.PI);
            number = (numberMax - numberMin) * i / (tickMajorCount - 1) + numberMin;
            str = String.valueOf(number);
            paint.getTextBounds(str, 0, str.length(), rect);
    
    
            double c = Math.cos(angle);
            double s = Math.sin(angle);
            if(rect.width() * Math.abs(s) < rect.height() * Math.abs(c)) {
                x2 = (float) (Math.signum(c) * rect.width() / 2);
                y2 = (float) (Math.tan(angle) * x2);
            } else {
                y2 = (float) (Math.signum(s) * rect.height() / 2);
                x2 = (float) (1 / Math.tan(angle) * y2);//Math.cotg(angle) * y;
            }
    
    
    
    
    
            x1 = (float) (center_x + Math.cos(angle) * numberRadius - rect.width() / 2 - x2 * 1.25);
            y1 = (float) (center_y - Math.sin(angle) * numberRadius + rect.height() / 2 + y2 * 1.25);
            canvas.drawText(str, x1, y1, paint);
        }
    }
    
    private void drawTitle(Canvas canvas) {
        paint.reset();
        paint.setTextSize(titleFontSize);
        paint.setColor(getResources().getColor(android.R.color.black));
        angle = (float) ((270.0 / 180) * Math.PI);
        paint.getTextBounds(titleStr, 0, str.length(), rect);
        x1 = (float) (center_x + Math.cos(angle) * titleRadius - rect.width() / 2);
        y1 = (float) (center_y - Math.sin(angle) * titleRadius + rect.height() / 3);
        canvas.drawText(titleStr, x1, y1, paint);
    }
    
    private void drawArrow(Canvas canvas) {
        paint.reset();
        paint.setColor(getResources().getColor(android.R.color.holo_red_dark));
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
    
        //Calculate (x,y) coordinates for the arrow path.
        temp = (tickAngleEnd - tickAngleStart) * sensorInput / numberMax + tickAngleStart;
        angle = (float) (temp / 180 * Math.PI);
        x1 = (float) (center_x + Math.cos(angle) * arrowTipRadius);
        y1 = (float) (center_y - Math.sin(angle) * arrowTipRadius);
        angle = (float) ((temp + 170) / 180 * Math.PI);
        x2 = (float) (center_x + Math.cos(angle) * arrowRearRadius);
        y2 = (float) (center_y - Math.sin(angle) * arrowRearRadius);
        angle = (float) ((temp - 170) / 180 * Math.PI);
        x3 = (float) (center_x + Math.cos(angle) * arrowRearRadius);
        y3 = (float) (center_y - Math.sin(angle) * arrowRearRadius);
    
        //Draw arrow path using calculated coordinates.
        path.moveTo(x1, y1);
        path.lineTo(x2, y2);
        path.lineTo(x3, y3);
        path.close();
        canvas.drawPath(path, paint);
    
        //Calculate (x,y) coordinates for dial center.
        x1 = center_x;
        y1 = center_y;
    
        canvas.drawCircle(x1, y1, arrowCenterRadius, paint);
    
        paint.setColor(getResources().getColor(android.R.color.darker_gray));
        canvas.drawCircle(x1, y1, arrowPinRadius, paint);
    
    }
    }
    

    我的主要活动:

    package net.dynu.kubie.redneksldhlr;
    
    import android.app.Activity;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.TextView;
    
    import org.w3c.dom.Text;
    
    public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        hideSystemUI();
    
        startGenerating();
    }
    
    @Override
    protected void onResume() {
        super.onResume();
    
        hideSystemUI();
        //demoData();
    }
    
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus) {
            hideSystemUI();
            //demoData();
        }
    }
    private void hideSystemUI() {
        // Enables regular immersive mode.
        // For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE.
        // Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
        View decorView = getWindow().getDecorView();
        decorView.setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_IMMERSIVE
                        // Set the content to appear under the system bars so that the
                        // content doesn't resize when the system bars hide and show.
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        // Hide the nav bar and status bar
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_FULLSCREEN);
    }
    
    
    private void startGenerating() {
        DoSomethingThread randomWork = new DoSomethingThread();
        randomWork.start();
    }
    
    
    public class DoSomethingThread extends Thread {
        private static final String TAG = "DoSomethingThread";
        private static final int DELAY = 15000; // 5 seconds
        private static final int RANDOM_MULTIPLIER = 120;
    
        @Override
        public void run() {
            //Log.v(TAG, "doing work in Random Number Thread");
            while (true) {
                float randNum = (float) (Math.random() * RANDOM_MULTIPLIER);
                // need to publish the random number back on the UI at this point in the code through the publishProgress(randNum) call
                publishProgress(randNum);
                try {
                    Thread.sleep(DELAY);
                } catch (InterruptedException e) {
                   // Log.v(TAG, "Interrupting and stopping the Random Number Thread");
                    return;
                }
            }
        }
    }
    
    
    
    private void publishProgress(float randNum) {
        //Log.v(TAG, "reporting back from the Random Number Thread");
        //final String text = String.format(getString(R.string.service_msg), randNum);
        //final String text = Integer.toString(randNum);
        final float text = randNum;
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                updateResults(text);
            }
        });
    }
    
    
    public void updateResults(float results) {
        //TextView myTextView = (TextView) findViewById(R.id.testTextView);
        DialGaugeView myDial = findViewById(R.id.my_gauge);
        myDial.setSensorInput(results);
    }
    
    
    }
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   kubiej21    7 年前

    我最终意识到我的问题根本不是人工制品。我的箭被画成一系列的路径。如果我在为新箭头添加坐标之前没有重置路径,那么前面的箭头就会被拖走。正在添加path.reset();在添加第一个箭头坐标之前解决了我的问题。

    //Draw arrow path using calculated coordinates.
    path.reset();
    path.moveTo(x1, y1);
    path.moveTo(x2, y2);
    path.moveTo(x3, y3);
    path.close();
    canvas.drawPath(path, paint);