Android的JNI【实战教程】6⃣️--温控计

6 篇文章 7 订阅

demo下载地址:http://download.csdn.net/detail/github_33304260/9860547
#相关链接:
Android的JNI【实战教程】1⃣️–java和c/c++的那些事

Android的JNI【实战教程】2⃣️–AS下NDK环境配置及第一个工程

Android的JNI【实战教程】3⃣️–Java调用C代码

Android的JNI【实战教程】4⃣️–C调用Java代码

Android的JNI【实战教程】5⃣️—Android Studio 2.2 以上 NDK开发

#原理

温控计主要是通过C语言获取设备的温度数据(我们这里写个随机数模拟获取设备温度),然后我们JAVA通过JNI获取到C中的数据,然后在Android界面以某种试展现出来。

这里写图片描述

在Java程序执行的时候,若在某个类中调用了native方法,则虚拟机会通过JNI来转调用库文件中的C语言代码。提示:C代码最终是在Linux进程中执行的,而不是在虚拟机中。

#效果

demo下载地址:http://download.csdn.net/detail/github_33304260/9860547

效果如图所示:
这里做的是C++代码产生一个0~90的随机数。
这里写图片描述

#代码
##C代码

下面的代码非常简单,定时2秒产生一个随机数 。

#include <jni.h>
#include <string>
#include <unistd.h>
#include <stdlib.h>

extern "C"
JNIEXPORT jint JNICALL
Java_pressure_libin_com_pressure_MainActivity_getPressureFromJNI(JNIEnv *env, jobject instance) {

    while (1) {
        sleep(2);   //休眠两秒 
        int pressure  = rand()%90;

        printf("当前锅炉压力值是:%d \n", pressure);

        return pressure;

    }

}

##JAVA代码
代码也非常的简单,就是通过Timer产生一个定时任务,去定时读取温度(C++模拟的)数值,通过handler接收读取到的数值进行展示。

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    private Timer timer;
    private TimerTask task;

	//接收消息并展示到界面
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            if(msg.what == 1){
                cv.setCurrentDegree((int)msg.obj);
            }

        }
    };

    private CircleView cv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        cv = (CircleView) findViewById(R.id.cv);

		//定时器,每两秒读取一次C++产生的随机数
        timer = new Timer();
        task = new TimerTask() {
            @Override
            public void run() {
                Message msg = new Message();
                msg.obj = getPressureFromJNI();
                msg.what = 1;
                handler.sendMessage(msg);
            }
        };

        timer.schedule(task,100,2000);


    }
    
    /**
     * 从JNI获取检测数据
     * @return 返回检测到的数据
     */
    public native int getPressureFromJNI();
}

##自定义View
本节的重点不是自定义View,所以这里就不详细讲解啦,感兴趣的可以自己google。附上代码:

public class CircleView extends View {

    private Paint linePaint;
    private Paint circlePaint;
    private Paint textPaint;//刻度值
    private Paint centerTextPaint;//中间的温度值
    private Paint indicatorPaint;//指示器
    private RectF mRectF;

    private int circleWidth = 60; //空心圆的宽度

    private int viewWidth, viewHeight;  //画布的宽高

    private int minCount = 0; // 最小度数
    private int maxCount = 90; // 最大度数

    private float centerX, centerY;

    private int defaultValue;
    int mCenter = 0;// 圆的半径
    int mRadius = 0;
    private SweepGradient mSweepGradient;
    private int scanDegree = 0;//最高温度和最低温度扫描角度
    private int currentScanDegree = 0;//当前温度扫过的角度

    int currentDegree = 0;//当前温度

    int screenWidth, screenHeight;

    public CircleView(Context context) {
        super(context);
        Log.e("My----->", "1");
        initPaint();
    }

