基于DragonBoard 410c的遥控炮台五之远程交互(下)

    xiaoxiao2021-03-25  125

    一.背景

    在实现了android手机对Dragonboard 410c系统的远程舵机控制后,我们再回头来优化android客户端的界面控制效果。

    二.虚拟摇杆

    1.android虚拟摇杆

    上一节我们在android的客户端是通过手动输入数值的方式来控制舵机,虽然这也能实现远程控制,但是相对于人的操作方式相当不方便,这里我们考虑使用虚拟的摇杆来优化这个控制方式。

    图1 虚拟摇杆效果

    2.摇杆实现代码

    2.1.RockerView.java

    package com.boss.xiao.streerocker; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.PointF; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnPreDrawListener; public class RockerView extends View { //固定摇杆背景圆形的X,Y坐标以及半径 private float mRockerBg_X; private float mRockerBg_Y; private float mRockerBg_R; //摇杆的X,Y坐标以及摇杆的半径 private float mRockerBtn_X; private float mRockerBtn_Y; private float mRockerBtn_R; private Bitmap mBmpRockerBg; private Bitmap mBmpRockerBtn; private PointF mCenterPoint; public RockerView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub // 获取bitmap mBmpRockerBg = BitmapFactory.decodeResource(context.getResources(), R.drawable.rocker_bg); mBmpRockerBtn = BitmapFactory.decodeResource(context.getResources(), R.drawable.rocker_btn); getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { // 调用该方法时可以获取view实际的宽getWidth()和高getHeight() @Override public boolean onPreDraw() { // TODO Auto-generated method stub getViewTreeObserver().removeOnPreDrawListener(this); Log.e("RockerView", getWidth() + "/" + getHeight()); mCenterPoint = new PointF(getWidth() / 2, getHeight() / 2); mRockerBg_X = mCenterPoint.x; mRockerBg_Y = mCenterPoint.y; mRockerBtn_X = mCenterPoint.x; mRockerBtn_Y = mCenterPoint.y; float tmp_f = mBmpRockerBg.getWidth() / (float)(mBmpRockerBg.getWidth() + mBmpRockerBtn.getWidth()); mRockerBg_R = tmp_f * getWidth() / 2; mRockerBtn_R = (1.0f - tmp_f)* getWidth() / 2; return true; } }); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub while(true){ //系统调用onDraw方法刷新画面 RockerView.this.postInvalidate(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); canvas.drawBitmap(mBmpRockerBg, null, new Rect((int)(mRockerBg_X - mRockerBg_R), (int)(mRockerBg_Y - mRockerBg_R), (int)(mRockerBg_X + mRockerBg_R), (int)(mRockerBg_Y + mRockerBg_R)), null); canvas.drawBitmap(mBmpRockerBtn, null, new Rect((int)(mRockerBtn_X - mRockerBtn_R), (int)(mRockerBtn_Y - mRockerBtn_R), (int)(mRockerBtn_X + mRockerBtn_R), (int)(mRockerBtn_Y + mRockerBtn_R)), null); } @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) { // 当触屏区域不在活动范围内 if (Math.sqrt(Math.pow((mRockerBg_X - (int) event.getX()), 2) + Math.pow((mRockerBg_Y - (int) event.getY()), 2)) >= mRockerBg_R) { //得到摇杆与触屏点所形成的角度 double tempRad = getRad(mRockerBg_X, mRockerBg_Y, event.getX(), event.getY()); //保证内部小圆运动的长度限制 getXY(mRockerBg_X, mRockerBg_Y, mRockerBg_R, tempRad); } else {//如果小球中心点小于活动区域则随着用户触屏点移动即可 mRockerBtn_X = (int) event.getX(); mRockerBtn_Y = (int) event.getY(); } if(mRockerChangeListener != null) { mRockerChangeListener.report(mRockerBtn_X - mCenterPoint.x, mRockerBtn_Y - mCenterPoint.y); } } else if (event.getAction() == MotionEvent.ACTION_UP) { //当释放按键时摇杆要恢复摇杆的位置为初始位置 mRockerBtn_X = mCenterPoint.x; mRockerBtn_Y = mCenterPoint.y; if(mRockerChangeListener != null) { mRockerChangeListener.report(0, 0); } } return true; } /*** * 得到两点之间的弧度 */ public double getRad(float px1, float py1, float px2, float py2) { //得到两点X的距离 float x = px2 - px1; //得到两点Y的距离 float y = py1 - py2; //算出斜边长 float xie = (float) Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); //得到这个角度的余弦值(通过三角函数中的定理 :邻边/斜边=角度余弦值) float cosAngle = x / xie; //通过反余弦定理获取到其角度的弧度 float rad = (float) Math.acos(cosAngle); //注意:当触屏的位置Y坐标 <摇杆的y坐标我们要取反值-0~-180 if (py2 < py1) { rad } return rad; ** * @param r 圆周运动的旋转点 centerx 旋转点x centery 旋转点y 旋转的弧度 public void getxy(float centerx, float centery, r, double rad) 获取圆周运动的x坐标 mrockerbtn_x="(float)" (r math.cos(rad)) + centerx; 获取圆周运动的y坐标 mrockerbtn_y="(float)" math.sin(rad)) centery; rockerchangelistener mrockerchangelistener="rockerChangeListener;" setrockerchangelistener(rockerchangelistener rockerchangelistener) interface report(float x, y); code>

    2.2. activity_main.xml 

    三.远程控制

    1.核心控制代码:

    MainActivity.java

    package com.boss.xiao.streerocker; import android.app.Activity; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.Button; import android.content.Intent; import android.os.Bundle; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.ToggleButton; import java.io.IOException; import java.io.Serializable; public class MainActivity extends Activity { private EditText edtTxt_Addr; private EditText edtTxt_Port; private ToggleButton tglBtn; private ToggleButton btn_fire; private Button btn_rocker; private String model="connector"; Intent intent; private RockerView rockerView1; private RockerView rockerView2; private static String left_flag="LEFT"; private static String right_flag="RIGHT"; private static String rec_flag="Succee"; private static String open_flag="OPEN"; private static String close_flag="CLOSE"; private static boolean flag_report=true; // private TextView tv_Msg; // private EditText edtTxt_Data; // private Button btn_Send; private static final String TAG = "MainActivity"; private TcpClientConnector connector; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); InitWidgets(); connector = TcpClientConnector.getInstance(); //获取connector实例 tglBtn.setOnCheckedChangeListener(new TglBtnCheckedChangeEvents()); btn_fire.setOnCheckedChangeListener(new fireBtnCheckedChangeEvents()); InitRocker(); // btn_rocker.setOnClickListener(new ButtonClickEvent()); } /*初始化 摇杆**/ private void InitRocker() { rockerView1 = (RockerView) findViewById(R.id.rockerView1); rockerView2 = (RockerView) findViewById(R.id.rockerView2); rockerView1.setRockerChangeListener(new RockerView.RockerChangeListener() { @Override public void report(float x, float y) { // TODO Auto-generated method stub Log.e(x + "/" + y,TAG); // setLayout(rockerView2, (int)x, (int)y); //setLayout(rockerView2, (int)x, (int)y); //String data="1000000"; try{ // connector.send(left_flag); // if(flag_report) if(flag_report==true) { if(x<0) { connector.send(left_flag); } else if(x>=0) { connector.send(right_flag); } /*else { Log.d("everything is ok",TAG); }*/ flag_report=false; } else { Log.d("this cmd is not acesss",TAG); } }catch (IOException e){ e.printStackTrace(); } } }); rockerView2.setRockerChangeListener(new RockerView.RockerChangeListener() { @Override public void report(float x, float y) { // TODO Auto-generated method stub Log.e(x + "/" + y,TAG); } }); } /*** * 控件初始化 */ private void InitWidgets(){ edtTxt_Addr = (EditText) findViewById(R.id.edtTxt_Addr); edtTxt_Port = (EditText) findViewById(R.id.edtTxt_Port); tglBtn = (ToggleButton) findViewById(R.id.tglBtn); btn_fire= (ToggleButton) findViewById(R.id.btn_fire); // btn_rocker = (Button) findViewById(R.id.btn_rocker); // tv_Msg = (TextView) findViewById(R.id.tv_Msg); // edtTxt_Data = (EditText) findViewById(R.id.edtTxt_Data); // btn_Send = (Button) findViewById(R.id.btn_Send); } class TglBtnCheckedChangeEvents implements ToggleButton.OnCheckedChangeListener{ @Override public void onCheckedChanged(CompoundButton btnView, boolean isChecked){ if(btnView == tglBtn){ if(isChecked == true){ //连接Tcp服务器端 connector.createConnect("172.27.35.16",8888); //调试使用 // connector.createConnect(edtTxt_Addr.getText().toString(),Integer.parseInt(edtTxt_Port.getText().toString())); connector.setOnConnectListener(new TcpClientConnector.ConnectListener() { @Override public void onReceiveData(String data) { //Received Data,do somethings. // tv_Msg.append("Server:"+ data + "\n"); Log.d("data = "+data,TAG); if(data.equals(rec_flag)) { flag_report=true; Log.d("I get report "+data,TAG); } } }); }else{ try{ //断开与服务器的连接 connector.disconnect(); }catch(IOException e){ e.printStackTrace(); } } } } } class fireBtnCheckedChangeEvents implements ToggleButton.OnCheckedChangeListener{ @Override public void onCheckedChanged(CompoundButton btnView, boolean isChecked){ if(btnView == btn_fire){ if(isChecked == true){ try{ connector.send(open_flag); }catch (IOException e){ e.printStackTrace(); } }else{ try{ connector.send(close_flag); }catch (IOException e){ e.printStackTrace(); } } } } } /*class ButtonClickEvent implements View.OnClickListener{ public void onClick(View v) { if (v == btn_rocker){ // connector.send(edtTxt_Data.getText().toString()); // tv_Msg.append("Client:"+ edtTxt_Data.getText().toString() + "\n"); } } }*/ }

    四.实测效果

    图2 android虚拟摇杆图

    转载请注明原文地址: https://ju.6miu.com/read-9657.html

    最新回复(0)