    public CircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        screenWidth = MeasureUtil.getScreenWidth(context);
        screenHeight = MeasureUtil.getScreenHeight(context);
        Log.e("My----->", "2  " + screenWidth + "  " + screenHeight);
        initPaint();
    }

    private void initPaint() {
        linePaint = new Paint();
        linePaint.setColor(Color.CYAN);
        linePaint.setStyle(Paint.Style.FILL);
        linePaint.setAntiAlias(true);
        linePaint.setStrokeWidth(1.0f);

        textPaint = new Paint();
        textPaint.setColor(Color.BLACK);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(30);

        centerTextPaint = new Paint();
        centerTextPaint.setColor(Color.BLUE);
        centerTextPaint.setTextAlign(Paint.Align.CENTER);
        centerTextPaint.setAntiAlias(true);
        centerTextPaint.setTextSize(80);

        circlePaint = new Paint();
        circlePaint.setColor(Color.WHITE);
        circlePaint.setAntiAlias(true);
        circlePaint.setStyle(Paint.Style.STROKE);
        circlePaint.setStrokeCap(Paint.Cap.ROUND);//实现末端圆弧
        circlePaint.setStrokeWidth(circleWidth);

        indicatorPaint = new Paint();
        indicatorPaint.setColor(0xFFF7F709);
        indicatorPaint.setAntiAlias(true);
        indicatorPaint.setStyle(Paint.Style.FILL);

        // 着色的共有270度,这里设置了12个颜色均分360度s
        int[] colors = {0xFFD52B2B, 0xFFf70101, 0xFFFFFFFF, 0xFFFFFFFF,
                0xFF6AE2FD, 0xFF8CD0E5, 0xFFA3CBCB, 0xFFD1C299, 0xFFE5BD7D,
                0xFFAA5555, 0xFFBB4444, 0xFFC43C3C};

        mCenter = screenWidth / 2;
        mRadius = screenWidth / 2 - 100;
        // 渐变色
        mSweepGradient = new SweepGradient(mCenter, mCenter, colors, null);
        // 构建圆的外切矩形
        mRectF = new RectF(mCenter - mRadius, mCenter - mRadius, mCenter
                + mRadius, mCenter + mRadius);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO 自动生成的方法存根
        super.onDraw(canvas);
        circlePaint.setShader(null);

        canvas.drawArc(mRectF, 135, 270, false, circlePaint);

        Log.e("scan--1-->", scanDegree + "");
        // TODO: 2017/5/10  扫过度数
        //重新赋值了,所以手指滑动没有显示
        scanDegree = getCurrentDegree() * 3;

        // 设置画笔渐变色
        circlePaint.setShader(mSweepGradient);
        canvas.drawArc(mRectF, 135, (float) scanDegree, false, circlePaint);

        int insideIndicator = mRadius - circleWidth;//离圆环的距离
        currentScanDegree = getCurrentDegree() * 3;
        canvas.drawText((getCurrentDegree()) + "℃", mCenter, mCenter, centerTextPaint);//全温度盘时,把currentScanDegree改为scanDegree,中间的值就会跟着改变了
        Log.e("指示角度----》", currentScanDegree + "");
        if (currentScanDegree <= 45) {//第三象限
            canvas.drawCircle((float) (mCenter - insideIndicator * Math.sin(Math.PI * (currentScanDegree + 45) / 180)), (float) (mCenter + insideIndicator * Math.cos(Math.PI * (currentScanDegree + 45) / 180)), 10, indicatorPaint);
        } else if (45 < currentScanDegree && currentScanDegree <= 135) {//第二象限
            canvas.drawCircle((float) (mCenter - insideIndicator * Math.cos(Math.PI * (currentScanDegree - 45) / 180)), (float) (mCenter - insideIndicator * Math.sin(Math.PI * (currentScanDegree - 45) / 180)), 10, indicatorPaint);
        } else if (135 < currentScanDegree && currentScanDegree <= 225) {//第一象限
            canvas.drawCircle((float) (mCenter + insideIndicator * Math.sin(Math.PI * (currentScanDegree - 135) / 180)), (float) (mCenter - insideIndicator * Math.cos(Math.PI * (currentScanDegree - 135) / 180)), 10, indicatorPaint);
        } else if (225 < currentScanDegree && currentScanDegree <= 270) {//第四象限
            canvas.drawCircle((float) (mCenter + insideIndicator * Math.cos(Math.PI * (currentScanDegree - 225) / 180)), (float) (mCenter + insideIndicator * Math.sin(Math.PI * (currentScanDegree - 225) / 180)), 10, indicatorPaint);
        }

        for (int i = 0; i < 120; i++) {
            if (i <= 45 || i >= 75) {
                canvas.drawLine(mCenter, mCenter - mRadius - 30, mCenter,
                        mCenter - mRadius + 30, linePaint);
            }

            canvas.rotate(3, mCenter, mCenter);
        }

        // x代表文字的x轴距离圆心x轴的距离 因为刚好是45度,所以文字x轴值和y值相等
        int x = 0;
        // 三角形的斜边
        int c = mRadius + 60 / 2 + 40;// 40代表这个字距离圆外边的距离
        // 因为是每45度写一次文字,故根据到圆心的位置,利用三角形的公式,可以算出文字的坐标值
        x = (int) Math.sqrt((c * c / 2));

        canvas.drawText("0", mCenter - x, mCenter + x, textPaint);

        canvas.drawText("15", mCenter - c, mCenter, textPaint);

        canvas.drawText("30", mCenter - x, mCenter - x, textPaint);

        canvas.drawText("45", mCenter, mCenter - c, textPaint);

        canvas.drawText("60", mCenter + x, mCenter - x, textPaint);

        canvas.drawText("75", mCenter + c, mCenter, textPaint);

        canvas.drawText("90", mCenter + x, mCenter + x, textPaint);

        Log.e("TAG", "calculateX(mRadius,50) ====== " + calculateX(mRadius, 50));

    }

    /**
     * 设置当前温度
     *
     * @param currentDegree
     */
    public void setCurrentDegree(int currentDegree) {
        this.currentDegree = currentDegree;

        invalidate();
    }

    public int getCurrentDegree() {
        return currentDegree;
    }

    // 因为自定义的空间的高度设置的是wrap_content,所以我们必须要重写onMeasure方法去测量高度,否则布局界面看不到
    // 其他控件(被覆盖)
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec),
                measureHeight(heightMeasureSpec));
    }

    /**
     * 测量宽度
     *
     * @param widthMeasureSpec
     * @return
     */
    private int measureWidth(int widthMeasureSpec) {
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        int size = MeasureSpec.getSize(widthMeasureSpec);
        // 默认宽高;
        defaultValue = screenWidth;

        switch (mode) {
            case MeasureSpec.AT_MOST:
                // 最大值模式 当控件的layout_Width或layout_height属性指定为wrap_content时
                Log.e("cmos---->", "size " + size + " screenWidth " + screenWidth);
                size = Math.min(defaultValue, size);
                break;
            case MeasureSpec.EXACTLY:
                // 精确值模式
                // 当控件的android:layout_width=”100dp”或android:layout_height=”match_parent”时

                break;
            default:
                size = defaultValue;
                break;
        }
        defaultValue = size;
        return size;
    }

    /**
     * 测量高度
     *
     * @param heightMeasureSpec
     * @return
     */
    private int measureHeight(int heightMeasureSpec) {
        int mode = MeasureSpec.getMode(heightMeasureSpec);
        int size = MeasureSpec.getSize(heightMeasureSpec);

        switch (mode) {
            case MeasureSpec.AT_MOST:
                // 最大值模式 当控件的layout_Width或layout_height属性指定为wrap_content时
                Log.e("cmos---->", "size " + size + " screenHeight " + screenHeight);
                size = Math.min(screenHeight / 2, size);
                break;
            case MeasureSpec.EXACTLY:
                // 精确值模式
                // 当控件的android:layout_width=”100dp”或android:layout_height=”match_parent”时

                break;
            default:
                size = defaultValue;
                break;
        }
        return size;
    }

    /**
     * 根据半径和角度计算x坐标
     */
    private float calculateX(float r, double angle) {
        angle = angle * ((2 * Math.PI) / 360);
        Log.e(TAG, "angle = " + angle + ",Math.sin(angle) = " + Math.sin(angle));
        double x = r * Math.sin(angle);

        double xFinal = centerX + x;
        return (float) xFinal;
    }

    /**
     * 根据半径和角度计算y坐标
     */
    private float calculateY(float r, double angle) {
        angle = angle * ((2 * Math.PI) / 360);
        Log.e(TAG, "angle = " + angle + ",Math.cos(angle) = " + Math.cos(angle));
        double y = r * Math.cos(angle);

        double yFinal = centerY - y;
        return (float) yFinal;
    }

    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);

        viewWidth = getWidth();
        viewHeight = getHeight();

        centerX = viewWidth / 2f;
        centerY = viewHeight / 2f;
    }
}

扫码关注公众号“伟大程序猿的诞生“,更多干货新鲜文章等着你~

公众号回复“资料获取”,获取更多干货哦~

有问题添加本人微信号“fenghuokeji996” 或扫描博客导航栏本人二维码

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值